Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,10 @@ internal static IEnumerable<ExtendedTypeDefinition> GetFormatData()
"System.Management.Automation.Subsystem.SubsystemInfo",
ViewsOf_System_Management_Automation_Subsystem_SubsystemInfo());

yield return new ExtendedTypeDefinition(
"System.Management.Automation.Subsystem.SubsystemInfo+ImplementationInfo",
ViewsOf_System_Management_Automation_Subsystem_SubsystemInfo_ImplementationInfo());

yield return new ExtendedTypeDefinition(
"System.Management.Automation.ShellVariable",
ViewsOf_System_Management_Automation_ShellVariable());
Expand Down Expand Up @@ -774,6 +778,21 @@ private static IEnumerable<FormatViewDefinition> ViewsOf_System_Management_Autom
.EndTable());
}

private static IEnumerable<FormatViewDefinition> ViewsOf_System_Management_Automation_Subsystem_SubsystemInfo_ImplementationInfo()
{
yield return new FormatViewDefinition(
"System.Management.Automation.Subsystem.SubsystemInfo+ImplementationInfo",
ListControl.Create()
.StartEntry()
.AddItemProperty(@"Id")
.AddItemProperty(@"Kind")
.AddItemProperty(@"Name")
.AddItemProperty(@"Description")
.AddItemProperty(@"ImplementationType")
.EndEntry()
.EndList());
}

private static IEnumerable<FormatViewDefinition> ViewsOf_System_Management_Automation_ShellVariable()
{
yield return new FormatViewDefinition("ShellVariable",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,6 @@ namespace System.Management.Automation.Subsystem.DSC
/// </summary>
public interface ICrossPlatformDsc : ISubsystem
{
/// <summary>
/// Subsystem kind.
/// </summary>
SubsystemKind ISubsystem.Kind => SubsystemKind.CrossPlatformDsc;

/// <summary>
/// Default implementation. No function is required for this subsystem.
/// </summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,11 +24,6 @@ public interface IFeedbackProvider : ISubsystem
/// </summary>
Dictionary<string, string>? ISubsystem.FunctionsToDefine => null;

/// <summary>
/// Default implementation for `ISubsystem.Kind`.
/// </summary>
SubsystemKind ISubsystem.Kind => SubsystemKind.FeedbackProvider;

/// <summary>
/// Gets feedback based on the given commandline and error record.
/// </summary>
Expand Down Expand Up @@ -123,8 +118,6 @@ internal UnixCommandNotFound()

Dictionary<string, string>? ISubsystem.FunctionsToDefine => null;

SubsystemKind ISubsystem.Kind => SubsystemKind.FeedbackProvider | SubsystemKind.CommandPredictor;

public Guid Id => _guid;

public string Name => "cmd-not-found";
Expand Down
19 changes: 6 additions & 13 deletions src/System.Management.Automation/engine/Subsystem/ISubsystem.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,11 @@ namespace System.Management.Automation.Subsystem
{
/// <summary>
/// Define the kinds of subsystems.
/// Allow composite enum values to enable one subsystem implementation to serve as multiple subystems.
/// </summary>
[Flags]
/// <remarks>
/// This enum uses power of 2 as the values for the enum elements, so as to make sure
/// the bitwise 'or' operation of the elements always results in an invalid value.
/// </remarks>
public enum SubsystemKind : uint
{
/// <summary>
Expand All @@ -36,12 +38,8 @@ public enum SubsystemKind : uint
/// The API contracts for specific subsystems are defined within the specific interfaces/abstract classes that implements this interface.
/// </summary>
/// <remarks>
/// There are two purposes to have the internal member `Kind` declared in 'ISubsystem':
/// 1. Make the mapping from an `ISubsystem` implementation to the `SubsystemKind` easy;
/// 2. Make sure a user cannot directly implement 'ISubsystem', but have to derive from one of the concrete subsystem interface or abstract class.
/// <para/>
/// The internal member needs to have a default implementation defined by the specific subsystem interfaces or abstract class,
/// because it should be the same for a specific kind of subsystem.
/// A user should not directly implement <see cref="ISubsystem"/>, but instead should derive from one of the concrete subsystem interfaces or abstract classes.
/// The instance of a type that only implements 'ISubsystem' cannot be registered to the <see cref="SubsystemManager"/>.
/// </remarks>
public interface ISubsystem
{
Expand All @@ -65,10 +63,5 @@ public interface ISubsystem
/// Key: function name; Value: function script.
/// </summary>
Dictionary<string, string>? FunctionsToDefine { get; }

/// <summary>
/// Gets the subsystem kind.
/// </summary>
internal SubsystemKind Kind { get; }
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -21,11 +21,6 @@ public interface ICommandPredictor : ISubsystem
/// </summary>
Dictionary<string, string>? ISubsystem.FunctionsToDefine => null;

/// <summary>
/// Default implementation for `ISubsystem.Kind`.
/// </summary>
SubsystemKind ISubsystem.Kind => SubsystemKind.CommandPredictor;

/// <summary>
/// Get the predictive suggestions. It indicates the start of a suggestion rendering session.
/// </summary>
Expand Down
46 changes: 28 additions & 18 deletions src/System.Management.Automation/engine/Subsystem/SubsystemInfo.cs
Original file line number Diff line number Diff line change
Expand Up @@ -18,22 +18,22 @@ public abstract class SubsystemInfo
#region "Metadata of a Subsystem (public)"

/// <summary>
/// The kind of a concrete subsystem.
/// Gets the kind of a concrete subsystem.
/// </summary>
public SubsystemKind Kind { get; }

/// <summary>
/// The type of a concrete subsystem.
/// Gets the type of a concrete subsystem.
/// </summary>
public Type SubsystemType { get; }

/// <summary>
/// Indicate whether the subsystem allows to unregister an implementation.
/// Gets a value indicating whether the subsystem allows to unregister an implementation.
/// </summary>
public bool AllowUnregistration { get; private set; }

/// <summary>
/// Indicate whether the subsystem allows to have multiple implementations registered.
/// Gets a value indicating whether the subsystem allows to have multiple implementations registered.
/// </summary>
public bool AllowMultipleRegistration { get; private set; }

Expand Down Expand Up @@ -78,8 +78,6 @@ public abstract class SubsystemInfo

private protected SubsystemInfo(SubsystemKind kind, Type subsystemType)
{
Requires.OneSpecificSubsystemKind(kind);

_syncObj = new object();
_cachedImplInfos = Utils.EmptyReadOnlyCollection<ImplementationInfo>();

Expand Down Expand Up @@ -161,10 +159,10 @@ internal static SubsystemInfo Create<TConcreteSubsystem>(
/// </summary>
public class ImplementationInfo
{
internal ImplementationInfo(ISubsystem implementation)
internal ImplementationInfo(SubsystemKind kind, ISubsystem implementation)
{
Id = implementation.Id;
Kind = implementation.Kind;
Kind = kind;
Name = implementation.Name;
Description = implementation.Description;
ImplementationType = implementation.GetType();
Expand Down Expand Up @@ -219,6 +217,7 @@ internal SubsystemInfoImpl(SubsystemKind kind)
/// In the subsystem scenario, registration operations will be minimum, and in most cases, the registered
/// implementation will never be unregistered, so optimization for reading is more important.
/// </remarks>
/// <param name="rawImpl">The subsystem implementation to be added.</param>
private protected override void AddImplementation(ISubsystem rawImpl)
{
lock (_syncObj)
Expand All @@ -228,7 +227,7 @@ private protected override void AddImplementation(ISubsystem rawImpl)
if (_registeredImpls.Count == 0)
{
_registeredImpls = new ReadOnlyCollection<TConcreteSubsystem>(new[] { impl });
_cachedImplInfos = new ReadOnlyCollection<ImplementationInfo>(new[] { new ImplementationInfo(impl) });
_cachedImplInfos = new ReadOnlyCollection<ImplementationInfo>(new[] { new ImplementationInfo(Kind, impl) });
return;
}

Expand All @@ -252,12 +251,17 @@ private protected override void AddImplementation(ISubsystem rawImpl)
}
}

var list = new List<TConcreteSubsystem>(_registeredImpls.Count + 1);
list.AddRange(_registeredImpls);
list.Add(impl);
int newCapacity = _registeredImpls.Count + 1;
var implList = new List<TConcreteSubsystem>(newCapacity);
implList.AddRange(_registeredImpls);
implList.Add(impl);

var implInfo = new List<ImplementationInfo>(newCapacity);
implInfo.AddRange(_cachedImplInfos);
implInfo.Add(new ImplementationInfo(Kind, impl));

_registeredImpls = new ReadOnlyCollection<TConcreteSubsystem>(list);
_cachedImplInfos = new ReadOnlyCollection<ImplementationInfo>(list.ConvertAll(static s => new ImplementationInfo(s)));
_registeredImpls = new ReadOnlyCollection<TConcreteSubsystem>(implList);
_cachedImplInfos = new ReadOnlyCollection<ImplementationInfo>(implInfo);
}
}

Expand All @@ -270,6 +274,8 @@ private protected override void AddImplementation(ISubsystem rawImpl)
/// In the subsystem scenario, registration operations will be minimum, and in most cases, the registered
/// implementation will never be unregistered, so optimization for reading is more important.
/// </remarks>
/// <param name="id">The id of the subsystem implementation to be removed.</param>
/// <returns>The subsystem implementation that was removed.</returns>
private protected override ISubsystem RemoveImplementation(Guid id)
{
if (!AllowUnregistration)
Expand Down Expand Up @@ -316,19 +322,23 @@ private protected override ISubsystem RemoveImplementation(Guid id)
}
else
{
var list = new List<TConcreteSubsystem>(_registeredImpls.Count - 1);
int newCapacity = _registeredImpls.Count - 1;
var implList = new List<TConcreteSubsystem>(newCapacity);
var implInfo = new List<ImplementationInfo>(newCapacity);

for (int i = 0; i < _registeredImpls.Count; i++)
{
if (index == i)
{
continue;
}

list.Add(_registeredImpls[i]);
implList.Add(_registeredImpls[i]);
implInfo.Add(_cachedImplInfos[i]);
}

_registeredImpls = new ReadOnlyCollection<TConcreteSubsystem>(list);
_cachedImplInfos = new ReadOnlyCollection<ImplementationInfo>(list.ConvertAll(static s => new ImplementationInfo(s)));
_registeredImpls = new ReadOnlyCollection<TConcreteSubsystem>(implList);
_cachedImplInfos = new ReadOnlyCollection<ImplementationInfo>(implInfo);
}

return target;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -139,9 +139,12 @@ public static SubsystemInfo GetSubsystemInfo(Type subsystemType)
}

throw new ArgumentException(
StringUtil.Format(
SubsystemStrings.SubsystemTypeUnknown,
subsystemType.FullName));
subsystemType == typeof(ISubsystem)
? SubsystemStrings.MustUseConcreteSubsystemType
: StringUtil.Format(
SubsystemStrings.SubsystemTypeUnknown,
subsystemType.FullName),
nameof(subsystemType));
}

/// <summary>
Expand All @@ -151,8 +154,6 @@ public static SubsystemInfo GetSubsystemInfo(Type subsystemType)
/// <returns>The <see cref="SubsystemInfo"/> object that represents the concrete subsystem.</returns>
public static SubsystemInfo GetSubsystemInfo(SubsystemKind kind)
{
Requires.OneSpecificSubsystemKind(kind);

if (s_subSystemKindMap.TryGetValue(kind, out SubsystemInfo? subsystemInfo))
{
return subsystemInfo;
Expand All @@ -161,7 +162,8 @@ public static SubsystemInfo GetSubsystemInfo(SubsystemKind kind)
throw new ArgumentException(
StringUtil.Format(
SubsystemStrings.SubsystemKindUnknown,
kind.ToString()));
kind.ToString()),
nameof(kind));
}

#endregion
Expand Down Expand Up @@ -191,19 +193,19 @@ public static void RegisterSubsystem<TConcreteSubsystem, TImplementation>(TImple
public static void RegisterSubsystem(SubsystemKind kind, ISubsystem proxy)
{
Requires.NotNull(proxy, nameof(proxy));
Requires.OneSpecificSubsystemKind(kind);

if (!proxy.Kind.HasFlag(kind))
SubsystemInfo info = GetSubsystemInfo(kind);
if (!info.SubsystemType.IsAssignableFrom(proxy.GetType()))
{
throw new ArgumentException(
StringUtil.Format(
SubsystemStrings.ImplementationMismatch,
proxy.Kind.ToString(),
kind.ToString()),
SubsystemStrings.ConcreteSubsystemNotImplemented,
kind.ToString(),
info.SubsystemType.Name),
nameof(proxy));
}

RegisterSubsystem(GetSubsystemInfo(kind), proxy);
RegisterSubsystem(info, proxy);
}

private static void RegisterSubsystem(SubsystemInfo subsystemInfo, ISubsystem proxy)
Expand Down Expand Up @@ -278,8 +280,6 @@ public static void UnregisterSubsystem<TConcreteSubsystem>(Guid id)
/// <param name="id">The Id of the implementation to be unregistered.</param>
public static void UnregisterSubsystem(SubsystemKind kind, Guid id)
{
Requires.OneSpecificSubsystemKind(kind);

UnregisterSubsystem(GetSubsystemInfo(kind), id);
}

Expand Down
14 changes: 0 additions & 14 deletions src/System.Management.Automation/engine/Utils.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1768,19 +1768,5 @@ internal static void Condition([DoesNotReturnIf(false)] bool precondition, strin
throw new ArgumentException(paramName);
}
}

internal static void OneSpecificSubsystemKind(Subsystem.SubsystemKind kind)
{
uint value = (uint)kind;
if (value == 0 || (value & (value - 1)) != 0)
{
// The value is either invalid or a composite value because it's not power of 2.
throw new ArgumentException(
StringUtil.Format(
SubsystemStrings.RequireOneSpecificSubsystemKind,
kind.ToString()),
nameof(kind));
}
}
}
}
10 changes: 5 additions & 5 deletions src/System.Management.Automation/resources/SubsystemStrings.resx
Original file line number Diff line number Diff line change
Expand Up @@ -135,11 +135,14 @@
<data name="SubsystemTypeUnknown" xml:space="preserve">
<value>The specified subsystem type '{0}' is unknown.</value>
</data>
<data name="MustUseConcreteSubsystemType" xml:space="preserve">
<value>You must specify a concrete subsystem type instead of the base interface 'ISubsystem'.</value>
</data>
<data name="SubsystemKindUnknown" xml:space="preserve">
<value>The specified subsystem kind '{0}' is unknown.</value>
</data>
<data name="ImplementationMismatch" xml:space="preserve">
<value>The specified implementation instance implements the subsystem '{0}', which does not match the target subsystem '{1}'.</value>
<data name="ConcreteSubsystemNotImplemented" xml:space="preserve">
<value>For the target subsystem kind '{0}', the specified subsystem instance needs to implement the corresponding concrete interface or abstract class '{1}'.</value>
</data>
<data name="InvalidSubsystemInfo" xml:space="preserve">
<value>The declared metadata for subsystem kind '{0}' is invalid. A subsystem that requires cmdlets or functions to be defined cannot allow multiple registrations because that would result in one implementation overwriting the commands defined by another implementation.</value>
Expand All @@ -153,7 +156,4 @@
<data name="NullOrEmptyImplementationDescription" xml:space="preserve">
<value>The 'Description' property of an implementation for the subsystem '{0}' cannot be null or an empty string.</value>
</data>
<data name="RequireOneSpecificSubsystemKind" xml:space="preserve">
<value>The subsystem kind here should represent one specific subsystem, but the given value is either invalid or a composite enum value: '{0}'.</value>
</data>
</root>
Loading