Skip to content

Commit fd4d8a3

Browse files
Robert Holtrjmholt
authored andcommitted
Fix compatibility alias resolution
1 parent 9a557de commit fd4d8a3

File tree

2 files changed

+126
-45
lines changed

2 files changed

+126
-45
lines changed

PSCompatibilityCollector/Microsoft.PowerShell.CrossCompatibility/Query/Modules/ModuleData.cs

Lines changed: 35 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -13,24 +13,30 @@ namespace Microsoft.PowerShell.CrossCompatibility.Query
1313
/// </summary>
1414
public class ModuleData
1515
{
16+
private readonly RuntimeData _parent;
17+
1618
private readonly Data.ModuleData _moduleData;
1719

18-
private readonly Lazy<Tuple<IReadOnlyDictionary<string, FunctionData>, IReadOnlyDictionary<string, CmdletData>, IReadOnlyDictionary<string, CommandData>>> _commands;
20+
private readonly Lazy<Tuple<IReadOnlyDictionary<string, FunctionData>, IReadOnlyDictionary<string, CmdletData>>> _lazyCommands;
21+
22+
private readonly Lazy<IReadOnlyDictionary<string, IReadOnlyList<CommandData>>> _lazyAliases;
1923

2024
/// <summary>
2125
/// Create a query object around a module data object.
2226
/// </summary>
2327
/// <param name="name">The name of the module.</param>
2428
/// <param name="version">The version of the module.</param>
2529
/// <param name="moduleData">The module data object.</param>
26-
public ModuleData(string name, Version version, Data.ModuleData moduleData)
30+
public ModuleData(string name, Version version, RuntimeData parent, Data.ModuleData moduleData)
2731
{
2832
_moduleData = moduleData;
33+
_parent = parent;
2934

3035
Name = name;
3136
Version = version;
3237

33-
_commands = new Lazy<Tuple<IReadOnlyDictionary<string, FunctionData>, IReadOnlyDictionary<string, CmdletData>, IReadOnlyDictionary<string, CommandData>>>(() => CreateCommandTables(moduleData.Functions, moduleData.Cmdlets, moduleData.Aliases));
38+
_lazyCommands = new Lazy<Tuple<IReadOnlyDictionary<string, FunctionData>, IReadOnlyDictionary<string, CmdletData>>>(() => CreateCommandTables(moduleData.Functions, moduleData.Cmdlets));
39+
_lazyAliases = new Lazy<IReadOnlyDictionary<string, IReadOnlyList<CommandData>>>(() => CreateAliasTable(_moduleData.Aliases));
3440
}
3541

3642
/// <summary>
@@ -51,12 +57,12 @@ public ModuleData(string name, Version version, Data.ModuleData moduleData)
5157
/// <summary>
5258
/// Functions exported by the module.
5359
/// </summary>
54-
public IReadOnlyDictionary<string, FunctionData> Functions => _commands.Value.Item1;
60+
public IReadOnlyDictionary<string, FunctionData> Functions => _lazyCommands.Value.Item1;
5561

5662
/// <summary>
5763
/// Cmdlets exported by the module.
5864
/// </summary>
59-
public IReadOnlyDictionary<string, CmdletData> Cmdlets => _commands.Value.Item2;
65+
public IReadOnlyDictionary<string, CmdletData> Cmdlets => _lazyCommands.Value.Item2;
6066

6167
/// <summary>
6268
/// Variables exported by the module.
@@ -66,16 +72,33 @@ public ModuleData(string name, Version version, Data.ModuleData moduleData)
6672
/// <summary>
6773
/// Aliases exported by the module.
6874
/// </summary>
69-
public IReadOnlyDictionary<string, CommandData> Aliases => _commands.Value.Item3;
75+
public IReadOnlyDictionary<string, IReadOnlyList<CommandData>> Aliases => _lazyAliases.Value;
76+
77+
private IReadOnlyDictionary<string, IReadOnlyList<CommandData>> CreateAliasTable(IReadOnlyDictionary<string, string> aliases)
78+
{
79+
if (aliases == null || aliases.Count == 0)
80+
{
81+
return null;
82+
}
83+
84+
var aliasTable = new Dictionary<string, IReadOnlyList<CommandData>>();
85+
foreach (KeyValuePair<string, string> alias in aliases)
86+
{
87+
if (_parent.Aliases.TryGetValue(alias.Key, out IReadOnlyList<CommandData> aliasedCommands))
88+
{
89+
aliasTable[alias.Key] = aliasedCommands;
90+
}
91+
}
7092

71-
private static Tuple<IReadOnlyDictionary<string, FunctionData>, IReadOnlyDictionary<string, CmdletData>, IReadOnlyDictionary<string, CommandData>> CreateCommandTables(
93+
return aliasTable;
94+
}
95+
96+
private static Tuple<IReadOnlyDictionary<string, FunctionData>, IReadOnlyDictionary<string, CmdletData>> CreateCommandTables(
7297
IReadOnlyDictionary<string, Data.FunctionData> functions,
73-
IReadOnlyDictionary<string, Data.CmdletData> cmdlets,
74-
IReadOnlyDictionary<string, string> aliases)
98+
IReadOnlyDictionary<string, Data.CmdletData> cmdlets)
7599
{
76100
Dictionary<string, FunctionData> funcDict = null;
77101
Dictionary<string, CmdletData> cmdletDict = null;
78-
Dictionary<string, CommandData> aliasDict = null;
79102

80103
if (functions != null)
81104
{
@@ -95,29 +118,9 @@ private static Tuple<IReadOnlyDictionary<string, FunctionData>, IReadOnlyDiction
95118
}
96119
}
97120

98-
if (aliases != null)
99-
{
100-
aliasDict = new Dictionary<string, CommandData>(aliases.Count, StringComparer.OrdinalIgnoreCase);
101-
foreach (KeyValuePair<string, string> alias in aliases)
102-
{
103-
if (funcDict != null && funcDict.TryGetValue(alias.Value, out FunctionData function))
104-
{
105-
aliasDict[alias.Key] = function;
106-
continue;
107-
}
108-
109-
if (cmdletDict != null && cmdletDict.TryGetValue(alias.Value, out CmdletData cmdlet))
110-
{
111-
aliasDict[alias.Key] = cmdlet;
112-
continue;
113-
}
114-
}
115-
}
116-
117-
return new Tuple<IReadOnlyDictionary<string, FunctionData>, IReadOnlyDictionary<string, CmdletData>, IReadOnlyDictionary<string, CommandData>>(
121+
return new Tuple<IReadOnlyDictionary<string, FunctionData>, IReadOnlyDictionary<string, CmdletData>>(
118122
funcDict,
119-
cmdletDict,
120-
aliasDict);
123+
cmdletDict);
121124
}
122125
}
123126
}

PSCompatibilityCollector/Microsoft.PowerShell.CrossCompatibility/Query/RuntimeData.cs

Lines changed: 91 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,9 @@
22
// Licensed under the MIT License.
33

44
using System;
5+
using System.Collections;
56
using System.Collections.Generic;
7+
using System.Linq;
68
using Data = Microsoft.PowerShell.CrossCompatibility.Data;
79

810
namespace Microsoft.PowerShell.CrossCompatibility.Query
@@ -14,6 +16,10 @@ public class RuntimeData
1416
{
1517
private readonly Lazy<IReadOnlyDictionary<string, IReadOnlyDictionary<Version, ModuleData>>> _modules;
1618

19+
private readonly Lazy<IReadOnlyDictionary<string, IReadOnlyList<CommandData>>> _nonAliasCommands;
20+
21+
private readonly Lazy<IReadOnlyDictionary<string, IReadOnlyList<CommandData>>> _aliases;
22+
1723
private readonly Lazy<IReadOnlyDictionary<string, IReadOnlyList<CommandData>>> _commands;
1824

1925
private readonly Lazy<NativeCommandLookupTable> _nativeCommands;
@@ -28,7 +34,9 @@ public RuntimeData(Data.RuntimeData runtimeData)
2834
Common = new CommonPowerShellData(runtimeData.Common);
2935

3036
_modules = new Lazy<IReadOnlyDictionary<string, IReadOnlyDictionary<Version, ModuleData>>>(() => CreateModuleTable(runtimeData.Modules));
31-
_commands = new Lazy<IReadOnlyDictionary<string, IReadOnlyList<CommandData>>>(() => CreateCommandLookupTable(Modules));
37+
_nonAliasCommands = new Lazy<IReadOnlyDictionary<string, IReadOnlyList<CommandData>>>(() => CreateNonAliasCommandLookupTable(Modules));
38+
_aliases = new Lazy<IReadOnlyDictionary<string, IReadOnlyList<CommandData>>>(() => CreateAliasLookupTable(runtimeData.Modules, NonAliasCommands));
39+
_commands = new Lazy<IReadOnlyDictionary<string, IReadOnlyList<CommandData>>>(() => new DualLookupTable<string, IReadOnlyList<CommandData>>(Aliases, NonAliasCommands));
3240
_nativeCommands = new Lazy<NativeCommandLookupTable>(() => NativeCommandLookupTable.Create(runtimeData.NativeCommands));
3341
}
3442

@@ -58,22 +66,26 @@ public RuntimeData(Data.RuntimeData runtimeData)
5866
/// </summary>
5967
public CommonPowerShellData Common { get; }
6068

61-
private static IReadOnlyDictionary<string, IReadOnlyDictionary<Version, ModuleData>> CreateModuleTable(IDictionary<string, JsonDictionary<Version, Data.ModuleData>> modules)
69+
internal IReadOnlyDictionary<string, IReadOnlyList<CommandData>> NonAliasCommands => _nonAliasCommands.Value;
70+
71+
internal IReadOnlyDictionary<string, IReadOnlyList<CommandData>> Aliases => _aliases.Value;
72+
73+
private IReadOnlyDictionary<string, IReadOnlyDictionary<Version, ModuleData>> CreateModuleTable(IDictionary<string, JsonDictionary<Version, Data.ModuleData>> modules)
6274
{
6375
var moduleDict = new Dictionary<string, IReadOnlyDictionary<Version, ModuleData>>(modules.Count, StringComparer.OrdinalIgnoreCase);
6476
foreach (KeyValuePair<string, JsonDictionary<Version, Data.ModuleData>> moduleVersions in modules)
6577
{
6678
var moduleVersionDict = new Dictionary<Version, ModuleData>(moduleVersions.Value.Count);
6779
foreach (KeyValuePair<Version, Data.ModuleData> module in moduleVersions.Value)
6880
{
69-
moduleVersionDict[module.Key] = new ModuleData(name: moduleVersions.Key, version: module.Key, moduleData: module.Value);
81+
moduleVersionDict[module.Key] = new ModuleData(name: moduleVersions.Key, version: module.Key, parent: this, moduleData: module.Value);
7082
}
7183
moduleDict[moduleVersions.Key] = moduleVersionDict;
7284
}
7385
return moduleDict;
7486
}
7587

76-
private static IReadOnlyDictionary<string, IReadOnlyList<CommandData>> CreateCommandLookupTable(
88+
private static IReadOnlyDictionary<string, IReadOnlyList<CommandData>> CreateNonAliasCommandLookupTable(
7789
IReadOnlyDictionary<string, IReadOnlyDictionary<Version, ModuleData>> modules)
7890
{
7991
var commandTable = new Dictionary<string, IReadOnlyList<CommandData>>(StringComparer.OrdinalIgnoreCase);
@@ -106,23 +118,89 @@ private static IReadOnlyDictionary<string, IReadOnlyList<CommandData>> CreateCom
106118
((List<CommandData>)commandTable[function.Key]).Add(function.Value);
107119
}
108120
}
121+
}
122+
}
109123

110-
if (module.Aliases != null)
124+
return commandTable;
125+
}
126+
127+
private static IReadOnlyDictionary<string, IReadOnlyList<CommandData>> CreateAliasLookupTable(
128+
IReadOnlyDictionary<string, JsonDictionary<Version, Data.ModuleData>> modules,
129+
IReadOnlyDictionary<string, IReadOnlyList<CommandData>> commands)
130+
{
131+
var aliasTable = new Dictionary<string, IReadOnlyList<CommandData>>();
132+
foreach (KeyValuePair<string, JsonDictionary<Version, Data.ModuleData>> module in modules)
133+
{
134+
foreach (KeyValuePair<Version, Data.ModuleData> moduleVersion in module.Value)
135+
{
136+
foreach (KeyValuePair<string, string> alias in moduleVersion.Value.Aliases)
111137
{
112-
foreach (KeyValuePair<string, CommandData> alias in module.Aliases)
138+
if (commands.TryGetValue(alias.Value, out IReadOnlyList<CommandData> aliasedCommands))
113139
{
114-
if (!commandTable.ContainsKey(alias.Key))
115-
{
116-
commandTable.Add(alias.Key, new List<CommandData>());
117-
}
118-
119-
((List<CommandData>)commandTable[alias.Key]).Add(alias.Value);
140+
aliasTable[alias.Key] = aliasedCommands;
120141
}
121142
}
122143
}
123144
}
145+
return aliasTable;
146+
}
124147

125-
return commandTable;
148+
private class DualLookupTable<K, V> : IReadOnlyDictionary<K, V>
149+
{
150+
private readonly IReadOnlyDictionary<K, V> _firstTable;
151+
152+
private readonly IReadOnlyDictionary<K, V> _secondTable;
153+
154+
public DualLookupTable(IReadOnlyDictionary<K, V> firstTable, IReadOnlyDictionary<K, V> secondTable)
155+
{
156+
_firstTable = firstTable;
157+
_secondTable = secondTable;
158+
}
159+
160+
public V this[K key]
161+
{
162+
get
163+
{
164+
if (_firstTable.TryGetValue(key, out V firstValue))
165+
{
166+
return firstValue;
167+
}
168+
169+
if (_secondTable.TryGetValue(key, out V secondValue))
170+
{
171+
return secondValue;
172+
}
173+
174+
throw new KeyNotFoundException();
175+
}
176+
}
177+
178+
public IEnumerable<K> Keys => _firstTable.Keys.Concat(_secondTable.Keys);
179+
180+
public IEnumerable<V> Values => _firstTable.Values.Concat(_secondTable.Values);
181+
182+
public int Count => _firstTable.Count + _secondTable.Count;
183+
184+
public bool ContainsKey(K key)
185+
{
186+
return _firstTable.ContainsKey(key) || _secondTable.ContainsKey(key);
187+
}
188+
189+
public IEnumerator<KeyValuePair<K, V>> GetEnumerator()
190+
{
191+
return _firstTable.Concat(_secondTable).GetEnumerator();
192+
}
193+
194+
public bool TryGetValue(K key, out V value)
195+
{
196+
return _firstTable.TryGetValue(key, out value)
197+
|| _secondTable.TryGetValue(key, out value);
198+
}
199+
200+
IEnumerator IEnumerable.GetEnumerator()
201+
{
202+
return GetEnumerator();
203+
}
126204
}
127205
}
128206
}

0 commit comments

Comments
 (0)