mirror of
https://github.com/aphrody-labs/PowerShell.git
synced 2026-10-17 20:56:02 +00:00
Use https in URLS across the repository where the domain serves content by https.
5012 lines
183 KiB
C#
5012 lines
183 KiB
C#
// Copyright (c) Microsoft Corporation. All rights reserved.
|
|
// Licensed under the MIT License.
|
|
|
|
using System.Collections;
|
|
using System.Collections.Generic;
|
|
using System.Collections.ObjectModel;
|
|
using System.Collections.Specialized;
|
|
using System.ComponentModel;
|
|
using System.Globalization;
|
|
using System.Linq;
|
|
using System.Reflection;
|
|
using System.Management.Automation.Language;
|
|
using System.Management.Automation.Internal;
|
|
using System.Management.Automation.Interpreter;
|
|
using System.Text;
|
|
|
|
using Microsoft.PowerShell;
|
|
using TypeTable = System.Management.Automation.Runspaces.TypeTable;
|
|
|
|
#pragma warning disable 1634, 1691 // Stops compiler from warning about unknown warnings
|
|
#pragma warning disable 56503
|
|
|
|
namespace System.Management.Automation
|
|
{
|
|
#region PSMemberInfo
|
|
|
|
/// <summary>
|
|
/// Enumerates all possible types of members.
|
|
/// </summary>
|
|
[TypeConverterAttribute(typeof(LanguagePrimitives.EnumMultipleTypeConverter))]
|
|
[FlagsAttribute()]
|
|
public enum PSMemberTypes
|
|
{
|
|
/// <summary>
|
|
/// An alias to another member.
|
|
/// </summary>
|
|
AliasProperty = 1,
|
|
|
|
/// <summary>
|
|
/// A property defined as a reference to a method.
|
|
/// </summary>
|
|
CodeProperty = 2,
|
|
|
|
/// <summary>
|
|
/// A property from the BaseObject.
|
|
/// </summary>
|
|
Property = 4,
|
|
|
|
/// <summary>
|
|
/// A property defined by a Name-Value pair.
|
|
/// </summary>
|
|
NoteProperty = 8,
|
|
|
|
/// <summary>
|
|
/// A property defined by script language.
|
|
/// </summary>
|
|
ScriptProperty = 16,
|
|
|
|
/// <summary>
|
|
/// A set of properties.
|
|
/// </summary>
|
|
PropertySet = 32,
|
|
|
|
/// <summary>
|
|
/// A method from the BaseObject.
|
|
/// </summary>
|
|
Method = 64,
|
|
|
|
/// <summary>
|
|
/// A method defined as a reference to another method.
|
|
/// </summary>
|
|
CodeMethod = 128,
|
|
|
|
/// <summary>
|
|
/// A method defined as a script.
|
|
/// </summary>
|
|
ScriptMethod = 256,
|
|
|
|
/// <summary>
|
|
/// A member that acts like a Property that takes parameters. This is not consider to be a property or a method.
|
|
/// </summary>
|
|
ParameterizedProperty = 512,
|
|
|
|
/// <summary>
|
|
/// A set of members.
|
|
/// </summary>
|
|
MemberSet = 1024,
|
|
|
|
/// <summary>
|
|
/// All events.
|
|
/// </summary>
|
|
Event = 2048,
|
|
|
|
/// <summary>
|
|
/// All dynamic members (where PowerShell cannot know the type of the member)
|
|
/// </summary>
|
|
Dynamic = 4096,
|
|
|
|
/// <summary>
|
|
/// Members that are inferred by type inference for PSObject and hashtable.
|
|
/// </summary>
|
|
InferredProperty = 8192,
|
|
/// <summary>
|
|
/// All property member types.
|
|
/// </summary>
|
|
Properties = AliasProperty | CodeProperty | Property | NoteProperty | ScriptProperty | InferredProperty,
|
|
|
|
/// <summary>
|
|
/// All method member types.
|
|
/// </summary>
|
|
Methods = CodeMethod | Method | ScriptMethod,
|
|
|
|
/// <summary>
|
|
/// All member types.
|
|
/// </summary>
|
|
All = Properties | Methods | Event | PropertySet | MemberSet | ParameterizedProperty | Dynamic
|
|
}
|
|
|
|
/// <summary>
|
|
/// Enumerator for all possible views available on a PSObject.
|
|
/// </summary>
|
|
[TypeConverterAttribute(typeof(LanguagePrimitives.EnumMultipleTypeConverter))]
|
|
[FlagsAttribute()]
|
|
public enum PSMemberViewTypes
|
|
{
|
|
/// <summary>
|
|
/// Extended methods / properties.
|
|
/// </summary>
|
|
Extended = 1,
|
|
|
|
/// <summary>
|
|
/// Adapted methods / properties.
|
|
/// </summary>
|
|
Adapted = 2,
|
|
|
|
/// <summary>
|
|
/// Base methods / properties.
|
|
/// </summary>
|
|
Base = 4,
|
|
|
|
/// <summary>
|
|
/// All methods / properties.
|
|
/// </summary>
|
|
All = Extended | Adapted | Base
|
|
}
|
|
|
|
/// <summary>
|
|
/// Match options.
|
|
/// </summary>
|
|
[FlagsAttribute]
|
|
internal enum MshMemberMatchOptions
|
|
{
|
|
/// <summary>
|
|
/// No options.
|
|
/// </summary>
|
|
None = 0,
|
|
|
|
/// <summary>
|
|
/// Hidden members should be displayed.
|
|
/// </summary>
|
|
IncludeHidden = 1,
|
|
|
|
/// <summary>
|
|
/// Only include members with <see cref="PSMemberInfo.ShouldSerialize"/> property set to <c>true</c>
|
|
/// </summary>
|
|
OnlySerializable = 2
|
|
}
|
|
|
|
/// <summary>
|
|
/// Serves as the base class for all members of an PSObject.
|
|
/// </summary>
|
|
public abstract class PSMemberInfo
|
|
{
|
|
internal object instance;
|
|
internal string name;
|
|
internal bool ShouldSerialize { get; set; }
|
|
|
|
internal virtual void ReplicateInstance(object particularInstance)
|
|
{
|
|
this.instance = particularInstance;
|
|
}
|
|
|
|
internal void SetValueNoConversion(object setValue)
|
|
{
|
|
if (!(this is PSProperty thisAsProperty))
|
|
{
|
|
this.Value = setValue;
|
|
return;
|
|
}
|
|
|
|
thisAsProperty.SetAdaptedValue(setValue, false);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Initializes a new instance of an PSMemberInfo derived class.
|
|
/// </summary>
|
|
protected PSMemberInfo()
|
|
{
|
|
ShouldSerialize = true;
|
|
IsInstance = true;
|
|
}
|
|
|
|
internal void CloneBaseProperties(PSMemberInfo destiny)
|
|
{
|
|
destiny.name = name;
|
|
destiny.IsHidden = IsHidden;
|
|
destiny.IsReservedMember = IsReservedMember;
|
|
destiny.IsInstance = IsInstance;
|
|
destiny.instance = instance;
|
|
destiny.ShouldSerialize = ShouldSerialize;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets the member type.
|
|
/// </summary>
|
|
public abstract PSMemberTypes MemberType { get; }
|
|
|
|
/// <summary>
|
|
/// Gets the member name.
|
|
/// </summary>
|
|
public string Name => this.name;
|
|
|
|
/// <summary>
|
|
/// Allows a derived class to set the member name...
|
|
/// </summary>
|
|
/// <param name="name"></param>
|
|
protected void SetMemberName(string name)
|
|
{
|
|
if (string.IsNullOrEmpty(name))
|
|
{
|
|
throw PSTraceSource.NewArgumentException("name");
|
|
}
|
|
|
|
this.name = name;
|
|
}
|
|
|
|
/// <summary>
|
|
/// True if this is one of the reserved members.
|
|
/// </summary>
|
|
internal bool IsReservedMember { get; set; }
|
|
|
|
/// <summary>
|
|
/// True if the member should be hidden when searching with PSMemberInfoInternalCollection's Match
|
|
/// or enumerating a collection.
|
|
/// This should not be settable as it would make the count of hidden properties in
|
|
/// PSMemberInfoInternalCollection invalid.
|
|
/// For now, we are carefully setting this.isHidden before adding
|
|
/// the members toPSObjectMembersetCollection. In the future, we might need overload for all
|
|
/// PSMemberInfo constructors to take isHidden.
|
|
/// </summary>
|
|
internal bool IsHidden { get; set; }
|
|
|
|
/// <summary>
|
|
/// True if this member has been added to the instance as opposed to
|
|
/// coming from the adapter or from type data.
|
|
/// </summary>
|
|
public bool IsInstance { get; internal set; }
|
|
|
|
/// <summary>
|
|
/// Gets and Sets the value of this member.
|
|
/// </summary>
|
|
/// <exception cref="GetValueException">When getting the value of a property throws an exception.
|
|
/// This exception is also thrown if the property is an <see cref="PSScriptProperty"/> and there
|
|
/// is no Runspace to run the script.</exception>
|
|
/// <exception cref="SetValueException">When setting the value of a property throws an exception.
|
|
/// This exception is also thrown if the property is an <see cref="PSScriptProperty"/> and there
|
|
/// is no Runspace to run the script.</exception>
|
|
/// <exception cref="ExtendedTypeSystemException">When some problem other then getting/setting the value happened.</exception>
|
|
public abstract object Value { get; set; }
|
|
|
|
/// <summary>
|
|
/// Gets the type of the value for this member.
|
|
/// </summary>
|
|
/// <exception cref="ExtendedTypeSystemException">When there was a problem getting the property.</exception>
|
|
public abstract string TypeNameOfValue { get; }
|
|
|
|
/// <summary>
|
|
/// Returns a new PSMemberInfo that is a copy of this PSMemberInfo.
|
|
/// </summary>
|
|
/// <returns>A new PSMemberInfo that is a copy of this PSMemberInfo.</returns>
|
|
public abstract PSMemberInfo Copy();
|
|
|
|
internal bool MatchesOptions(MshMemberMatchOptions options)
|
|
{
|
|
if (this.IsHidden && (0 == (options & MshMemberMatchOptions.IncludeHidden)))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
if (!this.ShouldSerialize && (0 != (options & MshMemberMatchOptions.OnlySerializable)))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Serves as a base class for all members that behave like properties.
|
|
/// </summary>
|
|
public abstract class PSPropertyInfo : PSMemberInfo
|
|
{
|
|
/// <summary>
|
|
/// Initializes a new instance of an PSPropertyInfo derived class.
|
|
/// </summary>
|
|
protected PSPropertyInfo()
|
|
{
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets true if this property can be set.
|
|
/// </summary>
|
|
public abstract bool IsSettable { get; }
|
|
|
|
/// <summary>
|
|
/// Gets true if this property can be read.
|
|
/// </summary>
|
|
public abstract bool IsGettable { get; }
|
|
|
|
internal Exception NewSetValueException(Exception e, string errorId)
|
|
{
|
|
return new SetValueInvocationException(errorId,
|
|
e,
|
|
ExtendedTypeSystem.ExceptionWhenSetting,
|
|
this.Name, e.Message);
|
|
}
|
|
|
|
internal Exception NewGetValueException(Exception e, string errorId)
|
|
{
|
|
return new GetValueInvocationException(errorId,
|
|
e,
|
|
ExtendedTypeSystem.ExceptionWhenGetting,
|
|
this.Name, e.Message);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Serves as an alias to another member.
|
|
/// </summary>
|
|
/// <remarks>
|
|
/// It is permitted to subclass <see cref="PSAliasProperty"/>
|
|
/// but there is no established scenario for doing this, nor has it been tested.
|
|
/// </remarks>
|
|
public class PSAliasProperty : PSPropertyInfo
|
|
{
|
|
/// <summary>
|
|
/// Returns the string representation of this property.
|
|
/// </summary>
|
|
/// <returns>This property as a string.</returns>
|
|
public override string ToString()
|
|
{
|
|
StringBuilder returnValue = new StringBuilder();
|
|
returnValue.Append(this.Name);
|
|
returnValue.Append(" = ");
|
|
if (ConversionType != null)
|
|
{
|
|
returnValue.Append("(");
|
|
returnValue.Append(ConversionType);
|
|
returnValue.Append(")");
|
|
}
|
|
|
|
returnValue.Append(ReferencedMemberName);
|
|
return returnValue.ToString();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Initializes a new instance of PSAliasProperty setting the name of the alias
|
|
/// and the name of the member this alias refers to.
|
|
/// </summary>
|
|
/// <param name="name">Name of the alias.</param>
|
|
/// <param name="referencedMemberName">Name of the member this alias refers to.</param>
|
|
/// <exception cref="ArgumentException">For invalid arguments.</exception>
|
|
public PSAliasProperty(string name, string referencedMemberName)
|
|
{
|
|
if (string.IsNullOrEmpty(name))
|
|
{
|
|
throw PSTraceSource.NewArgumentException("name");
|
|
}
|
|
|
|
this.name = name;
|
|
if (string.IsNullOrEmpty(referencedMemberName))
|
|
{
|
|
throw PSTraceSource.NewArgumentException("referencedMemberName");
|
|
}
|
|
|
|
ReferencedMemberName = referencedMemberName;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Initializes a new instance of PSAliasProperty setting the name of the alias,
|
|
/// the name of the member this alias refers to and the type to convert the referenced
|
|
/// member's value.
|
|
/// </summary>
|
|
/// <param name="name">Name of the alias.</param>
|
|
/// <param name="referencedMemberName">Name of the member this alias refers to.</param>
|
|
/// <param name="conversionType">The type to convert the referenced member's value.</param>
|
|
/// <exception cref="ArgumentException">For invalid arguments.</exception>
|
|
public PSAliasProperty(string name, string referencedMemberName, Type conversionType)
|
|
{
|
|
if (string.IsNullOrEmpty(name))
|
|
{
|
|
throw PSTraceSource.NewArgumentException("name");
|
|
}
|
|
|
|
this.name = name;
|
|
if (string.IsNullOrEmpty(referencedMemberName))
|
|
{
|
|
throw PSTraceSource.NewArgumentException("referencedMemberName");
|
|
}
|
|
|
|
ReferencedMemberName = referencedMemberName;
|
|
// conversionType is optional and can be null
|
|
ConversionType = conversionType;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets the name of the member this alias refers to.
|
|
/// </summary>
|
|
public string ReferencedMemberName { get; }
|
|
|
|
/// <summary>
|
|
/// Gets the member this alias refers to.
|
|
/// </summary>
|
|
internal PSMemberInfo ReferencedMember => this.LookupMember(ReferencedMemberName);
|
|
|
|
/// <summary>
|
|
/// Gets the the type to convert the referenced member's value. It might be
|
|
/// null when no conversion is done.
|
|
/// </summary>
|
|
public Type ConversionType { get; private set; }
|
|
|
|
#region virtual implementation
|
|
|
|
/// <summary>
|
|
/// Returns a new PSMemberInfo that is a copy of this PSMemberInfo.
|
|
/// </summary>
|
|
/// <returns>A new PSMemberInfo that is a copy of this PSMemberInfo.</returns>
|
|
public override PSMemberInfo Copy()
|
|
{
|
|
PSAliasProperty alias = new PSAliasProperty(name, ReferencedMemberName) {ConversionType = ConversionType};
|
|
CloneBaseProperties(alias);
|
|
return alias;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets the member type.
|
|
/// </summary>
|
|
public override PSMemberTypes MemberType => PSMemberTypes.AliasProperty;
|
|
|
|
/// <summary>
|
|
/// Gets the type of the value for this member.
|
|
/// </summary>
|
|
/// <exception cref="ExtendedTypeSystemException">
|
|
/// When
|
|
/// the alias has not been added to an PSObject or
|
|
/// the alias has a cycle or
|
|
/// an aliased member is not present
|
|
/// </exception>
|
|
public override string TypeNameOfValue
|
|
{
|
|
get
|
|
{
|
|
if (ConversionType != null)
|
|
{
|
|
return ConversionType.FullName;
|
|
}
|
|
|
|
return this.ReferencedMember.TypeNameOfValue;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets true if this property can be set.
|
|
/// </summary>
|
|
/// <exception cref="ExtendedTypeSystemException">
|
|
/// When
|
|
/// the alias has not been added to an PSObject or
|
|
/// the alias has a cycle or
|
|
/// an aliased member is not present
|
|
/// </exception>
|
|
public override bool IsSettable
|
|
{
|
|
get
|
|
{
|
|
if (this.ReferencedMember is PSPropertyInfo memberProperty)
|
|
{
|
|
return memberProperty.IsSettable;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets true if this property can be read.
|
|
/// </summary>
|
|
/// <exception cref="ExtendedTypeSystemException">
|
|
/// When
|
|
/// the alias has not been added to an PSObject or
|
|
/// the alias has a cycle or
|
|
/// an aliased member is not present
|
|
/// </exception>
|
|
public override bool IsGettable
|
|
{
|
|
get
|
|
{
|
|
if (this.ReferencedMember is PSPropertyInfo memberProperty)
|
|
{
|
|
return memberProperty.IsGettable;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
}
|
|
|
|
private PSMemberInfo LookupMember(string name)
|
|
{
|
|
LookupMember(name, new HashSet<string>(StringComparer.OrdinalIgnoreCase), out PSMemberInfo returnValue, out bool hasCycle);
|
|
if (hasCycle)
|
|
{
|
|
throw new
|
|
ExtendedTypeSystemException(
|
|
"CycleInAliasLookup",
|
|
null,
|
|
ExtendedTypeSystem.CycleInAlias,
|
|
this.Name);
|
|
}
|
|
|
|
return returnValue;
|
|
}
|
|
|
|
private void LookupMember(string name, HashSet<string> visitedAliases, out PSMemberInfo returnedMember, out bool hasCycle)
|
|
{
|
|
returnedMember = null;
|
|
if (this.instance == null)
|
|
{
|
|
throw new ExtendedTypeSystemException("AliasLookupMemberOutsidePSObject",
|
|
null,
|
|
ExtendedTypeSystem.AccessMemberOutsidePSObject,
|
|
name);
|
|
}
|
|
|
|
PSMemberInfo member = PSObject.AsPSObject(this.instance).Properties[name];
|
|
if (member == null)
|
|
{
|
|
throw new ExtendedTypeSystemException(
|
|
"AliasLookupMemberNotPresent",
|
|
null,
|
|
ExtendedTypeSystem.MemberNotPresent,
|
|
name);
|
|
}
|
|
|
|
if (!(member is PSAliasProperty aliasMember))
|
|
{
|
|
hasCycle = false;
|
|
returnedMember = member;
|
|
return;
|
|
}
|
|
|
|
if (visitedAliases.Contains(name))
|
|
{
|
|
hasCycle = true;
|
|
return;
|
|
}
|
|
|
|
visitedAliases.Add(name);
|
|
LookupMember(aliasMember.ReferencedMemberName, visitedAliases, out returnedMember, out hasCycle);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets and Sets the value of this member.
|
|
/// </summary>
|
|
/// <exception cref="ExtendedTypeSystemException">
|
|
/// When
|
|
/// the alias has not been added to an PSObject or
|
|
/// the alias has a cycle or
|
|
/// an aliased member is not present
|
|
/// </exception>
|
|
/// <exception cref="GetValueException">When getting the value of a property throws an exception.</exception>
|
|
/// <exception cref="SetValueException">When setting the value of a property throws an exception.</exception>
|
|
public override object Value
|
|
{
|
|
get
|
|
{
|
|
object returnValue = this.ReferencedMember.Value;
|
|
if (ConversionType != null)
|
|
{
|
|
returnValue = LanguagePrimitives.ConvertTo(returnValue, ConversionType, CultureInfo.InvariantCulture);
|
|
}
|
|
|
|
return returnValue;
|
|
}
|
|
|
|
set => this.ReferencedMember.Value = value;
|
|
}
|
|
|
|
#endregion virtual implementation
|
|
}
|
|
|
|
/// <summary>
|
|
/// Serves as a property implemented with references to methods for getter and setter.
|
|
/// </summary>
|
|
/// <remarks>
|
|
/// It is permitted to subclass <see cref="PSCodeProperty"/>
|
|
/// but there is no established scenario for doing this, nor has it been tested.
|
|
/// </remarks>
|
|
public class PSCodeProperty : PSPropertyInfo
|
|
{
|
|
/// <summary>
|
|
/// Returns the string representation of this property.
|
|
/// </summary>
|
|
/// <returns>This property as a string.</returns>
|
|
public override string ToString()
|
|
{
|
|
StringBuilder returnValue = new StringBuilder();
|
|
returnValue.Append(this.TypeNameOfValue);
|
|
returnValue.Append(" ");
|
|
returnValue.Append(this.Name);
|
|
returnValue.Append("{");
|
|
if (this.IsGettable)
|
|
{
|
|
returnValue.Append("get=");
|
|
returnValue.Append(GetterCodeReference.Name);
|
|
returnValue.Append(";");
|
|
}
|
|
|
|
if (this.IsSettable)
|
|
{
|
|
returnValue.Append("set=");
|
|
returnValue.Append(SetterCodeReference.Name);
|
|
returnValue.Append(";");
|
|
}
|
|
|
|
returnValue.Append("}");
|
|
return returnValue.ToString();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Called from TypeTableUpdate before SetSetterFromTypeTable is called.
|
|
/// </summary>
|
|
internal void SetGetterFromTypeTable(Type type, string methodName)
|
|
{
|
|
MethodInfo methodAsMember = null;
|
|
|
|
try
|
|
{
|
|
methodAsMember = type.GetMethod(methodName, BindingFlags.Static | BindingFlags.Public | BindingFlags.IgnoreCase);
|
|
}
|
|
catch (AmbiguousMatchException)
|
|
{
|
|
// Ignore the AmbiguousMatchException.
|
|
// We will generate error below if we cannot find exactly one match method.
|
|
}
|
|
|
|
if (methodAsMember == null)
|
|
{
|
|
throw new ExtendedTypeSystemException(
|
|
"GetterFormatFromTypeTable",
|
|
null,
|
|
ExtendedTypeSystem.CodePropertyGetterFormat);
|
|
}
|
|
|
|
SetGetter(methodAsMember);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Called from TypeTableUpdate after SetGetterFromTypeTable is called.
|
|
/// </summary>
|
|
internal void SetSetterFromTypeTable(Type type, string methodName)
|
|
{
|
|
MethodInfo methodAsMember = null;
|
|
|
|
try
|
|
{
|
|
methodAsMember = type.GetMethod(methodName, BindingFlags.Static | BindingFlags.Public | BindingFlags.IgnoreCase);
|
|
}
|
|
catch (AmbiguousMatchException)
|
|
{
|
|
// Ignore the AmbiguousMatchException.
|
|
// We will generate error below if we cannot find exactly one match method.
|
|
}
|
|
|
|
if (methodAsMember == null)
|
|
{
|
|
throw new ExtendedTypeSystemException(
|
|
"SetterFormatFromTypeTable",
|
|
null,
|
|
ExtendedTypeSystem.CodePropertySetterFormat);
|
|
}
|
|
|
|
SetSetter(methodAsMember, GetterCodeReference);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Used from TypeTable with the internal constructor.
|
|
/// </summary>
|
|
internal void SetGetter(MethodInfo methodForGet)
|
|
{
|
|
if (methodForGet == null)
|
|
{
|
|
GetterCodeReference = null;
|
|
return;
|
|
}
|
|
|
|
if (!CheckGetterMethodInfo(methodForGet))
|
|
{
|
|
throw new ExtendedTypeSystemException(
|
|
"GetterFormat",
|
|
null,
|
|
ExtendedTypeSystem.CodePropertyGetterFormat);
|
|
}
|
|
|
|
GetterCodeReference = methodForGet;
|
|
}
|
|
|
|
internal static bool CheckGetterMethodInfo(MethodInfo methodForGet)
|
|
{
|
|
ParameterInfo[] parameters = methodForGet.GetParameters();
|
|
return methodForGet.IsPublic
|
|
&& methodForGet.IsStatic
|
|
&& methodForGet.ReturnType != typeof(void)
|
|
&& parameters.Length == 1
|
|
&& parameters[0].ParameterType == typeof(PSObject);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Used from TypeTable with the internal constructor.
|
|
/// </summary>
|
|
private void SetSetter(MethodInfo methodForSet, MethodInfo methodForGet)
|
|
{
|
|
if (methodForSet == null)
|
|
{
|
|
if (methodForGet == null)
|
|
{
|
|
throw new ExtendedTypeSystemException(
|
|
"SetterAndGetterNullFormat",
|
|
null,
|
|
ExtendedTypeSystem.CodePropertyGetterAndSetterNull);
|
|
}
|
|
|
|
SetterCodeReference = null;
|
|
return;
|
|
}
|
|
|
|
if (!CheckSetterMethodInfo(methodForSet, methodForGet))
|
|
{
|
|
throw new ExtendedTypeSystemException(
|
|
"SetterFormat",
|
|
null,
|
|
ExtendedTypeSystem.CodePropertySetterFormat);
|
|
}
|
|
|
|
SetterCodeReference = methodForSet;
|
|
}
|
|
|
|
internal static bool CheckSetterMethodInfo(MethodInfo methodForSet, MethodInfo methodForGet)
|
|
{
|
|
ParameterInfo[] parameters = methodForSet.GetParameters();
|
|
return methodForSet.IsPublic
|
|
&& methodForSet.IsStatic
|
|
&& methodForSet.ReturnType == typeof(void)
|
|
&& parameters.Length == 2
|
|
&& parameters[0].ParameterType == typeof(PSObject)
|
|
&& (methodForGet == null || methodForGet.ReturnType == parameters[1].ParameterType);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Used from TypeTable to delay setting getter and setter.
|
|
/// </summary>
|
|
internal PSCodeProperty(string name)
|
|
{
|
|
if (string.IsNullOrEmpty(name))
|
|
{
|
|
throw PSTraceSource.NewArgumentException("name");
|
|
}
|
|
|
|
this.name = name;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Initializes a new instance of the PSCodeProperty class as a read only property.
|
|
/// </summary>
|
|
/// <param name="name">Name of the property.</param>
|
|
/// <param name="getterCodeReference">This should be a public static non void method taking one PSObject parameter.</param>
|
|
/// <exception cref="ArgumentException">If name is null or empty or getterCodeReference is null.</exception>
|
|
/// <exception cref="ExtendedTypeSystemException">If getterCodeReference doesn't have the right format.</exception>
|
|
public PSCodeProperty(string name, MethodInfo getterCodeReference)
|
|
{
|
|
if (string.IsNullOrEmpty(name))
|
|
{
|
|
throw PSTraceSource.NewArgumentException("name");
|
|
}
|
|
|
|
this.name = name;
|
|
if (getterCodeReference == null)
|
|
{
|
|
throw PSTraceSource.NewArgumentNullException("getterCodeReference");
|
|
}
|
|
|
|
SetGetter(getterCodeReference);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Initializes a new instance of the PSCodeProperty class. Setter or getter can be null, but both cannot be null.
|
|
/// </summary>
|
|
/// <param name="name">Name of the property.</param>
|
|
/// <param name="getterCodeReference">This should be a public static non void method taking one PSObject parameter.</param>
|
|
/// <param name="setterCodeReference">This should be a public static void method taking 2 parameters, where the first is an PSObject.</param>
|
|
/// <exception cref="ArgumentException">When methodForGet and methodForSet are null.</exception>
|
|
/// <exception cref="ExtendedTypeSystemException">
|
|
/// if:
|
|
/// - getterCodeReference doesn't have the right format,
|
|
/// - setterCodeReference doesn't have the right format,
|
|
/// - both getterCodeReference and setterCodeReference are null.
|
|
/// </exception>
|
|
public PSCodeProperty(string name, MethodInfo getterCodeReference, MethodInfo setterCodeReference)
|
|
{
|
|
if (string.IsNullOrEmpty(name))
|
|
{
|
|
throw PSTraceSource.NewArgumentException("name");
|
|
}
|
|
|
|
this.name = name;
|
|
if (getterCodeReference == null && setterCodeReference == null)
|
|
{
|
|
throw PSTraceSource.NewArgumentNullException("getterCodeReference setterCodeReference");
|
|
}
|
|
|
|
SetGetter(getterCodeReference);
|
|
SetSetter(setterCodeReference, getterCodeReference);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets the method used for the properties' getter. It might be null.
|
|
/// </summary>
|
|
public MethodInfo GetterCodeReference { get; private set; }
|
|
|
|
/// <summary>
|
|
/// Gets the method used for the properties' setter. It might be null.
|
|
/// </summary>
|
|
public MethodInfo SetterCodeReference { get; private set; }
|
|
|
|
#region virtual implementation
|
|
|
|
/// <summary>
|
|
/// Returns a new PSMemberInfo that is a copy of this PSMemberInfo.
|
|
/// </summary>
|
|
/// <returns>A new PSMemberInfo that is a copy of this PSMemberInfo.</returns>
|
|
public override PSMemberInfo Copy()
|
|
{
|
|
PSCodeProperty property = new PSCodeProperty(name, GetterCodeReference, SetterCodeReference);
|
|
CloneBaseProperties(property);
|
|
return property;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets the member type.
|
|
/// </summary>
|
|
public override PSMemberTypes MemberType => PSMemberTypes.CodeProperty;
|
|
|
|
/// <summary>
|
|
/// Gets true if this property can be set.
|
|
/// </summary>
|
|
public override bool IsSettable => this.SetterCodeReference != null;
|
|
|
|
/// <summary>
|
|
/// Gets true if this property can be read.
|
|
/// </summary>
|
|
public override bool IsGettable => GetterCodeReference != null;
|
|
|
|
/// <summary>
|
|
/// Gets and Sets the value of this member.
|
|
/// </summary>
|
|
/// <exception cref="GetValueException">When getting and there is no getter or when the getter throws an exception.</exception>
|
|
/// <exception cref="SetValueException">When setting and there is no setter or when the setter throws an exception.</exception>
|
|
public override object Value
|
|
{
|
|
get
|
|
{
|
|
if (GetterCodeReference == null)
|
|
{
|
|
throw new GetValueException(
|
|
"GetWithoutGetterFromCodePropertyValue",
|
|
null,
|
|
ExtendedTypeSystem.GetWithoutGetterException,
|
|
this.Name);
|
|
}
|
|
|
|
try
|
|
{
|
|
return GetterCodeReference.Invoke(null, new object[] { this.instance });
|
|
}
|
|
catch (TargetInvocationException ex)
|
|
{
|
|
Exception inner = ex.InnerException ?? ex;
|
|
throw new GetValueInvocationException(
|
|
"CatchFromCodePropertyGetTI",
|
|
inner,
|
|
ExtendedTypeSystem.ExceptionWhenGetting,
|
|
this.name,
|
|
inner.Message);
|
|
}
|
|
catch (Exception e)
|
|
{
|
|
if (e is GetValueException)
|
|
{
|
|
throw;
|
|
}
|
|
|
|
throw new GetValueInvocationException(
|
|
"CatchFromCodePropertyGet",
|
|
e,
|
|
ExtendedTypeSystem.ExceptionWhenGetting,
|
|
this.name,
|
|
e.Message);
|
|
}
|
|
}
|
|
|
|
set
|
|
{
|
|
if (SetterCodeReference == null)
|
|
{
|
|
throw new SetValueException(
|
|
"SetWithoutSetterFromCodeProperty",
|
|
null,
|
|
ExtendedTypeSystem.SetWithoutSetterException,
|
|
this.Name);
|
|
}
|
|
|
|
try
|
|
{
|
|
SetterCodeReference.Invoke(null, new object[] { this.instance, value });
|
|
}
|
|
catch (TargetInvocationException ex)
|
|
{
|
|
Exception inner = ex.InnerException ?? ex;
|
|
throw new SetValueInvocationException(
|
|
"CatchFromCodePropertySetTI",
|
|
inner,
|
|
ExtendedTypeSystem.ExceptionWhenSetting,
|
|
this.name,
|
|
inner.Message);
|
|
}
|
|
catch (Exception e)
|
|
{
|
|
if (e is SetValueException)
|
|
{
|
|
throw;
|
|
}
|
|
|
|
throw new SetValueInvocationException(
|
|
"CatchFromCodePropertySet",
|
|
e,
|
|
ExtendedTypeSystem.ExceptionWhenSetting,
|
|
this.name,
|
|
e.Message);
|
|
}
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets the type of the value for this member.
|
|
/// </summary>
|
|
/// <exception cref="GetValueException">If there is no property getter.</exception>
|
|
public override string TypeNameOfValue
|
|
{
|
|
get
|
|
{
|
|
if (GetterCodeReference == null)
|
|
{
|
|
throw new GetValueException(
|
|
"GetWithoutGetterFromCodePropertyTypeOfValue",
|
|
null,
|
|
ExtendedTypeSystem.GetWithoutGetterException,
|
|
this.Name);
|
|
}
|
|
|
|
return GetterCodeReference.ReturnType.FullName;
|
|
}
|
|
}
|
|
|
|
#endregion virtual implementation
|
|
}
|
|
|
|
/// <summary>
|
|
/// Type used to capture the properties inferred from Hashtable and PSObject.
|
|
/// </summary>
|
|
internal class PSInferredProperty : PSPropertyInfo
|
|
{
|
|
public PSInferredProperty(string name, PSTypeName typeName)
|
|
{
|
|
this.name = name;
|
|
TypeName = typeName;
|
|
}
|
|
|
|
internal PSTypeName TypeName { get; }
|
|
|
|
public override PSMemberTypes MemberType => PSMemberTypes.InferredProperty;
|
|
|
|
public override object Value { get; set; }
|
|
|
|
public override string TypeNameOfValue => TypeName.Name;
|
|
|
|
public override PSMemberInfo Copy() => new PSInferredProperty(Name, TypeName);
|
|
|
|
public override bool IsSettable => false;
|
|
|
|
public override bool IsGettable => false;
|
|
|
|
public override string ToString() => $"{ToStringCodeMethods.Type(TypeName.Type)} {Name}";
|
|
}
|
|
|
|
/// <summary>
|
|
/// Used to access the adapted or base properties from the BaseObject.
|
|
/// </summary>
|
|
public class PSProperty : PSPropertyInfo
|
|
{
|
|
/// <summary>
|
|
/// Returns the string representation of this property.
|
|
/// </summary>
|
|
/// <returns>This property as a string.</returns>
|
|
public override string ToString()
|
|
{
|
|
if (this.isDeserialized)
|
|
{
|
|
StringBuilder returnValue = new StringBuilder();
|
|
returnValue.Append(this.TypeNameOfValue);
|
|
returnValue.Append(" {get;set;}");
|
|
return returnValue.ToString();
|
|
}
|
|
|
|
Diagnostics.Assert((this.baseObject != null) && (this.adapter != null), "if it is deserialized, it should have all these properties set");
|
|
return adapter.BasePropertyToString(this);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Used by the adapters to keep intermediate data used between DoGetProperty and
|
|
/// DoGetValue or DoSetValue.
|
|
/// </summary>
|
|
internal string typeOfValue;
|
|
|
|
internal object serializedValue;
|
|
internal bool isDeserialized;
|
|
|
|
/// <summary>
|
|
/// This will be either instance.adapter or instance.clrAdapter.
|
|
/// </summary>
|
|
internal Adapter adapter;
|
|
|
|
internal object adapterData;
|
|
internal object baseObject;
|
|
|
|
/// <summary>
|
|
/// Constructs a property from a serialized value.
|
|
/// </summary>
|
|
/// <param name="name">Name of the property.</param>
|
|
/// <param name="serializedValue">Value of the property.</param>
|
|
internal PSProperty(string name, object serializedValue)
|
|
{
|
|
this.isDeserialized = true;
|
|
this.serializedValue = serializedValue;
|
|
this.name = name;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Constructs this property.
|
|
/// </summary>
|
|
/// <param name="name">Name of the property.</param>
|
|
/// <param name="adapter">Adapter used in DoGetProperty.</param>
|
|
/// <param name="baseObject">Object passed to DoGetProperty.</param>
|
|
/// <param name="adapterData">Adapter specific data.</param>
|
|
/// <exception cref="ArgumentException">For invalid arguments.</exception>
|
|
internal PSProperty(string name, Adapter adapter, object baseObject, object adapterData)
|
|
{
|
|
if (string.IsNullOrEmpty(name))
|
|
{
|
|
throw PSTraceSource.NewArgumentException("name");
|
|
}
|
|
|
|
this.name = name;
|
|
this.adapter = adapter;
|
|
this.adapterData = adapterData;
|
|
this.baseObject = baseObject;
|
|
}
|
|
|
|
#region virtual implementation
|
|
|
|
/// <summary>
|
|
/// Returns a new PSMemberInfo that is a copy of this PSMemberInfo.
|
|
/// </summary>
|
|
/// <returns>A new PSMemberInfo that is a copy of this PSMemberInfo.</returns>
|
|
public override PSMemberInfo Copy()
|
|
{
|
|
PSProperty property = new PSProperty(this.name, this.adapter, this.baseObject, this.adapterData);
|
|
CloneBaseProperties(property);
|
|
property.typeOfValue = this.typeOfValue;
|
|
property.serializedValue = this.serializedValue;
|
|
property.isDeserialized = this.isDeserialized;
|
|
return property;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets the member type.
|
|
/// </summary>
|
|
public override PSMemberTypes MemberType => PSMemberTypes.Property;
|
|
|
|
private object GetAdaptedValue()
|
|
{
|
|
if (this.isDeserialized)
|
|
{
|
|
return serializedValue;
|
|
}
|
|
|
|
Diagnostics.Assert((this.baseObject != null) && (this.adapter != null), "if it is deserialized, it should have all these properties set");
|
|
|
|
object o = adapter.BasePropertyGet(this);
|
|
return o;
|
|
}
|
|
|
|
internal void SetAdaptedValue(object setValue, bool shouldConvert)
|
|
{
|
|
if (this.isDeserialized)
|
|
{
|
|
serializedValue = setValue;
|
|
return;
|
|
}
|
|
|
|
Diagnostics.Assert((this.baseObject != null) && (this.adapter != null), "if it is deserialized, it should have all these properties set");
|
|
adapter.BasePropertySet(this, setValue, shouldConvert);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets or sets the value of this property.
|
|
/// </summary>
|
|
/// <exception cref="GetValueException">When getting the value of a property throws an exception.</exception>
|
|
/// <exception cref="SetValueException">When setting the value of a property throws an exception.</exception>
|
|
public override object Value
|
|
{
|
|
get => GetAdaptedValue();
|
|
set => SetAdaptedValue(value, true);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets true if this property can be set.
|
|
/// </summary>
|
|
public override bool IsSettable
|
|
{
|
|
get
|
|
{
|
|
if (this.isDeserialized)
|
|
{
|
|
return true;
|
|
}
|
|
|
|
Diagnostics.Assert((this.baseObject != null) && (this.adapter != null), "if it is deserialized, it should have all these properties set");
|
|
return adapter.BasePropertyIsSettable(this);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets true if this property can be read.
|
|
/// </summary>
|
|
public override bool IsGettable
|
|
{
|
|
get
|
|
{
|
|
if (this.isDeserialized)
|
|
{
|
|
return true;
|
|
}
|
|
|
|
Diagnostics.Assert((this.baseObject != null) && (this.adapter != null), "if it is deserialized, it should have all these properties set");
|
|
return adapter.BasePropertyIsGettable(this);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets the type of the value for this member.
|
|
/// </summary>
|
|
public override string TypeNameOfValue
|
|
{
|
|
get
|
|
{
|
|
if (this.isDeserialized)
|
|
{
|
|
if (serializedValue == null)
|
|
{
|
|
return string.Empty;
|
|
}
|
|
|
|
if (serializedValue is PSObject serializedValueAsPSObject)
|
|
{
|
|
var typeNames = serializedValueAsPSObject.InternalTypeNames;
|
|
if ((typeNames != null) && (typeNames.Count >= 1))
|
|
{
|
|
// type name at 0-th index is the most specific type (i.e. deserialized.system.io.directoryinfo)
|
|
// type names at other indices are less specific (i.e. deserialized.system.object)
|
|
return typeNames[0];
|
|
}
|
|
}
|
|
|
|
return serializedValue.GetType().FullName;
|
|
}
|
|
|
|
Diagnostics.Assert((this.baseObject != null) && (this.adapter != null), "if it is deserialized, it should have all these properties set");
|
|
return adapter.BasePropertyType(this);
|
|
}
|
|
}
|
|
|
|
#endregion virtual implementation
|
|
}
|
|
|
|
/// <summary>
|
|
/// A property created by a user-defined PSPropertyAdapter.
|
|
/// </summary>
|
|
public class PSAdaptedProperty : PSProperty
|
|
{
|
|
/// <summary>
|
|
/// Creates a property for the given base object.
|
|
/// </summary>
|
|
/// <param name="name">Name of the property.</param>
|
|
/// <param name="tag">An adapter can use this object to keep any arbitrary data it needs.</param>
|
|
/// <exception cref="ArgumentException">For invalid arguments.</exception>
|
|
public PSAdaptedProperty(string name, object tag)
|
|
: base(name, null, null, tag)
|
|
{
|
|
//
|
|
// Note that the constructor sets the adapter and base object to null; the ThirdPartyAdapter managing this property must set these values
|
|
//
|
|
}
|
|
|
|
internal PSAdaptedProperty(string name, Adapter adapter, object baseObject, object adapterData)
|
|
: base(name, adapter, baseObject, adapterData)
|
|
{
|
|
}
|
|
|
|
/// <summary>
|
|
/// Copy an adapted property.
|
|
/// </summary>
|
|
public override PSMemberInfo Copy()
|
|
{
|
|
PSAdaptedProperty property = new PSAdaptedProperty(this.name, this.adapter, this.baseObject, this.adapterData);
|
|
CloneBaseProperties(property);
|
|
property.typeOfValue = this.typeOfValue;
|
|
property.serializedValue = this.serializedValue;
|
|
property.isDeserialized = this.isDeserialized;
|
|
return property;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets the object the property belongs to.
|
|
/// </summary>
|
|
public object BaseObject => this.baseObject;
|
|
|
|
/// <summary>
|
|
/// Gets the data attached to this property.
|
|
/// </summary>
|
|
public object Tag => this.adapterData;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Serves as a property that is a simple name-value pair.
|
|
/// </summary>
|
|
public class PSNoteProperty : PSPropertyInfo
|
|
{
|
|
/// <summary>
|
|
/// Returns the string representation of this property.
|
|
/// </summary>
|
|
/// <returns>This property as a string.</returns>
|
|
public override string ToString()
|
|
{
|
|
StringBuilder returnValue = new StringBuilder();
|
|
|
|
returnValue.Append(GetDisplayTypeNameOfValue(this.Value));
|
|
returnValue.Append(" ");
|
|
returnValue.Append(this.Name);
|
|
returnValue.Append("=");
|
|
returnValue.Append(this.noteValue == null ? "null" : this.noteValue.ToString());
|
|
return returnValue.ToString();
|
|
}
|
|
|
|
internal object noteValue;
|
|
|
|
/// <summary>
|
|
/// Initializes a new instance of the PSNoteProperty class.
|
|
/// </summary>
|
|
/// <param name="name">Name of the property.</param>
|
|
/// <param name="value">Value of the property.</param>
|
|
/// <exception cref="ArgumentException">For an empty or null name.</exception>
|
|
public PSNoteProperty(string name, object value)
|
|
{
|
|
if (string.IsNullOrEmpty(name))
|
|
{
|
|
throw PSTraceSource.NewArgumentException("name");
|
|
}
|
|
|
|
this.name = name;
|
|
// value can be null
|
|
this.noteValue = value;
|
|
}
|
|
|
|
#region virtual implementation
|
|
|
|
/// <summary>
|
|
/// Returns a new PSMemberInfo that is a copy of this PSMemberInfo.
|
|
/// </summary>
|
|
/// <returns>A new PSMemberInfo that is a copy of this PSMemberInfo.</returns>
|
|
public override PSMemberInfo Copy()
|
|
{
|
|
PSNoteProperty property = new PSNoteProperty(this.name, this.noteValue);
|
|
CloneBaseProperties(property);
|
|
return property;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets PSMemberTypes.NoteProperty.
|
|
/// </summary>
|
|
public override PSMemberTypes MemberType => PSMemberTypes.NoteProperty;
|
|
|
|
/// <summary>
|
|
/// Gets true since the value of an PSNoteProperty can always be set.
|
|
/// </summary>
|
|
public override bool IsSettable => this.IsInstance;
|
|
|
|
/// <summary>
|
|
/// Gets true since the value of an PSNoteProperty can always be obtained.
|
|
/// </summary>
|
|
public override bool IsGettable => true;
|
|
|
|
/// <summary>
|
|
/// Gets or sets the value of this property.
|
|
/// </summary>
|
|
public override object Value
|
|
{
|
|
get => this.noteValue;
|
|
set
|
|
{
|
|
if (!this.IsInstance)
|
|
{
|
|
throw new SetValueException("ChangeValueOfStaticNote",
|
|
null,
|
|
ExtendedTypeSystem.ChangeStaticMember,
|
|
this.Name);
|
|
}
|
|
|
|
this.noteValue = value;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets the type of the value for this member.
|
|
/// </summary>
|
|
public override string TypeNameOfValue
|
|
{
|
|
get
|
|
{
|
|
object val = this.Value;
|
|
|
|
if (val == null)
|
|
{
|
|
return typeof(object).FullName;
|
|
}
|
|
|
|
if (val is PSObject valAsPSObject)
|
|
{
|
|
var typeNames = valAsPSObject.InternalTypeNames;
|
|
if ((typeNames != null) && (typeNames.Count >= 1))
|
|
{
|
|
// type name at 0-th index is the most specific type (i.e. system.string)
|
|
// type names at other indices are less specific (i.e. system.object)
|
|
return typeNames[0];
|
|
}
|
|
}
|
|
|
|
return val.GetType().FullName;
|
|
}
|
|
}
|
|
|
|
#endregion virtual implementation
|
|
|
|
internal static string GetDisplayTypeNameOfValue(object val)
|
|
{
|
|
string displayTypeName = null;
|
|
|
|
if (val is PSObject valAsPSObject)
|
|
{
|
|
var typeNames = valAsPSObject.InternalTypeNames;
|
|
if ((typeNames != null) && (typeNames.Count >= 1))
|
|
{
|
|
displayTypeName = typeNames[0];
|
|
}
|
|
}
|
|
|
|
if (string.IsNullOrEmpty(displayTypeName))
|
|
{
|
|
displayTypeName = val == null
|
|
? "object"
|
|
: ToStringCodeMethods.Type(val.GetType(), dropNamespaces: true);
|
|
}
|
|
|
|
return displayTypeName;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Serves as a property that is a simple name-value pair.
|
|
/// </summary>
|
|
/// <remarks>
|
|
/// It is permitted to subclass <see cref="PSNoteProperty"/>
|
|
/// but there is no established scenario for doing this, nor has it been tested.
|
|
/// </remarks>
|
|
public class PSVariableProperty : PSNoteProperty
|
|
{
|
|
/// <summary>
|
|
/// Returns the string representation of this property.
|
|
/// </summary>
|
|
/// <returns>This property as a string.</returns>
|
|
public override string ToString()
|
|
{
|
|
StringBuilder returnValue = new StringBuilder();
|
|
returnValue.Append(GetDisplayTypeNameOfValue(_variable.Value));
|
|
returnValue.Append(" ");
|
|
returnValue.Append(_variable.Name);
|
|
returnValue.Append("=");
|
|
returnValue.Append(_variable.Value ?? "null");
|
|
return returnValue.ToString();
|
|
}
|
|
|
|
internal PSVariable _variable;
|
|
|
|
/// <summary>
|
|
/// Initializes a new instance of the PSVariableProperty class. This is
|
|
/// a subclass of the NoteProperty that wraps a variable instead of a simple value.
|
|
/// </summary>
|
|
/// <param name="variable">The variable to wrap.</param>
|
|
/// <exception cref="ArgumentException">For an empty or null name.</exception>
|
|
public PSVariableProperty(PSVariable variable)
|
|
: base(variable?.Name, null)
|
|
{
|
|
_variable = variable ?? throw PSTraceSource.NewArgumentException("variable");
|
|
}
|
|
|
|
#region virtual implementation
|
|
|
|
/// <summary>
|
|
/// Returns a new PSMemberInfo that is a copy of this PSMemberInfo,
|
|
/// Note that it returns another reference to the variable, not a reference
|
|
/// to a new variable...
|
|
/// </summary>
|
|
/// <returns>A new PSMemberInfo that is a copy of this PSMemberInfo.</returns>
|
|
public override PSMemberInfo Copy()
|
|
{
|
|
PSNoteProperty property = new PSVariableProperty(_variable);
|
|
CloneBaseProperties(property);
|
|
return property;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets PSMemberTypes.NoteProperty.
|
|
/// </summary>
|
|
public override PSMemberTypes MemberType => PSMemberTypes.NoteProperty;
|
|
|
|
/// <summary>
|
|
/// True if the underlying variable is settable...
|
|
/// </summary>
|
|
public override bool IsSettable => (_variable.Options & (ScopedItemOptions.Constant | ScopedItemOptions.ReadOnly)) == ScopedItemOptions.None;
|
|
|
|
/// <summary>
|
|
/// Gets true since the value of an PSNoteProperty can always be obtained.
|
|
/// </summary>
|
|
public override bool IsGettable => true;
|
|
|
|
/// <summary>
|
|
/// Gets or sets the value of this property.
|
|
/// </summary>
|
|
public override object Value
|
|
{
|
|
get => _variable.Value;
|
|
set
|
|
{
|
|
if (!this.IsInstance)
|
|
{
|
|
throw new SetValueException("ChangeValueOfStaticNote",
|
|
null,
|
|
ExtendedTypeSystem.ChangeStaticMember,
|
|
this.Name);
|
|
}
|
|
|
|
_variable.Value = value;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets the type of the value for this member.
|
|
/// </summary>
|
|
public override string TypeNameOfValue
|
|
{
|
|
get
|
|
{
|
|
object val = _variable.Value;
|
|
|
|
if (val == null)
|
|
{
|
|
return typeof(object).FullName;
|
|
}
|
|
|
|
if (val is PSObject valAsPSObject)
|
|
{
|
|
var typeNames = valAsPSObject.InternalTypeNames;
|
|
if ((typeNames != null) && (typeNames.Count >= 1))
|
|
{
|
|
// type name at 0-th index is the most specific type (i.e. system.string)
|
|
// type names at other indices are less specific (i.e. system.object)
|
|
return typeNames[0];
|
|
}
|
|
}
|
|
|
|
return val.GetType().FullName;
|
|
}
|
|
}
|
|
|
|
#endregion virtual implementation
|
|
}
|
|
|
|
/// <summary>
|
|
/// Serves as a property implemented with getter and setter scripts.
|
|
/// </summary>
|
|
/// <remarks>
|
|
/// It is permitted to subclass <see cref="PSScriptProperty"/>
|
|
/// but there is no established scenario for doing this, nor has it been tested.
|
|
/// </remarks>
|
|
public class PSScriptProperty : PSPropertyInfo
|
|
{
|
|
/// <summary>
|
|
/// Returns the string representation of this property.
|
|
/// </summary>
|
|
/// <returns>This property as a string.</returns>
|
|
public override string ToString()
|
|
{
|
|
StringBuilder returnValue = new StringBuilder();
|
|
returnValue.Append(this.TypeNameOfValue);
|
|
returnValue.Append(" ");
|
|
returnValue.Append(this.Name);
|
|
returnValue.Append(" {");
|
|
if (this.IsGettable)
|
|
{
|
|
returnValue.Append("get=");
|
|
returnValue.Append(this.GetterScript.ToString());
|
|
returnValue.Append(";");
|
|
}
|
|
|
|
if (this.IsSettable)
|
|
{
|
|
returnValue.Append("set=");
|
|
returnValue.Append(this.SetterScript.ToString());
|
|
returnValue.Append(";");
|
|
}
|
|
|
|
returnValue.Append("}");
|
|
return returnValue.ToString();
|
|
}
|
|
|
|
private readonly PSLanguageMode? _languageMode;
|
|
private readonly string _getterScriptText;
|
|
private ScriptBlock _getterScript;
|
|
|
|
private readonly string _setterScriptText;
|
|
private ScriptBlock _setterScript;
|
|
private bool _shouldCloneOnAccess;
|
|
|
|
/// <summary>
|
|
/// Gets the script used for the property getter. It might be null.
|
|
/// </summary>
|
|
public ScriptBlock GetterScript
|
|
{
|
|
get
|
|
{
|
|
// If we don't have a script block for the getter, see if we
|
|
// have the text for it (to support delayed script compilation).
|
|
if ((_getterScript == null) && (_getterScriptText != null))
|
|
{
|
|
_getterScript = ScriptBlock.Create(_getterScriptText);
|
|
|
|
if (_languageMode.HasValue)
|
|
{
|
|
_getterScript.LanguageMode = _languageMode;
|
|
}
|
|
|
|
_getterScript.DebuggerStepThrough = true;
|
|
}
|
|
|
|
if (_getterScript == null)
|
|
{
|
|
return null;
|
|
}
|
|
|
|
if (_shouldCloneOnAccess)
|
|
{
|
|
// returning a clone as TypeTable might be shared between multiple
|
|
// runspaces and ScriptBlock is not shareable. We decided to
|
|
// Clone as needed instead of Cloning whenever a shared TypeTable is
|
|
// attached to a Runspace to save on Memory.
|
|
ScriptBlock newGetterScript = _getterScript.Clone();
|
|
newGetterScript.LanguageMode = _getterScript.LanguageMode;
|
|
return newGetterScript;
|
|
}
|
|
else
|
|
{
|
|
return _getterScript;
|
|
}
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets the script used for the property setter. It might be null.
|
|
/// </summary>
|
|
public ScriptBlock SetterScript
|
|
{
|
|
get
|
|
{
|
|
// If we don't have a script block for the setter, see if we
|
|
// have the text for it (to support delayed script compilation).
|
|
if ((_setterScript == null) && (_setterScriptText != null))
|
|
{
|
|
_setterScript = ScriptBlock.Create(_setterScriptText);
|
|
|
|
if (_languageMode.HasValue)
|
|
{
|
|
_setterScript.LanguageMode = _languageMode;
|
|
}
|
|
|
|
_setterScript.DebuggerStepThrough = true;
|
|
}
|
|
|
|
if (_setterScript == null)
|
|
{
|
|
return null;
|
|
}
|
|
|
|
if (_shouldCloneOnAccess)
|
|
{
|
|
// returning a clone as TypeTable might be shared between multiple
|
|
// runspaces and ScriptBlock is not shareable. We decided to
|
|
// Clone as needed instead of Cloning whenever a shared TypeTable is
|
|
// attached to a Runspace to save on Memory.
|
|
ScriptBlock newSetterScript = _setterScript.Clone();
|
|
newSetterScript.LanguageMode = _setterScript.LanguageMode;
|
|
return newSetterScript;
|
|
}
|
|
else
|
|
{
|
|
return _setterScript;
|
|
}
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Initializes an instance of the PSScriptProperty class as a read only property.
|
|
/// </summary>
|
|
/// <param name="name">Name of the property.</param>
|
|
/// <param name="getterScript">Script to be used for the property getter. $this will be this PSObject.</param>
|
|
/// <exception cref="ArgumentException">For invalid arguments.</exception>
|
|
public PSScriptProperty(string name, ScriptBlock getterScript)
|
|
{
|
|
if (string.IsNullOrEmpty(name))
|
|
{
|
|
throw PSTraceSource.NewArgumentException("name");
|
|
}
|
|
|
|
this.name = name;
|
|
|
|
_getterScript = getterScript ?? throw PSTraceSource.NewArgumentNullException("getterScript");
|
|
}
|
|
|
|
/// <summary>
|
|
/// Initializes an instance of the PSScriptProperty class as a read only
|
|
/// property. getterScript or setterScript can be null, but not both.
|
|
/// </summary>
|
|
/// <param name="name">Name of this property.</param>
|
|
/// <param name="getterScript">Script to be used for the property getter. $this will be this PSObject.</param>
|
|
/// <param name="setterScript">Script to be used for the property setter. $this will be this PSObject and $args(1) will be the value to set.</param>
|
|
/// <exception cref="ArgumentException">For invalid arguments.</exception>
|
|
public PSScriptProperty(string name, ScriptBlock getterScript, ScriptBlock setterScript)
|
|
{
|
|
if (string.IsNullOrEmpty(name))
|
|
{
|
|
throw PSTraceSource.NewArgumentException("name");
|
|
}
|
|
|
|
this.name = name;
|
|
if (getterScript == null && setterScript == null)
|
|
{
|
|
// we only do not allow both getterScript and setterScript to be null
|
|
throw PSTraceSource.NewArgumentException("getterScript setterScript");
|
|
}
|
|
|
|
if (getterScript != null)
|
|
{
|
|
getterScript.DebuggerStepThrough = true;
|
|
}
|
|
|
|
if (setterScript != null)
|
|
{
|
|
setterScript.DebuggerStepThrough = true;
|
|
}
|
|
|
|
_getterScript = getterScript;
|
|
_setterScript = setterScript;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Initializes an instance of the PSScriptProperty class as a read only
|
|
/// property, using the text of the properties to support lazy initialization.
|
|
/// </summary>
|
|
/// <param name="name">Name of this property.</param>
|
|
/// <param name="getterScript">Script to be used for the property getter. $this will be this PSObject.</param>
|
|
/// <param name="setterScript">Script to be used for the property setter. $this will be this PSObject and $args(1) will be the value to set.</param>
|
|
/// <param name="languageMode">Language mode to be used during script block evaluation.</param>
|
|
/// <exception cref="ArgumentException">For invalid arguments.</exception>
|
|
internal PSScriptProperty(string name, string getterScript, string setterScript, PSLanguageMode? languageMode)
|
|
{
|
|
if (string.IsNullOrEmpty(name))
|
|
{
|
|
throw PSTraceSource.NewArgumentException("name");
|
|
}
|
|
|
|
this.name = name;
|
|
if (getterScript == null && setterScript == null)
|
|
{
|
|
// we only do not allow both getterScript and setterScript to be null
|
|
throw PSTraceSource.NewArgumentException("getterScript setterScript");
|
|
}
|
|
|
|
_getterScriptText = getterScript;
|
|
_setterScriptText = setterScript;
|
|
_languageMode = languageMode;
|
|
}
|
|
|
|
internal PSScriptProperty(string name, ScriptBlock getterScript, ScriptBlock setterScript, bool shouldCloneOnAccess)
|
|
: this(name, getterScript, setterScript)
|
|
{
|
|
_shouldCloneOnAccess = shouldCloneOnAccess;
|
|
}
|
|
|
|
internal PSScriptProperty(string name, string getterScript, string setterScript, PSLanguageMode? languageMode, bool shouldCloneOnAccess)
|
|
: this(name, getterScript, setterScript, languageMode)
|
|
{
|
|
_shouldCloneOnAccess = shouldCloneOnAccess;
|
|
}
|
|
|
|
#region virtual implementation
|
|
|
|
/// <summary>
|
|
/// Returns a new PSMemberInfo that is a copy of this PSMemberInfo.
|
|
/// </summary>
|
|
/// <returns>A new PSMemberInfo that is a copy of this PSMemberInfo.</returns>
|
|
public override PSMemberInfo Copy()
|
|
{
|
|
var property = new PSScriptProperty(name, this.GetterScript, this.SetterScript) {_shouldCloneOnAccess = _shouldCloneOnAccess};
|
|
CloneBaseProperties(property);
|
|
return property;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets the member type.
|
|
/// </summary>
|
|
public override PSMemberTypes MemberType => PSMemberTypes.ScriptProperty;
|
|
|
|
/// <summary>
|
|
/// Gets true if this property can be set.
|
|
/// </summary>
|
|
public override bool IsSettable => this._setterScript != null || this._setterScriptText != null;
|
|
|
|
/// <summary>
|
|
/// Gets true if this property can be read.
|
|
/// </summary>
|
|
public override bool IsGettable => this._getterScript != null || this._getterScriptText != null;
|
|
|
|
/// <summary>
|
|
/// Gets and Sets the value of this property.
|
|
/// </summary>
|
|
/// <exception cref="GetValueException">When getting and there is no getter,
|
|
/// when the getter throws an exception or when there is no Runspace to run the script.
|
|
/// </exception>
|
|
/// <exception cref="SetValueException">When setting and there is no setter,
|
|
/// when the setter throws an exception or when there is no Runspace to run the script.</exception>
|
|
public override object Value
|
|
{
|
|
get
|
|
{
|
|
if (this.GetterScript == null)
|
|
{
|
|
throw new GetValueException("GetWithoutGetterFromScriptPropertyValue",
|
|
null,
|
|
ExtendedTypeSystem.GetWithoutGetterException,
|
|
this.Name);
|
|
}
|
|
|
|
return InvokeGetter(this.instance);
|
|
}
|
|
|
|
set
|
|
{
|
|
if (this.SetterScript == null)
|
|
{
|
|
throw new SetValueException("SetWithoutSetterFromScriptProperty",
|
|
null,
|
|
ExtendedTypeSystem.SetWithoutSetterException,
|
|
this.Name);
|
|
}
|
|
|
|
InvokeSetter(this.instance, value);
|
|
}
|
|
}
|
|
|
|
internal object InvokeSetter(object scriptThis, object value)
|
|
{
|
|
try
|
|
{
|
|
SetterScript.DoInvokeReturnAsIs(
|
|
useLocalScope: true,
|
|
errorHandlingBehavior: ScriptBlock.ErrorHandlingBehavior.WriteToExternalErrorPipe,
|
|
dollarUnder: AutomationNull.Value,
|
|
input: AutomationNull.Value,
|
|
scriptThis: scriptThis,
|
|
args: new[] {value});
|
|
return value;
|
|
}
|
|
catch (RuntimeException e)
|
|
{
|
|
throw NewSetValueException(e, "ScriptSetValueRuntimeException");
|
|
}
|
|
catch (TerminateException)
|
|
{
|
|
// The debugger is terminating the execution; let the exception bubble up
|
|
throw;
|
|
}
|
|
catch (FlowControlException e)
|
|
{
|
|
throw NewSetValueException(e, "ScriptSetValueFlowControlException");
|
|
}
|
|
catch (PSInvalidOperationException e)
|
|
{
|
|
throw NewSetValueException(e, "ScriptSetValueInvalidOperationException");
|
|
}
|
|
}
|
|
|
|
internal object InvokeGetter(object scriptThis)
|
|
{
|
|
try
|
|
{
|
|
return GetterScript.DoInvokeReturnAsIs(
|
|
useLocalScope: true,
|
|
errorHandlingBehavior: ScriptBlock.ErrorHandlingBehavior.SwallowErrors,
|
|
dollarUnder: AutomationNull.Value,
|
|
input: AutomationNull.Value,
|
|
scriptThis: scriptThis,
|
|
args: Utils.EmptyArray<object>());
|
|
}
|
|
catch (RuntimeException e)
|
|
{
|
|
throw NewGetValueException(e, "ScriptGetValueRuntimeException");
|
|
}
|
|
catch (TerminateException)
|
|
{
|
|
// The debugger is terminating the execution; let the exception bubble up
|
|
throw;
|
|
}
|
|
catch (FlowControlException e)
|
|
{
|
|
throw NewGetValueException(e, "ScriptGetValueFlowControlException");
|
|
}
|
|
catch (PSInvalidOperationException e)
|
|
{
|
|
throw NewGetValueException(e, "ScriptgetValueInvalidOperationException");
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets the type of the value for this member. Currently this always returns typeof(object).FullName.
|
|
/// </summary>
|
|
public override string TypeNameOfValue
|
|
{
|
|
get
|
|
{
|
|
if ((this.GetterScript != null) &&
|
|
(this.GetterScript.OutputType.Count > 0))
|
|
{
|
|
return this.GetterScript.OutputType[0].Name;
|
|
}
|
|
else
|
|
{
|
|
return typeof(object).FullName;
|
|
}
|
|
}
|
|
}
|
|
|
|
#endregion virtual implementation
|
|
}
|
|
|
|
internal class PSMethodInvocationConstraints
|
|
{
|
|
internal PSMethodInvocationConstraints(
|
|
Type methodTargetType,
|
|
Type[] parameterTypes)
|
|
{
|
|
this.MethodTargetType = methodTargetType;
|
|
_parameterTypes = parameterTypes;
|
|
}
|
|
|
|
/// <remarks>
|
|
/// If <c>null</c> then there are no constraints
|
|
/// </remarks>
|
|
public Type MethodTargetType { get; }
|
|
|
|
/// <remarks>
|
|
/// If <c>null</c> then there are no constraints
|
|
/// </remarks>
|
|
public IEnumerable<Type> ParameterTypes => _parameterTypes;
|
|
|
|
private readonly Type[] _parameterTypes;
|
|
|
|
internal static bool EqualsForCollection<T>(ICollection<T> xs, ICollection<T> ys)
|
|
{
|
|
if (xs == null)
|
|
{
|
|
return ys == null;
|
|
}
|
|
|
|
if (ys == null)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
if (xs.Count != ys.Count)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
return xs.SequenceEqual(ys);
|
|
}
|
|
|
|
// TODO: IEnumerable<Type> genericTypeParameters { get; private set; }
|
|
|
|
public bool Equals(PSMethodInvocationConstraints other)
|
|
{
|
|
if (ReferenceEquals(null, other))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
if (ReferenceEquals(this, other))
|
|
{
|
|
return true;
|
|
}
|
|
|
|
if (other.MethodTargetType != this.MethodTargetType)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
if (!EqualsForCollection(_parameterTypes, other._parameterTypes))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
public override bool Equals(object obj)
|
|
{
|
|
if (ReferenceEquals(null, obj))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
if (ReferenceEquals(this, obj))
|
|
{
|
|
return true;
|
|
}
|
|
|
|
if (obj.GetType() != typeof(PSMethodInvocationConstraints))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
return Equals((PSMethodInvocationConstraints) obj);
|
|
}
|
|
|
|
public override int GetHashCode()
|
|
{
|
|
// algorithm based on https://stackoverflow.com/questions/263400/what-is-the-best-algorithm-for-an-overridden-system-object-gethashcode
|
|
unchecked
|
|
{
|
|
int result = 61;
|
|
|
|
result = result * 397 + (MethodTargetType != null ? MethodTargetType.GetHashCode() : 0);
|
|
result = result * 397 + ParameterTypes.SequenceGetHashCode();
|
|
|
|
return result;
|
|
}
|
|
}
|
|
|
|
public override string ToString()
|
|
{
|
|
StringBuilder sb = new StringBuilder();
|
|
string separator = string.Empty;
|
|
if (MethodTargetType != null)
|
|
{
|
|
sb.Append("this: ");
|
|
sb.Append(ToStringCodeMethods.Type(MethodTargetType, dropNamespaces: true));
|
|
separator = " ";
|
|
}
|
|
|
|
if (_parameterTypes != null)
|
|
{
|
|
sb.Append(separator);
|
|
sb.Append("args: ");
|
|
separator = string.Empty;
|
|
foreach (var p in _parameterTypes)
|
|
{
|
|
sb.Append(separator);
|
|
sb.Append(ToStringCodeMethods.Type(p, dropNamespaces: true));
|
|
separator = ", ";
|
|
}
|
|
}
|
|
|
|
if (sb.Length == 0)
|
|
{
|
|
sb.Append("<empty>");
|
|
}
|
|
|
|
return sb.ToString();
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Serves as a base class for all members that behave like methods.
|
|
/// </summary>
|
|
public abstract class PSMethodInfo : PSMemberInfo
|
|
{
|
|
/// <summary>
|
|
/// Initializes a new instance of a class derived from PSMethodInfo.
|
|
/// </summary>
|
|
protected PSMethodInfo()
|
|
{
|
|
}
|
|
|
|
/// <summary>
|
|
/// Invokes the appropriate method overload for the given arguments and returns its result.
|
|
/// </summary>
|
|
/// <param name="arguments">Arguments to the method.</param>
|
|
/// <returns>Return value from the method.</returns>
|
|
/// <exception cref="ArgumentException">If arguments is null.</exception>
|
|
/// <exception cref="MethodException">For problems finding an appropriate method for the arguments.</exception>
|
|
/// <exception cref="MethodInvocationException">For exceptions invoking the method.
|
|
/// This exception is also thrown for an <see cref="PSScriptMethod"/> when there is no Runspace to run the script.</exception>
|
|
public abstract object Invoke(params object[] arguments);
|
|
|
|
/// <summary>
|
|
/// Gets a list of all the overloads for this method.
|
|
/// </summary>
|
|
public abstract Collection<string> OverloadDefinitions { get; }
|
|
|
|
#region virtual implementation
|
|
|
|
/// <summary>
|
|
/// Gets the value of this member. The getter returns the PSMethodInfo itself.
|
|
/// </summary>
|
|
/// <exception cref="ExtendedTypeSystemException">When setting the member.</exception>
|
|
/// <remarks>
|
|
/// This is not the returned value of the method even for Methods with no arguments.
|
|
/// The getter returns this (the PSMethodInfo itself). The setter is not supported.
|
|
/// </remarks>
|
|
public sealed override object Value
|
|
{
|
|
get => this;
|
|
set => throw new ExtendedTypeSystemException("CannotChangePSMethodInfoValue",
|
|
null,
|
|
ExtendedTypeSystem.CannotSetValueForMemberType,
|
|
this.GetType().FullName);
|
|
}
|
|
|
|
#endregion virtual implementation
|
|
}
|
|
|
|
/// <summary>
|
|
/// Serves as a method implemented with a reference to another method.
|
|
/// </summary>
|
|
/// <remarks>
|
|
/// It is permitted to subclass <see cref="PSCodeMethod"/>
|
|
/// but there is no established scenario for doing this, nor has it been tested.
|
|
/// </remarks>
|
|
public class PSCodeMethod : PSMethodInfo
|
|
{
|
|
/// <summary>
|
|
/// Returns the string representation of this member.
|
|
/// </summary>
|
|
/// <returns>This property as a string.</returns>
|
|
public override string ToString()
|
|
{
|
|
StringBuilder returnValue = new StringBuilder();
|
|
foreach (string overload in OverloadDefinitions)
|
|
{
|
|
returnValue.Append(overload);
|
|
returnValue.Append(", ");
|
|
}
|
|
|
|
returnValue.Remove(returnValue.Length - 2, 2);
|
|
return returnValue.ToString();
|
|
}
|
|
|
|
private MethodInformation[] _codeReferenceMethodInformation;
|
|
|
|
internal static bool CheckMethodInfo(MethodInfo method)
|
|
{
|
|
ParameterInfo[] parameters = method.GetParameters();
|
|
return method.IsStatic
|
|
&& method.IsPublic
|
|
&& parameters.Length != 0
|
|
&& parameters[0].ParameterType == typeof(PSObject);
|
|
}
|
|
|
|
internal void SetCodeReference(Type type, string methodName)
|
|
{
|
|
MethodInfo methodAsMember = null;
|
|
|
|
try
|
|
{
|
|
methodAsMember = type.GetMethod(methodName, BindingFlags.Static | BindingFlags.Public | BindingFlags.IgnoreCase);
|
|
}
|
|
catch (AmbiguousMatchException)
|
|
{
|
|
// Ignore the AmbiguousMatchException.
|
|
// We will generate error below if we cannot find exactly one match method.
|
|
}
|
|
|
|
if (methodAsMember == null)
|
|
{
|
|
throw new ExtendedTypeSystemException("WrongMethodFormatFromTypeTable", null,
|
|
ExtendedTypeSystem.CodeMethodMethodFormat);
|
|
}
|
|
|
|
CodeReference = methodAsMember;
|
|
if (!CheckMethodInfo(CodeReference))
|
|
{
|
|
throw new ExtendedTypeSystemException("WrongMethodFormat", null, ExtendedTypeSystem.CodeMethodMethodFormat);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Used from TypeTable.
|
|
/// </summary>
|
|
internal PSCodeMethod(string name)
|
|
{
|
|
if (string.IsNullOrEmpty(name))
|
|
{
|
|
throw PSTraceSource.NewArgumentException("name");
|
|
}
|
|
|
|
this.name = name;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Initializes a new instance of the PSCodeMethod class.
|
|
/// </summary>
|
|
/// <param name="name">Name of the property.</param>
|
|
/// <param name="codeReference">This should be a public static method where the first parameter is an PSObject.</param>
|
|
/// <exception cref="ArgumentException">For invalid arguments.</exception>
|
|
/// <exception cref="ExtendedTypeSystemException">If the codeReference does not have the right format.</exception>
|
|
public PSCodeMethod(string name, MethodInfo codeReference)
|
|
{
|
|
if (string.IsNullOrEmpty(name))
|
|
{
|
|
throw PSTraceSource.NewArgumentException("name");
|
|
}
|
|
|
|
if (codeReference == null)
|
|
{
|
|
throw PSTraceSource.NewArgumentNullException("codeReference");
|
|
}
|
|
|
|
if (!CheckMethodInfo(codeReference))
|
|
{
|
|
throw new ExtendedTypeSystemException("WrongMethodFormat", null, ExtendedTypeSystem.CodeMethodMethodFormat);
|
|
}
|
|
|
|
this.name = name;
|
|
CodeReference = codeReference;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets the method referenced by this PSCodeMethod.
|
|
/// </summary>
|
|
public MethodInfo CodeReference { get; private set; }
|
|
|
|
#region virtual implementation
|
|
|
|
/// <summary>
|
|
/// Returns a new PSMemberInfo that is a copy of this PSMemberInfo.
|
|
/// </summary>
|
|
/// <returns>A new PSMemberInfo that is a copy of this PSMemberInfo.</returns>
|
|
public override PSMemberInfo Copy()
|
|
{
|
|
PSCodeMethod member = new PSCodeMethod(name, CodeReference);
|
|
CloneBaseProperties(member);
|
|
return member;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets the member type.
|
|
/// </summary>
|
|
public override PSMemberTypes MemberType => PSMemberTypes.CodeMethod;
|
|
|
|
/// <summary>
|
|
/// Invokes CodeReference method and returns its results.
|
|
/// </summary>
|
|
/// <param name="arguments">Arguments to the method.</param>
|
|
/// <returns>Return value from the method.</returns>
|
|
/// <exception cref="ArgumentException">If arguments is null.</exception>
|
|
/// <exception cref="MethodException">
|
|
/// When
|
|
/// could CodeReference cannot match the given argument count or
|
|
/// could not convert an argument to the type required
|
|
/// </exception>
|
|
/// <exception cref="MethodInvocationException">For exceptions invoking the CodeReference.</exception>
|
|
public override object Invoke(params object[] arguments)
|
|
{
|
|
if (arguments == null)
|
|
{
|
|
throw PSTraceSource.NewArgumentNullException("arguments");
|
|
}
|
|
|
|
object[] newArguments = new object[arguments.Length + 1];
|
|
newArguments[0] = this.instance;
|
|
for (int i = 0; i < arguments.Length; i++)
|
|
{
|
|
newArguments[i + 1] = arguments[i];
|
|
}
|
|
|
|
if (_codeReferenceMethodInformation == null)
|
|
{
|
|
_codeReferenceMethodInformation = DotNetAdapter.GetMethodInformationArray(new[] {CodeReference});
|
|
}
|
|
|
|
Adapter.GetBestMethodAndArguments(CodeReference.Name, _codeReferenceMethodInformation, newArguments, out object[] convertedArguments);
|
|
|
|
return DotNetAdapter.AuxiliaryMethodInvoke(null, convertedArguments, _codeReferenceMethodInformation[0], newArguments);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets the definition for CodeReference.
|
|
/// </summary>
|
|
public override Collection<string> OverloadDefinitions => new Collection<string>
|
|
{
|
|
DotNetAdapter.GetMethodInfoOverloadDefinition(null, CodeReference, 0)
|
|
};
|
|
|
|
/// <summary>
|
|
/// Gets the type of the value for this member. Currently this always returns typeof(PSCodeMethod).FullName.
|
|
/// </summary>
|
|
public override string TypeNameOfValue => typeof(PSCodeMethod).FullName;
|
|
|
|
#endregion virtual implementation
|
|
}
|
|
|
|
/// <summary>
|
|
/// Serves as a method implemented with a script.
|
|
/// </summary>
|
|
/// <remarks>
|
|
/// It is permitted to subclass <see cref="PSScriptMethod"/>
|
|
/// but there is no established scenario for doing this, nor has it been tested.
|
|
/// </remarks>
|
|
public class PSScriptMethod : PSMethodInfo
|
|
{
|
|
/// <summary>
|
|
/// Returns the string representation of this member.
|
|
/// </summary>
|
|
/// <returns>This property as a string.</returns>
|
|
public override string ToString()
|
|
{
|
|
StringBuilder returnValue = new StringBuilder();
|
|
returnValue.Append(this.TypeNameOfValue);
|
|
returnValue.Append(" ");
|
|
returnValue.Append(this.Name);
|
|
returnValue.Append("();");
|
|
return returnValue.ToString();
|
|
}
|
|
|
|
private readonly ScriptBlock _script;
|
|
private bool _shouldCloneOnAccess;
|
|
|
|
/// <summary>
|
|
/// Gets the script implementing this PSScriptMethod.
|
|
/// </summary>
|
|
public ScriptBlock Script
|
|
{
|
|
get
|
|
{
|
|
if (_shouldCloneOnAccess)
|
|
{
|
|
// returning a clone as TypeTable might be shared between multiple
|
|
// runspaces and ScriptBlock is not shareable. We decided to
|
|
// Clone as needed instead of Cloning whenever a shared TypeTable is
|
|
// attached to a Runspace to save on Memory.
|
|
ScriptBlock newScript = _script.Clone();
|
|
newScript.LanguageMode = _script.LanguageMode;
|
|
|
|
return newScript;
|
|
}
|
|
else
|
|
{
|
|
return _script;
|
|
}
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Initializes a new instance of PSScriptMethod.
|
|
/// </summary>
|
|
/// <param name="name">Name of the method.</param>
|
|
/// <param name="script">Script to be used when calling the method.</param>
|
|
/// <exception cref="ArgumentException">For invalid arguments.</exception>
|
|
public PSScriptMethod(string name, ScriptBlock script)
|
|
{
|
|
if (string.IsNullOrEmpty(name))
|
|
{
|
|
throw PSTraceSource.NewArgumentException("name");
|
|
}
|
|
|
|
this.name = name;
|
|
|
|
_script = script ?? throw PSTraceSource.NewArgumentNullException("script");
|
|
}
|
|
|
|
/// <summary>
|
|
/// </summary>
|
|
/// <param name="name"></param>
|
|
/// <param name="script"></param>
|
|
/// <param name="shouldCloneOnAccess">
|
|
/// Used by TypeTable.
|
|
/// TypeTable might be shared between multiple runspaces and
|
|
/// ScriptBlock is not shareable. We decided to Clone as needed
|
|
/// instead of Cloning whenever a shared TypeTable is attached
|
|
/// to a Runspace to save on Memory.
|
|
/// </param>
|
|
internal PSScriptMethod(string name, ScriptBlock script, bool shouldCloneOnAccess)
|
|
: this(name, script)
|
|
{
|
|
_shouldCloneOnAccess = shouldCloneOnAccess;
|
|
}
|
|
|
|
#region virtual implementation
|
|
|
|
/// <summary>
|
|
/// Invokes Script method and returns its results.
|
|
/// </summary>
|
|
/// <param name="arguments">Arguments to the method.</param>
|
|
/// <returns>Return value from the method.</returns>
|
|
/// <exception cref="ArgumentException">If arguments is null.</exception>
|
|
/// <exception cref="MethodInvocationException">For exceptions invoking the Script or if there is no Runspace to run the script.</exception>
|
|
public override object Invoke(params object[] arguments)
|
|
{
|
|
if (arguments == null)
|
|
{
|
|
throw PSTraceSource.NewArgumentNullException("arguments");
|
|
}
|
|
|
|
return InvokeScript(Name, _script, this.instance, arguments);
|
|
}
|
|
|
|
internal static object InvokeScript(string methodName, ScriptBlock script, object @this, object[] arguments)
|
|
{
|
|
try
|
|
{
|
|
return script.DoInvokeReturnAsIs(
|
|
useLocalScope: true,
|
|
errorHandlingBehavior: ScriptBlock.ErrorHandlingBehavior.WriteToExternalErrorPipe,
|
|
dollarUnder: AutomationNull.Value,
|
|
input: AutomationNull.Value,
|
|
scriptThis: @this,
|
|
args: arguments);
|
|
}
|
|
catch (RuntimeException e)
|
|
{
|
|
throw new MethodInvocationException(
|
|
"ScriptMethodRuntimeException",
|
|
e,
|
|
ExtendedTypeSystem.MethodInvocationException,
|
|
methodName, arguments.Length, e.Message);
|
|
}
|
|
catch (TerminateException)
|
|
{
|
|
// The debugger is terminating the execution; let the exception bubble up
|
|
throw;
|
|
}
|
|
catch (FlowControlException e)
|
|
{
|
|
throw new MethodInvocationException(
|
|
"ScriptMethodFlowControlException",
|
|
e,
|
|
ExtendedTypeSystem.MethodInvocationException,
|
|
methodName, arguments.Length, e.Message);
|
|
}
|
|
catch (PSInvalidOperationException e)
|
|
{
|
|
throw new MethodInvocationException(
|
|
"ScriptMethodInvalidOperationException",
|
|
e,
|
|
ExtendedTypeSystem.MethodInvocationException,
|
|
methodName, arguments.Length, e.Message);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets a list of all the overloads for this method.
|
|
/// </summary>
|
|
public override Collection<string> OverloadDefinitions
|
|
{
|
|
get
|
|
{
|
|
Collection<string> retValue = new Collection<string> {this.ToString()};
|
|
return retValue;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Returns a new PSMemberInfo that is a copy of this PSMemberInfo.
|
|
/// </summary>
|
|
/// <returns>A new PSMemberInfo that is a copy of this PSMemberInfo.</returns>
|
|
public override PSMemberInfo Copy()
|
|
{
|
|
var method = new PSScriptMethod(this.name, _script) {_shouldCloneOnAccess = _shouldCloneOnAccess};
|
|
CloneBaseProperties(method);
|
|
return method;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets the member type.
|
|
/// </summary>
|
|
public override PSMemberTypes MemberType => PSMemberTypes.ScriptMethod;
|
|
|
|
/// <summary>
|
|
/// Gets the type of the value for this member. Currently this always returns typeof(object).FullName.
|
|
/// </summary>
|
|
public override string TypeNameOfValue => typeof(object).FullName;
|
|
|
|
#endregion virtual implementation
|
|
}
|
|
|
|
/// <summary>
|
|
/// Used to access the adapted or base methods from the BaseObject.
|
|
/// </summary>
|
|
/// <remarks>
|
|
/// It is permitted to subclass <see cref="PSMethod"/>
|
|
/// but there is no established scenario for doing this, nor has it been tested.
|
|
/// </remarks>
|
|
public class PSMethod : PSMethodInfo
|
|
{
|
|
internal override void ReplicateInstance(object particularInstance)
|
|
{
|
|
base.ReplicateInstance(particularInstance);
|
|
baseObject = particularInstance;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Returns the string representation of this member.
|
|
/// </summary>
|
|
/// <returns>This property as a string.</returns>
|
|
public override string ToString()
|
|
{
|
|
return _adapter.BaseMethodToString(this);
|
|
}
|
|
|
|
internal object adapterData;
|
|
internal Adapter _adapter;
|
|
internal object baseObject;
|
|
|
|
/// <summary>
|
|
/// Constructs this method.
|
|
/// </summary>
|
|
/// <param name="name">Name.</param>
|
|
/// <param name="adapter">Adapter to be used invoking.</param>
|
|
/// <param name="baseObject">BaseObject for the methods.</param>
|
|
/// <param name="adapterData">AdapterData from adapter.GetMethodData.</param>
|
|
/// <exception cref="ArgumentException">For invalid arguments.</exception>
|
|
internal PSMethod(string name, Adapter adapter, object baseObject, object adapterData)
|
|
{
|
|
if (string.IsNullOrEmpty(name))
|
|
{
|
|
throw PSTraceSource.NewArgumentException("name");
|
|
}
|
|
|
|
this.name = name;
|
|
this.adapterData = adapterData;
|
|
this._adapter = adapter;
|
|
this.baseObject = baseObject;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Constructs a PSMethod.
|
|
/// </summary>
|
|
/// <param name="name">Name.</param>
|
|
/// <param name="adapter">Adapter to be used invoking.</param>
|
|
/// <param name="baseObject">BaseObject for the methods.</param>
|
|
/// <param name="adapterData">AdapterData from adapter.GetMethodData.</param>
|
|
/// <param name="isSpecial">True if this member is a special member, false otherwise.</param>
|
|
/// <param name="isHidden">True if this member is hidden, false otherwise.</param>
|
|
/// <exception cref="ArgumentException">For invalid arguments.</exception>
|
|
internal PSMethod(string name, Adapter adapter, object baseObject, object adapterData, bool isSpecial, bool isHidden)
|
|
: this(name, adapter, baseObject, adapterData)
|
|
{
|
|
this.IsSpecial = isSpecial;
|
|
this.IsHidden = isHidden;
|
|
}
|
|
|
|
#region virtual implementation
|
|
|
|
/// <summary>
|
|
/// Returns a new PSMemberInfo that is a copy of this PSMemberInfo.
|
|
/// </summary>
|
|
/// <returns>A new PSMemberInfo that is a copy of this PSMemberInfo.</returns>
|
|
public override PSMemberInfo Copy()
|
|
{
|
|
PSMethod member = new PSMethod(this.name, _adapter, this.baseObject, this.adapterData, this.IsSpecial, this.IsHidden);
|
|
CloneBaseProperties(member);
|
|
return member;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets the member type.
|
|
/// </summary>
|
|
public override PSMemberTypes MemberType => PSMemberTypes.Method;
|
|
|
|
/// <summary>
|
|
/// Invokes the appropriate method overload for the given arguments and returns its result.
|
|
/// </summary>
|
|
/// <param name="arguments">Arguments to the method.</param>
|
|
/// <returns>Return value from the method.</returns>
|
|
/// <exception cref="ArgumentException">If arguments is null.</exception>
|
|
/// <exception cref="MethodException">For problems finding an appropriate method for the arguments.</exception>
|
|
/// <exception cref="MethodInvocationException">For exceptions invoking the method.</exception>
|
|
public override object Invoke(params object[] arguments)
|
|
{
|
|
return this.Invoke(null, arguments);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Invokes the appropriate method overload for the given arguments and returns its result.
|
|
/// </summary>
|
|
/// <param name="invocationConstraints">Constraints.</param>
|
|
/// <param name="arguments">Arguments to the method.</param>
|
|
/// <returns>Return value from the method.</returns>
|
|
/// <exception cref="ArgumentException">If arguments is null.</exception>
|
|
/// <exception cref="MethodException">For problems finding an appropriate method for the arguments.</exception>
|
|
/// <exception cref="MethodInvocationException">For exceptions invoking the method.</exception>
|
|
internal object Invoke(PSMethodInvocationConstraints invocationConstraints, params object[] arguments)
|
|
{
|
|
if (arguments == null)
|
|
{
|
|
throw PSTraceSource.NewArgumentNullException("arguments");
|
|
}
|
|
|
|
return _adapter.BaseMethodInvoke(this, invocationConstraints, arguments);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets a list of all the overloads for this method.
|
|
/// </summary>
|
|
public override Collection<string> OverloadDefinitions => _adapter.BaseMethodDefinitions(this);
|
|
|
|
/// <summary>
|
|
/// Gets the type of the value for this member. This always returns typeof(PSMethod).FullName.
|
|
/// </summary>
|
|
public override string TypeNameOfValue => typeof(PSMethod).FullName;
|
|
|
|
#endregion virtual implementation
|
|
|
|
/// <summary>
|
|
/// True if the method is a special method like GET/SET property accessor methods.
|
|
/// </summary>
|
|
internal bool IsSpecial { get; }
|
|
|
|
internal static PSMethod Create(string name, DotNetAdapter dotNetInstanceAdapter, object baseObject, DotNetAdapter.MethodCacheEntry method)
|
|
{
|
|
return Create(name, dotNetInstanceAdapter, baseObject, method, false, false);
|
|
}
|
|
|
|
internal static PSMethod Create(string name, DotNetAdapter dotNetInstanceAdapter, object baseObject, DotNetAdapter.MethodCacheEntry method, bool isSpecial, bool isHidden)
|
|
{
|
|
if (method[0].method is ConstructorInfo)
|
|
{
|
|
// Constructor cannot be converted to a delegate, so just return a simple PSMethod instance
|
|
return new PSMethod(name, dotNetInstanceAdapter, baseObject, method, isSpecial, isHidden);
|
|
}
|
|
|
|
if (method.PSMethodCtor == null)
|
|
{
|
|
method.PSMethodCtor = CreatePSMethodConstructor(method.methodInformationStructures);
|
|
}
|
|
|
|
return method.PSMethodCtor.Invoke(name, dotNetInstanceAdapter, baseObject, method, isSpecial, isHidden);
|
|
}
|
|
|
|
private static Type GetMethodGroupType(MethodInfo methodInfo)
|
|
{
|
|
if (methodInfo.DeclaringType.IsGenericTypeDefinition)
|
|
{
|
|
// If the method is from a generic type definition, consider it not convertible.
|
|
return typeof(Func<PSNonBindableType>);
|
|
}
|
|
|
|
if (methodInfo.IsGenericMethodDefinition)
|
|
{
|
|
// For a generic method, it's possible to infer the generic parameters based on the target delegate.
|
|
// However, we don't yet handle generic methods in PSMethod-to-Delegate conversion, so for now, we
|
|
// don't produce the metadata type that represents the signature of a generic method.
|
|
//
|
|
// Say one day we want to support generic method in PSMethod-to-Delegate conversion and need to produce
|
|
// the metadata type, we should use the generic parameter types from the MethodInfo directly to construct
|
|
// the Func<> metadata type. See the concept shown in the following scripts:
|
|
// $class = "public class Zoo { public static T GetName<T>(int index, T input) { return default(T); } }"
|
|
// Add-Type -TypeDefinition $class
|
|
// $method = [Zoo].GetMethod("GetName")
|
|
// $allTypes = $method.GetParameters().ParameterType + $method.ReturnType
|
|
// $metadataType = [Func`3].MakeGenericType($allTypes)
|
|
// In this way, '$metadataType.ContainsGenericParameters' returns 'True', indicating it represents a generic method.
|
|
// And also, given a generic argument type from `$metadataType.GetGenericArguments()`, it's easy to tell if it's a
|
|
// generic parameter (for example, 'T') based on the property 'IsGenericParameter'.
|
|
// Moreover, it's also easy to get constraints of the generic parameter, via 'GetGenericParameterConstraints()'
|
|
// and 'GenericParameterAttributes'.
|
|
return typeof(Func<PSNonBindableType>);
|
|
}
|
|
|
|
var parameterInfos = methodInfo.GetParameters();
|
|
if (parameterInfos.Length > 16)
|
|
{
|
|
// Too many parameters, an unlikely scenario.
|
|
return typeof(Func<PSNonBindableType>);
|
|
}
|
|
|
|
try
|
|
{
|
|
var methodTypes = new Type[parameterInfos.Length + 1];
|
|
for (int i = 0; i < parameterInfos.Length; i++)
|
|
{
|
|
var parameterInfo = parameterInfos[i];
|
|
Type parameterType = parameterInfo.ParameterType;
|
|
methodTypes[i] = GetPSMethodProjectedType(parameterType, parameterInfo.IsOut);
|
|
}
|
|
|
|
methodTypes[parameterInfos.Length] = GetPSMethodProjectedType(methodInfo.ReturnType);
|
|
|
|
return DelegateHelpers.MakeDelegate(methodTypes);
|
|
}
|
|
catch (TypeLoadException)
|
|
{
|
|
return typeof(Func<PSNonBindableType>);
|
|
}
|
|
}
|
|
|
|
private static Type GetPSMethodProjectedType(Type type, bool isOut = false)
|
|
{
|
|
if (type == typeof(void))
|
|
{
|
|
return typeof(VOID);
|
|
}
|
|
|
|
if (type == typeof(TypedReference))
|
|
{
|
|
return typeof(PSTypedReference);
|
|
}
|
|
|
|
if (type.IsByRef)
|
|
{
|
|
var elementType = GetPSMethodProjectedType(type.GetElementType());
|
|
type = isOut ? typeof(PSOutParameter<>).MakeGenericType(elementType)
|
|
: typeof(PSReference<>).MakeGenericType(elementType);
|
|
}
|
|
else if (type.IsPointer)
|
|
{
|
|
var elementType = GetPSMethodProjectedType(type.GetElementType());
|
|
type = typeof(PSPointer<>).MakeGenericType(elementType);
|
|
}
|
|
|
|
return type;
|
|
}
|
|
|
|
private static Func<string, DotNetAdapter, object, object, bool, bool, PSMethod> CreatePSMethodConstructor(MethodInformation[] methods)
|
|
{
|
|
// Produce the PSMethod creator for MethodInfo objects
|
|
var types = new Type[methods.Length];
|
|
for (int i = 0; i < methods.Length; i++)
|
|
{
|
|
types[i] = GetMethodGroupType((MethodInfo) methods[i].method);
|
|
}
|
|
|
|
var methodGroupType = CreateMethodGroup(types, 0, types.Length);
|
|
Type psMethodType = typeof(PSMethod<>).MakeGenericType(methodGroupType);
|
|
var delegateType = typeof(Func<string, DotNetAdapter, object, object, bool, bool, PSMethod>);
|
|
return (Func<string, DotNetAdapter, object, object, bool, bool, PSMethod>) Delegate.CreateDelegate(delegateType,
|
|
psMethodType.GetMethod("Create", BindingFlags.NonPublic | BindingFlags.Static));
|
|
}
|
|
|
|
private static Type CreateMethodGroup(Type[] sourceTypes, int start, int count)
|
|
{
|
|
var types = sourceTypes;
|
|
if (count != sourceTypes.Length)
|
|
{
|
|
types = new Type[count];
|
|
Array.Copy(sourceTypes, start, types, 0, count);
|
|
}
|
|
|
|
switch (count)
|
|
{
|
|
case 1: return typeof(MethodGroup<>).MakeGenericType(types);
|
|
case 2: return typeof(MethodGroup<,>).MakeGenericType(types);
|
|
case 3: return typeof(MethodGroup<,>).MakeGenericType(types[0], CreateMethodGroup(types, 1, 2));
|
|
case 4: return typeof(MethodGroup<,,,>).MakeGenericType(types);
|
|
case int i when i < 8: return typeof(MethodGroup<,,,>).MakeGenericType(types[0], types[1], types[2], CreateMethodGroup(types, 3, i - 3));
|
|
case 8: return typeof(MethodGroup<,,,,,,,>).MakeGenericType(types);
|
|
case int i when i < 16:
|
|
return typeof(MethodGroup<,,,,,,,>).MakeGenericType(types[0], types[1], types[2], types[3], types[4], types[5], types[6], CreateMethodGroup(types, 7, i - 7));
|
|
case 16: return typeof(MethodGroup<,,,,,,,,,,,,,,,>).MakeGenericType(types);
|
|
case int i when i < 32:
|
|
return typeof(MethodGroup<,,,,,,,,,,,,,,,>).MakeGenericType(types[0], types[1], types[2], types[3], types[4], types[5], types[6], types[7], types[8], types[9], types[10],
|
|
types[11], types[12], types[13], types[14], CreateMethodGroup(types, 15, i - 15));
|
|
case 32: return typeof(MethodGroup<,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,>).MakeGenericType(types);
|
|
default:
|
|
return typeof(MethodGroup<,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,>).MakeGenericType(types[0], types[1], types[2], types[3], types[4], types[5], types[6], types[7], types[8],
|
|
types[9], types[10], types[11], types[12], types[13], types[14], types[15], types[16], types[17], types[18], types[19], types[20], types[21], types[22], types[23],
|
|
types[24], types[25], types[26], types[27], types[28], types[29], types[30], CreateMethodGroup(sourceTypes, start + 31, count - 31));
|
|
}
|
|
}
|
|
}
|
|
|
|
internal abstract class PSNonBindableType
|
|
{
|
|
}
|
|
|
|
internal class VOID
|
|
{
|
|
}
|
|
|
|
internal class PSOutParameter<T>
|
|
{
|
|
}
|
|
|
|
internal struct PSPointer<T>
|
|
{
|
|
}
|
|
|
|
internal struct PSTypedReference
|
|
{
|
|
}
|
|
|
|
internal abstract class MethodGroup
|
|
{
|
|
}
|
|
|
|
internal class MethodGroup<T1> : MethodGroup
|
|
{
|
|
}
|
|
|
|
internal class MethodGroup<T1, T2> : MethodGroup
|
|
{
|
|
}
|
|
|
|
internal class MethodGroup<T1, T2, T3, T4> : MethodGroup
|
|
{
|
|
}
|
|
|
|
internal class MethodGroup<T1, T2, T3, T4, T5, T6, T7, T8> : MethodGroup
|
|
{
|
|
}
|
|
|
|
internal class MethodGroup<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16> : MethodGroup
|
|
{
|
|
}
|
|
|
|
internal class MethodGroup<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28, T29, T30, T31,
|
|
T32> : MethodGroup
|
|
{
|
|
}
|
|
|
|
internal struct PSMethodSignatureEnumerator : IEnumerator<Type>
|
|
{
|
|
private int _currentIndex;
|
|
private readonly Type _t;
|
|
|
|
internal PSMethodSignatureEnumerator(Type t)
|
|
{
|
|
Diagnostics.Assert(t.IsSubclassOf(typeof(PSMethod)), "Must be a PSMethod<MethodGroup<>>");
|
|
_t = t.GenericTypeArguments[0];
|
|
Current = null;
|
|
_currentIndex = -1;
|
|
}
|
|
|
|
public bool MoveNext()
|
|
{
|
|
_currentIndex++;
|
|
return MoveNext(_t, _currentIndex);
|
|
}
|
|
|
|
bool MoveNext(Type type, int index)
|
|
{
|
|
var genericTypeArguments = type.GenericTypeArguments;
|
|
var length = genericTypeArguments.Length;
|
|
if (index < length - 1)
|
|
{
|
|
Current = genericTypeArguments[index];
|
|
return true;
|
|
}
|
|
|
|
var t = genericTypeArguments[length - 1];
|
|
if (t.IsSubclassOf(typeof(MethodGroup)))
|
|
{
|
|
var remaining = index - (length - 1);
|
|
return MoveNext(t, remaining);
|
|
}
|
|
|
|
if (index >= length)
|
|
{
|
|
Current = null;
|
|
return false;
|
|
}
|
|
|
|
Current = t;
|
|
return true;
|
|
}
|
|
|
|
public void Reset()
|
|
{
|
|
_currentIndex = -1;
|
|
Current = null;
|
|
}
|
|
|
|
public Type Current { get; private set; }
|
|
|
|
object IEnumerator.Current => Current;
|
|
|
|
public void Dispose()
|
|
{
|
|
}
|
|
}
|
|
|
|
internal sealed class PSMethod<T> : PSMethod
|
|
{
|
|
public override PSMemberInfo Copy()
|
|
{
|
|
PSMethod member = new PSMethod<T>(this.name, this._adapter, this.baseObject, this.adapterData, this.IsSpecial, this.IsHidden);
|
|
CloneBaseProperties(member);
|
|
return member;
|
|
}
|
|
|
|
internal PSMethod(string name, Adapter adapter, object baseObject, object adapterData)
|
|
: base(name, adapter, baseObject, adapterData)
|
|
{
|
|
}
|
|
|
|
internal PSMethod(string name, Adapter adapter, object baseObject, object adapterData, bool isSpecial, bool isHidden)
|
|
: base(name, adapter, baseObject, adapterData, isSpecial, isHidden)
|
|
{
|
|
}
|
|
|
|
/// <summary>
|
|
/// Helper factory function since we cannot bind a delegate to a ConstructorInfo.
|
|
/// </summary>
|
|
internal static PSMethod<T> Create(string name, Adapter adapter, object baseObject, object adapterData, bool isSpecial, bool isHidden)
|
|
{
|
|
return new PSMethod<T>(name, adapter, baseObject, adapterData, isSpecial, isHidden);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Used to access parameterized properties from the BaseObject.
|
|
/// </summary>
|
|
/// <remarks>
|
|
/// It is permitted to subclass <see cref="PSParameterizedProperty"/>
|
|
/// but there is no established scenario for doing this, nor has it been tested.
|
|
/// </remarks>
|
|
public class PSParameterizedProperty : PSMethodInfo
|
|
{
|
|
/// <summary>
|
|
/// Returns the string representation of this member.
|
|
/// </summary>
|
|
/// <returns>This property as a string.</returns>
|
|
public override string ToString()
|
|
{
|
|
Diagnostics.Assert((this.baseObject != null) && (this.adapter != null) && (this.adapterData != null), "it should have all these properties set");
|
|
return this.adapter.BaseParameterizedPropertyToString(this);
|
|
}
|
|
|
|
internal Adapter adapter;
|
|
internal object adapterData;
|
|
internal object baseObject;
|
|
|
|
/// <summary>
|
|
/// Constructs this parameterized property.
|
|
/// </summary>
|
|
/// <param name="name">Name of the property.</param>
|
|
/// <param name="adapter">Adapter used in DoGetMethod.</param>
|
|
/// <param name="baseObject">Object passed to DoGetMethod.</param>
|
|
/// <param name="adapterData">Adapter specific data.</param>
|
|
/// <exception cref="ArgumentException">For invalid arguments.</exception>
|
|
internal PSParameterizedProperty(string name, Adapter adapter, object baseObject, object adapterData)
|
|
{
|
|
if (string.IsNullOrEmpty(name))
|
|
{
|
|
throw PSTraceSource.NewArgumentException("name");
|
|
}
|
|
|
|
this.name = name;
|
|
this.adapter = adapter;
|
|
this.adapterData = adapterData;
|
|
this.baseObject = baseObject;
|
|
}
|
|
|
|
internal PSParameterizedProperty(string name)
|
|
{
|
|
if (string.IsNullOrEmpty(name))
|
|
{
|
|
throw PSTraceSource.NewArgumentException("name");
|
|
}
|
|
|
|
this.name = name;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets true if this property can be set.
|
|
/// </summary>
|
|
public bool IsSettable => adapter.BaseParameterizedPropertyIsSettable(this);
|
|
|
|
/// <summary>
|
|
/// Gets true if this property can be read.
|
|
/// </summary>
|
|
public bool IsGettable => adapter.BaseParameterizedPropertyIsGettable(this);
|
|
|
|
#region virtual implementation
|
|
|
|
/// <summary>
|
|
/// Invokes the getter method and returns its result.
|
|
/// </summary>
|
|
/// <param name="arguments">Arguments to the method.</param>
|
|
/// <returns>Return value from the method.</returns>
|
|
/// <exception cref="ArgumentException">If arguments is null.</exception>
|
|
/// <exception cref="GetValueException">When getting the value of a property throws an exception.</exception>
|
|
public override object Invoke(params object[] arguments)
|
|
{
|
|
if (arguments == null)
|
|
{
|
|
throw PSTraceSource.NewArgumentNullException("arguments");
|
|
}
|
|
|
|
return this.adapter.BaseParameterizedPropertyGet(this, arguments);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Invokes the setter method.
|
|
/// </summary>
|
|
/// <param name="valueToSet">Value to set this property with.</param>
|
|
/// <param name="arguments">Arguments to the method.</param>
|
|
/// <exception cref="ArgumentException">If arguments is null.</exception>
|
|
/// <exception cref="SetValueException">When setting the value of a property throws an exception.</exception>
|
|
public void InvokeSet(object valueToSet, params object[] arguments)
|
|
{
|
|
if (arguments == null)
|
|
{
|
|
throw PSTraceSource.NewArgumentNullException("arguments");
|
|
}
|
|
|
|
this.adapter.BaseParameterizedPropertySet(this, valueToSet, arguments);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Returns a collection of the definitions for this property.
|
|
/// </summary>
|
|
public override Collection<string> OverloadDefinitions => adapter.BaseParameterizedPropertyDefinitions(this);
|
|
|
|
/// <summary>
|
|
/// Gets the type of the value for this member.
|
|
/// </summary>
|
|
public override string TypeNameOfValue => adapter.BaseParameterizedPropertyType(this);
|
|
|
|
/// <summary>
|
|
/// Returns a new PSMemberInfo that is a copy of this PSMemberInfo.
|
|
/// </summary>
|
|
/// <returns>A new PSMemberInfo that is a copy of this PSMemberInfo.</returns>
|
|
public override PSMemberInfo Copy()
|
|
{
|
|
PSParameterizedProperty property = new PSParameterizedProperty(this.name, this.adapter, this.baseObject, this.adapterData);
|
|
CloneBaseProperties(property);
|
|
return property;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets the member type.
|
|
/// </summary>
|
|
public override PSMemberTypes MemberType => PSMemberTypes.ParameterizedProperty;
|
|
|
|
#endregion virtual implementation
|
|
}
|
|
|
|
/// <summary>
|
|
/// Serves as a set of members.
|
|
/// </summary>
|
|
public class PSMemberSet : PSMemberInfo
|
|
{
|
|
internal override void ReplicateInstance(object particularInstance)
|
|
{
|
|
base.ReplicateInstance(particularInstance);
|
|
foreach (var member in Members)
|
|
{
|
|
member.ReplicateInstance(particularInstance);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Returns the string representation of this member.
|
|
/// </summary>
|
|
/// <returns>This property as a string.</returns>
|
|
public override string ToString()
|
|
{
|
|
StringBuilder returnValue = new StringBuilder();
|
|
returnValue.Append(" {");
|
|
|
|
foreach (PSMemberInfo member in this.Members)
|
|
{
|
|
returnValue.Append(member.Name);
|
|
returnValue.Append(", ");
|
|
}
|
|
|
|
if (returnValue.Length > 2)
|
|
{
|
|
returnValue.Remove(returnValue.Length - 2, 2);
|
|
}
|
|
|
|
returnValue.Insert(0, this.Name);
|
|
returnValue.Append("}");
|
|
return returnValue.ToString();
|
|
}
|
|
|
|
private readonly PSMemberInfoIntegratingCollection<PSMemberInfo> _members;
|
|
private readonly PSMemberInfoIntegratingCollection<PSPropertyInfo> _properties;
|
|
private readonly PSMemberInfoIntegratingCollection<PSMethodInfo> _methods;
|
|
internal PSMemberInfoInternalCollection<PSMemberInfo> internalMembers;
|
|
private readonly PSObject _constructorPSObject;
|
|
|
|
private static readonly Collection<CollectionEntry<PSMemberInfo>> s_emptyMemberCollection = new Collection<CollectionEntry<PSMemberInfo>>();
|
|
private static readonly Collection<CollectionEntry<PSMethodInfo>> s_emptyMethodCollection = new Collection<CollectionEntry<PSMethodInfo>>();
|
|
private static readonly Collection<CollectionEntry<PSPropertyInfo>> s_emptyPropertyCollection = new Collection<CollectionEntry<PSPropertyInfo>>();
|
|
|
|
/// <summary>
|
|
/// Initializes a new instance of PSMemberSet with no initial members.
|
|
/// </summary>
|
|
/// <param name="name">Name for the member set.</param>
|
|
/// <exception cref="ArgumentException">For invalid arguments.</exception>
|
|
public PSMemberSet(string name)
|
|
{
|
|
if (string.IsNullOrEmpty(name))
|
|
{
|
|
throw PSTraceSource.NewArgumentException("name");
|
|
}
|
|
|
|
this.name = name;
|
|
this.internalMembers = new PSMemberInfoInternalCollection<PSMemberInfo>();
|
|
_members = new PSMemberInfoIntegratingCollection<PSMemberInfo>(this, s_emptyMemberCollection);
|
|
_properties = new PSMemberInfoIntegratingCollection<PSPropertyInfo>(this, s_emptyPropertyCollection);
|
|
_methods = new PSMemberInfoIntegratingCollection<PSMethodInfo>(this, s_emptyMethodCollection);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Initializes a new instance of PSMemberSet with all the initial members in <paramref name="members"/>
|
|
/// </summary>
|
|
/// <param name="name">Name for the member set.</param>
|
|
/// <param name="members">Members in the member set.</param>
|
|
/// <exception cref="ArgumentException">For invalid arguments.</exception>
|
|
public PSMemberSet(string name, IEnumerable<PSMemberInfo> members)
|
|
{
|
|
if (string.IsNullOrEmpty(name))
|
|
{
|
|
throw PSTraceSource.NewArgumentException("name");
|
|
}
|
|
|
|
this.name = name;
|
|
if (members == null)
|
|
{
|
|
throw PSTraceSource.NewArgumentNullException("members");
|
|
}
|
|
|
|
this.internalMembers = new PSMemberInfoInternalCollection<PSMemberInfo>();
|
|
foreach (PSMemberInfo member in members)
|
|
{
|
|
if (member == null)
|
|
{
|
|
throw PSTraceSource.NewArgumentNullException("members");
|
|
}
|
|
|
|
this.internalMembers.Add(member.Copy());
|
|
}
|
|
|
|
_members = new PSMemberInfoIntegratingCollection<PSMemberInfo>(this, s_emptyMemberCollection);
|
|
_properties = new PSMemberInfoIntegratingCollection<PSPropertyInfo>(this, s_emptyPropertyCollection);
|
|
_methods = new PSMemberInfoIntegratingCollection<PSMethodInfo>(this, s_emptyMethodCollection);
|
|
}
|
|
|
|
private static readonly Collection<CollectionEntry<PSMemberInfo>> s_typeMemberCollection = GetTypeMemberCollection();
|
|
private static readonly Collection<CollectionEntry<PSMethodInfo>> s_typeMethodCollection = GetTypeMethodCollection();
|
|
private static readonly Collection<CollectionEntry<PSPropertyInfo>> s_typePropertyCollection = GetTypePropertyCollection();
|
|
|
|
private static Collection<CollectionEntry<PSMemberInfo>> GetTypeMemberCollection()
|
|
{
|
|
Collection<CollectionEntry<PSMemberInfo>> returnValue = new Collection<CollectionEntry<PSMemberInfo>>();
|
|
returnValue.Add(new CollectionEntry<PSMemberInfo>(
|
|
PSObject.TypeTableGetMembersDelegate<PSMemberInfo>,
|
|
PSObject.TypeTableGetMemberDelegate<PSMemberInfo>,
|
|
true, true, "type table members"));
|
|
return returnValue;
|
|
}
|
|
|
|
private static Collection<CollectionEntry<PSMethodInfo>> GetTypeMethodCollection()
|
|
{
|
|
Collection<CollectionEntry<PSMethodInfo>> returnValue = new Collection<CollectionEntry<PSMethodInfo>>();
|
|
returnValue.Add(new CollectionEntry<PSMethodInfo>(
|
|
PSObject.TypeTableGetMembersDelegate<PSMethodInfo>,
|
|
PSObject.TypeTableGetMemberDelegate<PSMethodInfo>,
|
|
true, true, "type table members"));
|
|
return returnValue;
|
|
}
|
|
|
|
private static Collection<CollectionEntry<PSPropertyInfo>> GetTypePropertyCollection()
|
|
{
|
|
Collection<CollectionEntry<PSPropertyInfo>> returnValue = new Collection<CollectionEntry<PSPropertyInfo>>();
|
|
returnValue.Add(new CollectionEntry<PSPropertyInfo>(
|
|
PSObject.TypeTableGetMembersDelegate<PSPropertyInfo>,
|
|
PSObject.TypeTableGetMemberDelegate<PSPropertyInfo>,
|
|
true, true, "type table members"));
|
|
return returnValue;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Used to create the Extended MemberSet.
|
|
/// </summary>
|
|
/// <param name="name">Name of the memberSet.</param>
|
|
/// <param name="mshObject">Object associated with this memberset.</param>
|
|
/// <exception cref="ArgumentException">For invalid arguments.</exception>
|
|
internal PSMemberSet(string name, PSObject mshObject)
|
|
{
|
|
if (string.IsNullOrEmpty(name))
|
|
{
|
|
throw PSTraceSource.NewArgumentException("name");
|
|
}
|
|
|
|
this.name = name;
|
|
if (mshObject == null)
|
|
{
|
|
throw PSTraceSource.NewArgumentNullException("mshObject");
|
|
}
|
|
|
|
_constructorPSObject = mshObject;
|
|
this.internalMembers = mshObject.InstanceMembers;
|
|
_members = new PSMemberInfoIntegratingCollection<PSMemberInfo>(this, s_typeMemberCollection);
|
|
_properties = new PSMemberInfoIntegratingCollection<PSPropertyInfo>(this, s_typePropertyCollection);
|
|
_methods = new PSMemberInfoIntegratingCollection<PSMethodInfo>(this, s_typeMethodCollection);
|
|
}
|
|
|
|
internal bool inheritMembers = true;
|
|
|
|
/// <summary>
|
|
/// Gets a flag indicating whether the memberset will inherit members of the memberset
|
|
/// of the same name in the "parent" class.
|
|
/// </summary>
|
|
public bool InheritMembers => this.inheritMembers;
|
|
|
|
/// <summary>
|
|
/// Gets the internal member collection.
|
|
/// </summary>
|
|
internal virtual PSMemberInfoInternalCollection<PSMemberInfo> InternalMembers => this.internalMembers;
|
|
|
|
/// <summary>
|
|
/// Gets the member collection.
|
|
/// </summary>
|
|
public PSMemberInfoCollection<PSMemberInfo> Members => _members;
|
|
|
|
/// <summary>
|
|
/// Gets the Property collection, or the members that are actually properties.
|
|
/// </summary>
|
|
public PSMemberInfoCollection<PSPropertyInfo> Properties => _properties;
|
|
|
|
/// <summary>
|
|
/// Gets the Method collection, or the members that are actually methods.
|
|
/// </summary>
|
|
public PSMemberInfoCollection<PSMethodInfo> Methods => _methods;
|
|
|
|
#region virtual implementation
|
|
|
|
/// <summary>
|
|
/// Returns a new PSMemberInfo that is a copy of this PSMemberInfo.
|
|
/// </summary>
|
|
/// <returns>A new PSMemberInfo that is a copy of this PSMemberInfo.</returns>
|
|
public override PSMemberInfo Copy()
|
|
{
|
|
if (_constructorPSObject == null)
|
|
{
|
|
PSMemberSet memberSet = new PSMemberSet(name);
|
|
foreach (PSMemberInfo member in this.Members)
|
|
{
|
|
memberSet.Members.Add(member);
|
|
}
|
|
|
|
CloneBaseProperties(memberSet);
|
|
return memberSet;
|
|
}
|
|
else
|
|
{
|
|
return new PSMemberSet(name, _constructorPSObject);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets the member type. For PSMemberSet the member type is PSMemberTypes.MemberSet.
|
|
/// </summary>
|
|
public override PSMemberTypes MemberType => PSMemberTypes.MemberSet;
|
|
|
|
/// <summary>
|
|
/// Gets the value of this member. The getter returns the PSMemberSet itself.
|
|
/// </summary>
|
|
/// <exception cref="ExtendedTypeSystemException">When trying to set the property.</exception>
|
|
public override object Value
|
|
{
|
|
get => this;
|
|
set => throw new ExtendedTypeSystemException("CannotChangePSMemberSetValue", null,
|
|
ExtendedTypeSystem.CannotSetValueForMemberType, this.GetType().FullName);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets the type of the value for this member. This returns typeof(PSMemberSet).FullName.
|
|
/// </summary>
|
|
public override string TypeNameOfValue => typeof(PSMemberSet).FullName;
|
|
|
|
#endregion virtual implementation
|
|
}
|
|
|
|
/// <summary>
|
|
/// This MemberSet is used internally to represent the memberset for properties
|
|
/// PSObject, PSBase, PSAdapted members of a PSObject. Having a specialized
|
|
/// memberset enables delay loading the members for these members. This saves
|
|
/// time loading the members of a PSObject.
|
|
/// </summary>
|
|
/// <remarks>
|
|
/// This is added to improve hosting PowerShell's PSObjects in a ASP.Net GridView
|
|
/// Control
|
|
/// </remarks>
|
|
internal class PSInternalMemberSet : PSMemberSet
|
|
{
|
|
private readonly object _syncObject = new Object();
|
|
private readonly PSObject _psObject;
|
|
|
|
#region Constructor
|
|
|
|
/// <summary>
|
|
/// Constructs the specialized member set.
|
|
/// </summary>
|
|
/// <param name="propertyName">
|
|
/// Should be one of PSObject, PSBase, PSAdapted
|
|
/// </param>
|
|
/// <param name="psObject">
|
|
/// original PSObject to use to generate members
|
|
/// </param>
|
|
internal PSInternalMemberSet(string propertyName, PSObject psObject)
|
|
: base(propertyName)
|
|
{
|
|
this.internalMembers = null;
|
|
_psObject = psObject;
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region virtual overrides
|
|
|
|
/// <summary>
|
|
/// Generates the members when needed.
|
|
/// </summary>
|
|
internal override PSMemberInfoInternalCollection<PSMemberInfo> InternalMembers
|
|
{
|
|
get
|
|
{
|
|
// do not cache "psadapted"
|
|
if (name.Equals(PSObject.AdaptedMemberSetName, StringComparison.OrdinalIgnoreCase))
|
|
{
|
|
return GetInternalMembersFromAdapted();
|
|
}
|
|
|
|
// cache "psbase" and "psobject"
|
|
if (internalMembers == null)
|
|
{
|
|
lock (_syncObject)
|
|
{
|
|
if (internalMembers == null)
|
|
{
|
|
internalMembers = new PSMemberInfoInternalCollection<PSMemberInfo>();
|
|
|
|
switch (name.ToLowerInvariant())
|
|
{
|
|
case PSObject.BaseObjectMemberSetName:
|
|
GenerateInternalMembersFromBase();
|
|
break;
|
|
case PSObject.PSObjectMemberSetName:
|
|
GenerateInternalMembersFromPSObject();
|
|
break;
|
|
default:
|
|
Diagnostics.Assert(false,
|
|
string.Format(CultureInfo.InvariantCulture,
|
|
"PSInternalMemberSet cannot process {0}", name));
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return internalMembers;
|
|
}
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Private Methods
|
|
|
|
private void GenerateInternalMembersFromBase()
|
|
{
|
|
if (_psObject.isDeserialized)
|
|
{
|
|
if (_psObject.clrMembers != null)
|
|
{
|
|
foreach (PSMemberInfo member in _psObject.clrMembers)
|
|
{
|
|
internalMembers.Add(member.Copy());
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
foreach (PSMemberInfo member in
|
|
PSObject.dotNetInstanceAdapter.BaseGetMembers<PSMemberInfo>(_psObject.ImmediateBaseObject))
|
|
{
|
|
internalMembers.Add(member.Copy());
|
|
}
|
|
}
|
|
}
|
|
|
|
private PSMemberInfoInternalCollection<PSMemberInfo> GetInternalMembersFromAdapted()
|
|
{
|
|
PSMemberInfoInternalCollection<PSMemberInfo> retVal = new PSMemberInfoInternalCollection<PSMemberInfo>();
|
|
|
|
if (_psObject.isDeserialized)
|
|
{
|
|
if (_psObject.adaptedMembers != null)
|
|
{
|
|
foreach (PSMemberInfo member in _psObject.adaptedMembers)
|
|
{
|
|
retVal.Add(member.Copy());
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
foreach (PSMemberInfo member in _psObject.InternalAdapter.BaseGetMembers<PSMemberInfo>(
|
|
_psObject.ImmediateBaseObject))
|
|
{
|
|
retVal.Add(member.Copy());
|
|
}
|
|
}
|
|
|
|
return retVal;
|
|
}
|
|
|
|
private void GenerateInternalMembersFromPSObject()
|
|
{
|
|
PSMemberInfoCollection<PSMemberInfo> members = PSObject.dotNetInstanceAdapter.BaseGetMembers<PSMemberInfo>(
|
|
_psObject);
|
|
foreach (PSMemberInfo member in members)
|
|
{
|
|
internalMembers.Add(member.Copy());
|
|
}
|
|
}
|
|
|
|
#endregion
|
|
}
|
|
|
|
/// <summary>
|
|
/// Serves as a list of property names.
|
|
/// </summary>
|
|
/// <remarks>
|
|
/// It is permitted to subclass <see cref="PSPropertySet"/>
|
|
/// but there is no established scenario for doing this, nor has it been tested.
|
|
/// </remarks>
|
|
public class PSPropertySet : PSMemberInfo
|
|
{
|
|
/// <summary>
|
|
/// Returns the string representation of this member.
|
|
/// </summary>
|
|
/// <returns>This property as a string.</returns>
|
|
public override string ToString()
|
|
{
|
|
StringBuilder returnValue = new StringBuilder();
|
|
returnValue.Append(this.Name);
|
|
returnValue.Append(" {");
|
|
if (ReferencedPropertyNames.Count != 0)
|
|
{
|
|
foreach (string property in ReferencedPropertyNames)
|
|
{
|
|
returnValue.Append(property);
|
|
returnValue.Append(", ");
|
|
}
|
|
|
|
returnValue.Remove(returnValue.Length - 2, 2);
|
|
}
|
|
|
|
returnValue.Append("}");
|
|
return returnValue.ToString();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Initializes a new instance of PSPropertySet with a name and list of property names.
|
|
/// </summary>
|
|
/// <param name="name">Name of the set.</param>
|
|
/// <param name="referencedPropertyNames">Name of the properties in the set.</param>
|
|
/// <exception cref="ArgumentException">For invalid arguments.</exception>
|
|
public PSPropertySet(string name, IEnumerable<string> referencedPropertyNames)
|
|
{
|
|
if (string.IsNullOrEmpty(name))
|
|
{
|
|
throw PSTraceSource.NewArgumentException("name");
|
|
}
|
|
|
|
this.name = name;
|
|
if (referencedPropertyNames == null)
|
|
{
|
|
throw PSTraceSource.NewArgumentNullException("referencedPropertyNames");
|
|
}
|
|
|
|
ReferencedPropertyNames = new Collection<string>();
|
|
foreach (string referencedPropertyName in referencedPropertyNames)
|
|
{
|
|
if (string.IsNullOrEmpty(referencedPropertyName))
|
|
{
|
|
throw PSTraceSource.NewArgumentException("referencedPropertyNames");
|
|
}
|
|
|
|
ReferencedPropertyNames.Add(referencedPropertyName);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets the property names in this property set.
|
|
/// </summary>
|
|
public Collection<string> ReferencedPropertyNames { get; }
|
|
|
|
#region virtual implementation
|
|
|
|
/// <summary>
|
|
/// Returns a new PSMemberInfo that is a copy of this PSMemberInfo.
|
|
/// </summary>
|
|
/// <returns>A new PSMemberInfo that is a copy of this PSMemberInfo.</returns>
|
|
public override PSMemberInfo Copy()
|
|
{
|
|
PSPropertySet member = new PSPropertySet(name, ReferencedPropertyNames);
|
|
CloneBaseProperties(member);
|
|
return member;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets the member type.
|
|
/// </summary>
|
|
public override PSMemberTypes MemberType => PSMemberTypes.PropertySet;
|
|
|
|
/// <summary>
|
|
/// Gets the PSPropertySet itself.
|
|
/// </summary>
|
|
/// <exception cref="ExtendedTypeSystemException">When setting the member.</exception>
|
|
public override object Value
|
|
{
|
|
get => this;
|
|
set => throw new ExtendedTypeSystemException("CannotChangePSPropertySetValue", null,
|
|
ExtendedTypeSystem.CannotSetValueForMemberType, this.GetType().FullName);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets the type of the value for this member. This returns typeof(PSPropertySet).FullName.
|
|
/// </summary>
|
|
public override string TypeNameOfValue => typeof(PSPropertySet).FullName;
|
|
|
|
#endregion virtual implementation
|
|
}
|
|
|
|
/// <summary>
|
|
/// Used to access the adapted or base events from the BaseObject.
|
|
/// </summary>
|
|
/// <remarks>
|
|
/// It is permitted to subclass <see cref="PSMethod"/>
|
|
/// but there is no established scenario for doing this, nor has it been tested.
|
|
/// </remarks>
|
|
public class PSEvent : PSMemberInfo
|
|
{
|
|
/// <summary>
|
|
/// Returns the string representation of this member.
|
|
/// </summary>
|
|
/// <returns>This property as a string.</returns>
|
|
public override string ToString()
|
|
{
|
|
StringBuilder eventDefinition = new StringBuilder();
|
|
eventDefinition.Append(this.baseEvent.ToString());
|
|
|
|
eventDefinition.Append("(");
|
|
|
|
int loopCounter = 0;
|
|
foreach (ParameterInfo parameter in baseEvent.EventHandlerType.GetMethod("Invoke").GetParameters())
|
|
{
|
|
if (loopCounter > 0)
|
|
eventDefinition.Append(", ");
|
|
|
|
eventDefinition.Append(parameter.ParameterType.ToString());
|
|
|
|
loopCounter++;
|
|
}
|
|
|
|
eventDefinition.Append(")");
|
|
|
|
return eventDefinition.ToString();
|
|
}
|
|
|
|
internal EventInfo baseEvent;
|
|
|
|
/// <summary>
|
|
/// Constructs this event.
|
|
/// </summary>
|
|
/// <param name="baseEvent">The actual event.</param>
|
|
/// <exception cref="ArgumentException">For invalid arguments.</exception>
|
|
internal PSEvent(EventInfo baseEvent)
|
|
{
|
|
this.baseEvent = baseEvent;
|
|
this.name = baseEvent.Name;
|
|
}
|
|
|
|
#region virtual implementation
|
|
|
|
/// <summary>
|
|
/// Returns a new PSMemberInfo that is a copy of this PSMemberInfo.
|
|
/// </summary>
|
|
/// <returns>A new PSMemberInfo that is a copy of this PSMemberInfo.</returns>
|
|
public override PSMemberInfo Copy()
|
|
{
|
|
PSEvent member = new PSEvent(this.baseEvent);
|
|
CloneBaseProperties(member);
|
|
return member;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets the member type.
|
|
/// </summary>
|
|
public override PSMemberTypes MemberType => PSMemberTypes.Event;
|
|
|
|
/// <summary>
|
|
/// Gets the value of this member. The getter returns the
|
|
/// actual .NET event that this type wraps.
|
|
/// </summary>
|
|
/// <exception cref="ExtendedTypeSystemException">When setting the member.</exception>
|
|
public sealed override object Value
|
|
{
|
|
get => baseEvent;
|
|
set => throw new ExtendedTypeSystemException("CannotChangePSEventInfoValue", null,
|
|
ExtendedTypeSystem.CannotSetValueForMemberType, this.GetType().FullName);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets the type of the value for this member. This always returns typeof(PSMethod).FullName.
|
|
/// </summary>
|
|
public override string TypeNameOfValue => typeof(PSEvent).FullName;
|
|
|
|
#endregion virtual implementation
|
|
}
|
|
|
|
/// <summary>
|
|
/// A dynamic member.
|
|
/// </summary>
|
|
public class PSDynamicMember : PSMemberInfo
|
|
{
|
|
internal PSDynamicMember(string name)
|
|
{
|
|
this.name = name;
|
|
}
|
|
|
|
/// <summary/>
|
|
public override string ToString()
|
|
{
|
|
return "dynamic " + Name;
|
|
}
|
|
|
|
/// <summary/>
|
|
public override PSMemberTypes MemberType => PSMemberTypes.Dynamic;
|
|
|
|
/// <summary/>
|
|
public override object Value
|
|
{
|
|
get => throw PSTraceSource.NewInvalidOperationException();
|
|
set => throw PSTraceSource.NewInvalidOperationException();
|
|
}
|
|
|
|
/// <summary/>
|
|
public override string TypeNameOfValue => "dynamic";
|
|
|
|
/// <summary/>
|
|
public override PSMemberInfo Copy()
|
|
{
|
|
return new PSDynamicMember(Name);
|
|
}
|
|
}
|
|
|
|
#endregion PSMemberInfo
|
|
|
|
#region Member collection classes and its auxiliary classes
|
|
|
|
/// <summary>
|
|
/// /// This class is used in PSMemberInfoInternalCollection and ReadOnlyPSMemberInfoCollection.
|
|
/// </summary>
|
|
internal class MemberMatch
|
|
{
|
|
internal static WildcardPattern GetNamePattern(string name)
|
|
{
|
|
if (name != null && WildcardPattern.ContainsWildcardCharacters(name))
|
|
{
|
|
return WildcardPattern.Get(name, WildcardOptions.IgnoreCase);
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Returns all members in memberList matching name and memberTypes.
|
|
/// </summary>
|
|
/// <param name="memberList">Members to look for member with the correct types and name.</param>
|
|
/// <param name="name">Name of the members to look for. The name might contain globbing characters.</param>
|
|
/// <param name="nameMatch">WildcardPattern out of name.</param>
|
|
/// <param name="memberTypes">Type of members we want to retrieve.</param>
|
|
/// <returns>A collection of members of the right types and name extracted from memberList.</returns>
|
|
/// <exception cref="ArgumentException">For invalid arguments.</exception>
|
|
internal static PSMemberInfoInternalCollection<T> Match<T>(PSMemberInfoInternalCollection<T> memberList, string name, WildcardPattern nameMatch, PSMemberTypes memberTypes)
|
|
where T : PSMemberInfo
|
|
{
|
|
PSMemberInfoInternalCollection<T> returnValue = new PSMemberInfoInternalCollection<T>();
|
|
if (memberList == null)
|
|
{
|
|
throw PSTraceSource.NewArgumentNullException("memberList");
|
|
}
|
|
|
|
if (string.IsNullOrEmpty(name))
|
|
{
|
|
throw PSTraceSource.NewArgumentException("name");
|
|
}
|
|
|
|
if (nameMatch == null)
|
|
{
|
|
T member = memberList[name];
|
|
if (member != null && (member.MemberType & memberTypes) != 0)
|
|
{
|
|
returnValue.Add(member);
|
|
}
|
|
|
|
return returnValue;
|
|
}
|
|
|
|
foreach (T member in memberList)
|
|
{
|
|
if (nameMatch.IsMatch(member.Name) && ((member.MemberType & memberTypes) != 0))
|
|
{
|
|
returnValue.Add(member);
|
|
}
|
|
}
|
|
|
|
return returnValue;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Serves as the collection of members in an PSObject or MemberSet.
|
|
/// </summary>
|
|
public abstract class PSMemberInfoCollection<T> : IEnumerable<T> where T : PSMemberInfo
|
|
{
|
|
#region ctor
|
|
|
|
/// <summary>
|
|
/// Initializes a new instance of an PSMemberInfoCollection derived class.
|
|
/// </summary>
|
|
protected PSMemberInfoCollection()
|
|
{
|
|
}
|
|
|
|
#endregion ctor
|
|
|
|
#region abstract
|
|
|
|
/// <summary>
|
|
/// Adds a member to this collection.
|
|
/// </summary>
|
|
/// <param name="member">Member to be added.</param>
|
|
/// <exception cref="ExtendedTypeSystemException">
|
|
/// When:
|
|
/// adding a member to an PSMemberSet from the type configuration file or
|
|
/// adding a member with a reserved member name or
|
|
/// trying to add a member with a type not compatible with this collection or
|
|
/// a member by this name is already present
|
|
/// </exception>
|
|
/// <exception cref="ArgumentException">For invalid arguments.</exception>
|
|
public abstract void Add(T member);
|
|
|
|
/// <summary>
|
|
/// Adds a member to this collection.
|
|
/// </summary>
|
|
/// <param name="member">Member to be added.</param>
|
|
/// <param name="preValidated">flag to indicate that validation has already been done
|
|
/// on this new member. Use only when you can guarantee that the input will not
|
|
/// cause any of the errors normally caught by this method.</param>
|
|
/// <exception cref="ExtendedTypeSystemException">
|
|
/// When:
|
|
/// adding a member to an PSMemberSet from the type configuration file or
|
|
/// adding a member with a reserved member name or
|
|
/// trying to add a member with a type not compatible with this collection or
|
|
/// a member by this name is already present
|
|
/// </exception>
|
|
/// <exception cref="ArgumentException">For invalid arguments.</exception>
|
|
public abstract void Add(T member, bool preValidated);
|
|
|
|
/// <summary>
|
|
/// Removes a member from this collection.
|
|
/// </summary>
|
|
/// <param name="name">Name of the member to be removed.</param>
|
|
/// <exception cref="ExtendedTypeSystemException">
|
|
/// When:
|
|
/// removing a member from an PSMemberSet from the type configuration file
|
|
/// removing a member with a reserved member name or
|
|
/// trying to remove a member with a type not compatible with this collection
|
|
/// </exception>
|
|
/// <exception cref="ArgumentException">For invalid arguments.</exception>
|
|
public abstract void Remove(string name);
|
|
|
|
/// <summary>
|
|
/// Gets the member in this collection matching name. If the member does not exist, null is returned.
|
|
/// </summary>
|
|
/// <param name="name">Name of the member to look for.</param>
|
|
/// <returns>The member matching name.</returns>
|
|
/// <exception cref="ArgumentException">For invalid arguments.</exception>
|
|
public abstract T this[string name] { get; }
|
|
|
|
#endregion abstract
|
|
|
|
#region Match
|
|
|
|
/// <summary>
|
|
/// Returns all members in the collection matching name.
|
|
/// </summary>
|
|
/// <param name="name">Name of the members to be return. May contain wildcard characters.</param>
|
|
/// <returns>All members in the collection matching name.</returns>
|
|
/// <exception cref="ArgumentException">For invalid arguments.</exception>
|
|
public abstract ReadOnlyPSMemberInfoCollection<T> Match(string name);
|
|
|
|
/// <summary>
|
|
/// Returns all members in the collection matching name and types.
|
|
/// </summary>
|
|
/// <param name="name">Name of the members to be return. May contain wildcard characters.</param>
|
|
/// <param name="memberTypes">Type of the members to be searched.</param>
|
|
/// <returns>All members in the collection matching name and types.</returns>
|
|
/// <exception cref="ArgumentException">For invalid arguments.</exception>
|
|
public abstract ReadOnlyPSMemberInfoCollection<T> Match(string name, PSMemberTypes memberTypes);
|
|
|
|
/// <summary>
|
|
/// Returns all members in the collection matching name and types.
|
|
/// </summary>
|
|
/// <param name="name">Name of the members to be return. May contain wildcard characters.</param>
|
|
/// <param name="memberTypes">Type of the members to be searched.</param>
|
|
/// <param name="matchOptions">Match options.</param>
|
|
/// <returns>All members in the collection matching name and types.</returns>
|
|
/// <exception cref="ArgumentException">For invalid arguments.</exception>
|
|
internal abstract ReadOnlyPSMemberInfoCollection<T> Match(string name, PSMemberTypes memberTypes, MshMemberMatchOptions matchOptions);
|
|
|
|
#endregion Match
|
|
|
|
internal static bool IsReservedName(string name)
|
|
{
|
|
return (string.Equals(name, PSObject.BaseObjectMemberSetName, StringComparison.OrdinalIgnoreCase) ||
|
|
string.Equals(name, PSObject.AdaptedMemberSetName, StringComparison.OrdinalIgnoreCase) ||
|
|
string.Equals(name, PSObject.ExtendedMemberSetName, StringComparison.OrdinalIgnoreCase) ||
|
|
string.Equals(name, PSObject.PSObjectMemberSetName, StringComparison.OrdinalIgnoreCase) ||
|
|
string.Equals(name, PSObject.PSTypeNames, StringComparison.OrdinalIgnoreCase));
|
|
}
|
|
|
|
#region IEnumerable
|
|
|
|
/// <summary>
|
|
/// Gets the general enumerator for this collection.
|
|
/// </summary>
|
|
/// <returns>The enumerator for this collection.</returns>
|
|
IEnumerator IEnumerable.GetEnumerator()
|
|
{
|
|
return GetEnumerator();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets the specific enumerator for this collection.
|
|
/// </summary>
|
|
/// <returns>The enumerator for this collection.</returns>
|
|
public abstract IEnumerator<T> GetEnumerator();
|
|
|
|
#endregion IEnumerable
|
|
}
|
|
|
|
/// <summary>
|
|
/// Serves as a read only collection of members.
|
|
/// </summary>
|
|
/// <remarks>
|
|
/// It is permitted to subclass <see cref="ReadOnlyPSMemberInfoCollection<T>"/>
|
|
/// but there is no established scenario for doing this, nor has it been tested.
|
|
/// </remarks>
|
|
public class ReadOnlyPSMemberInfoCollection<T> : IEnumerable<T> where T : PSMemberInfo
|
|
{
|
|
private readonly PSMemberInfoInternalCollection<T> _members;
|
|
|
|
/// <summary>
|
|
/// Initializes a new instance of ReadOnlyPSMemberInfoCollection with the given members.
|
|
/// </summary>
|
|
/// <param name="members"></param>
|
|
/// <exception cref="ArgumentException">For invalid arguments.</exception>
|
|
internal ReadOnlyPSMemberInfoCollection(PSMemberInfoInternalCollection<T> members)
|
|
{
|
|
if (members == null)
|
|
{
|
|
throw PSTraceSource.NewArgumentNullException("members");
|
|
}
|
|
|
|
_members = members;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Return the member in this collection matching name. If the member does not exist, null is returned.
|
|
/// </summary>
|
|
/// <param name="name">Name of the member to look for.</param>
|
|
/// <returns>The member matching name.</returns>
|
|
/// <exception cref="ArgumentException">For invalid arguments.</exception>
|
|
public T this[string name]
|
|
{
|
|
get
|
|
{
|
|
if (string.IsNullOrEmpty(name))
|
|
{
|
|
throw PSTraceSource.NewArgumentException("name");
|
|
}
|
|
|
|
return _members[name];
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Returns all members in the collection matching name.
|
|
/// </summary>
|
|
/// <param name="name">Name of the members to be return. May contain wildcard characters.</param>
|
|
/// <returns>All members in the collection matching name.</returns>
|
|
/// <exception cref="ArgumentException">For invalid arguments.</exception>
|
|
public ReadOnlyPSMemberInfoCollection<T> Match(string name)
|
|
{
|
|
if (string.IsNullOrEmpty(name))
|
|
{
|
|
throw PSTraceSource.NewArgumentException("name");
|
|
}
|
|
|
|
return _members.Match(name);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Returns all members in the collection matching name and types.
|
|
/// </summary>
|
|
/// <param name="name">Name of the members to be return. May contain wildcard characters.</param>
|
|
/// <param name="memberTypes">Type of the members to be searched.</param>
|
|
/// <returns>All members in the collection matching name and types.</returns>
|
|
/// <exception cref="ArgumentException">For invalid arguments.</exception>
|
|
public ReadOnlyPSMemberInfoCollection<T> Match(string name, PSMemberTypes memberTypes)
|
|
{
|
|
if (string.IsNullOrEmpty(name))
|
|
{
|
|
throw PSTraceSource.NewArgumentException("name");
|
|
}
|
|
|
|
return _members.Match(name, memberTypes);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets the general enumerator for this collection.
|
|
/// </summary>
|
|
/// <returns>The enumerator for this collection.</returns>
|
|
IEnumerator IEnumerable.GetEnumerator()
|
|
{
|
|
return GetEnumerator();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets the specific enumerator for this collection.
|
|
/// </summary>
|
|
/// <returns>The enumerator for this collection.</returns>
|
|
public virtual IEnumerator<T> GetEnumerator()
|
|
{
|
|
return _members.GetEnumerator();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets the number of elements in this collection.
|
|
/// </summary>
|
|
public int Count => _members.Count;
|
|
|
|
/// <summary>
|
|
/// Returns the 0 based member identified by index.
|
|
/// </summary>
|
|
/// <param name="index">Index of the member to retrieve.</param>
|
|
/// <exception cref="ArgumentException">For invalid arguments.</exception>
|
|
public T this[int index] => _members[index];
|
|
}
|
|
|
|
/// <summary>
|
|
/// Collection of members.
|
|
/// </summary>
|
|
internal class PSMemberInfoInternalCollection<T> : PSMemberInfoCollection<T>, IEnumerable<T> where T : PSMemberInfo
|
|
{
|
|
private OrderedDictionary _members;
|
|
private int _countHidden;
|
|
|
|
/// <summary>
|
|
/// Gets the OrderedDictionary for holding all members.
|
|
/// We use this property to delay initializing _members until we absolutely need to.
|
|
/// </summary>
|
|
private OrderedDictionary Members
|
|
{
|
|
get
|
|
{
|
|
if (_members == null)
|
|
{
|
|
System.Threading.Interlocked.CompareExchange(ref _members, new OrderedDictionary(StringComparer.OrdinalIgnoreCase), null);
|
|
}
|
|
|
|
return _members;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Constructs this collection.
|
|
/// </summary>
|
|
internal PSMemberInfoInternalCollection()
|
|
{
|
|
}
|
|
|
|
/// <summary>
|
|
/// Constructs this collection with an initial capacity.
|
|
/// </summary>
|
|
internal PSMemberInfoInternalCollection(int capacity)
|
|
{
|
|
_members = new OrderedDictionary(capacity, StringComparer.OrdinalIgnoreCase);
|
|
}
|
|
|
|
private void Replace(T oldMember, T newMember)
|
|
{
|
|
Members[newMember.Name] = newMember;
|
|
if (oldMember.IsHidden)
|
|
{
|
|
_countHidden--;
|
|
}
|
|
|
|
if (newMember.IsHidden)
|
|
{
|
|
_countHidden++;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Adds a member to the collection by replacing the one with the same name.
|
|
/// </summary>
|
|
/// <param name="newMember"></param>
|
|
internal void Replace(T newMember)
|
|
{
|
|
Diagnostics.Assert(newMember != null, "called from internal code that checks for new member not null");
|
|
|
|
// Save to a local variable to reduce property access.
|
|
var members = Members;
|
|
lock (members)
|
|
{
|
|
var oldMember = members[newMember.Name] as T;
|
|
Diagnostics.Assert(oldMember != null, "internal code checks member already exists");
|
|
Replace(oldMember, newMember);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Adds a member to this collection.
|
|
/// </summary>
|
|
/// <param name="member">Member to be added.</param>
|
|
/// <exception cref="ExtendedTypeSystemException">When a member by this name is already present.</exception>
|
|
/// <exception cref="ArgumentException">For invalid arguments.</exception>
|
|
public override void Add(T member)
|
|
{
|
|
Add(member, false);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Adds a member to this collection.
|
|
/// </summary>
|
|
/// <param name="member">Member to be added.</param>
|
|
/// <param name="preValidated">flag to indicate that validation has already been done
|
|
/// on this new member. Use only when you can guarantee that the input will not
|
|
/// cause any of the errors normally caught by this method.</param>
|
|
/// <exception cref="ExtendedTypeSystemException">When a member by this name is already present.</exception>
|
|
/// <exception cref="ArgumentException">For invalid arguments.</exception>
|
|
public override void Add(T member, bool preValidated)
|
|
{
|
|
if (member == null)
|
|
{
|
|
throw PSTraceSource.NewArgumentNullException("member");
|
|
}
|
|
|
|
// Save to a local variable to reduce property access.
|
|
var members = Members;
|
|
lock (members)
|
|
{
|
|
if (members[member.Name] is T existingMember)
|
|
{
|
|
Replace(existingMember, member);
|
|
}
|
|
else
|
|
{
|
|
members[member.Name] = member;
|
|
if (member.IsHidden)
|
|
{
|
|
_countHidden++;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Removes a member from this collection.
|
|
/// </summary>
|
|
/// <param name="name">Name of the member to be removed.</param>
|
|
/// <exception cref="ExtendedTypeSystemException">When removing a member with a reserved member name.</exception>
|
|
/// <exception cref="ArgumentException">For invalid arguments.</exception>
|
|
public override void Remove(string name)
|
|
{
|
|
if (string.IsNullOrEmpty(name))
|
|
{
|
|
throw PSTraceSource.NewArgumentException("name");
|
|
}
|
|
|
|
if (IsReservedName(name))
|
|
{
|
|
throw new ExtendedTypeSystemException("PSMemberInfoInternalCollectionRemoveReservedName",
|
|
null,
|
|
ExtendedTypeSystem.ReservedMemberName,
|
|
name);
|
|
}
|
|
|
|
if (_members == null)
|
|
{
|
|
return;
|
|
}
|
|
|
|
lock (_members)
|
|
{
|
|
if (_members[name] is PSMemberInfo member)
|
|
{
|
|
if (member.IsHidden)
|
|
{
|
|
_countHidden--;
|
|
}
|
|
|
|
_members.Remove(name);
|
|
}
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Returns the member in this collection matching name.
|
|
/// </summary>
|
|
/// <param name="name">Name of the member to look for.</param>
|
|
/// <returns>The member matching name.</returns>
|
|
/// <exception cref="ArgumentException">For invalid arguments.</exception>
|
|
public override T this[string name]
|
|
{
|
|
get
|
|
{
|
|
if (string.IsNullOrEmpty(name))
|
|
{
|
|
throw PSTraceSource.NewArgumentException("name");
|
|
}
|
|
|
|
if (_members == null)
|
|
{
|
|
return null;
|
|
}
|
|
|
|
lock (_members)
|
|
{
|
|
return _members[name] as T;
|
|
}
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Returns all members in the collection matching name.
|
|
/// </summary>
|
|
/// <param name="name">Name of the members to be return. May contain wildcard characters.</param>
|
|
/// <returns>All members in the collection matching name.</returns>
|
|
/// <exception cref="ArgumentException">For invalid arguments.</exception>
|
|
public override ReadOnlyPSMemberInfoCollection<T> Match(string name)
|
|
{
|
|
if (string.IsNullOrEmpty(name))
|
|
{
|
|
throw PSTraceSource.NewArgumentException("name");
|
|
}
|
|
|
|
return Match(name, PSMemberTypes.All, MshMemberMatchOptions.None);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Returns all members in the collection matching name and types.
|
|
/// </summary>
|
|
/// <param name="name">Name of the members to be return. May contain wildcard characters.</param>
|
|
/// <param name="memberTypes">Type of the members to be searched.</param>
|
|
/// <returns>All members in the collection matching name and types.</returns>
|
|
/// <exception cref="ArgumentException">For invalid arguments.</exception>
|
|
public override ReadOnlyPSMemberInfoCollection<T> Match(string name, PSMemberTypes memberTypes)
|
|
{
|
|
if (string.IsNullOrEmpty(name))
|
|
{
|
|
throw PSTraceSource.NewArgumentException("name");
|
|
}
|
|
|
|
return Match(name, memberTypes, MshMemberMatchOptions.None);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Returns all members in the collection matching name and types.
|
|
/// </summary>
|
|
/// <param name="name">Name of the members to be return. May contain wildcard characters.</param>
|
|
/// <param name="memberTypes">Type of the members to be searched.</param>
|
|
/// <param name="matchOptions">Match options.</param>
|
|
/// <returns>All members in the collection matching name and types.</returns>
|
|
/// <exception cref="ArgumentException">For invalid arguments.</exception>
|
|
internal override ReadOnlyPSMemberInfoCollection<T> Match(string name, PSMemberTypes memberTypes, MshMemberMatchOptions matchOptions)
|
|
{
|
|
if (string.IsNullOrEmpty(name))
|
|
{
|
|
throw PSTraceSource.NewArgumentException("name");
|
|
}
|
|
|
|
PSMemberInfoInternalCollection<T> internalMembers = GetInternalMembers(matchOptions);
|
|
return new ReadOnlyPSMemberInfoCollection<T>(MemberMatch.Match(internalMembers, name, MemberMatch.GetNamePattern(name), memberTypes));
|
|
}
|
|
|
|
private PSMemberInfoInternalCollection<T> GetInternalMembers(MshMemberMatchOptions matchOptions)
|
|
{
|
|
PSMemberInfoInternalCollection<T> returnValue = new PSMemberInfoInternalCollection<T>();
|
|
|
|
if (_members == null)
|
|
{
|
|
return returnValue;
|
|
}
|
|
|
|
lock (_members)
|
|
{
|
|
foreach (T member in _members.Values.OfType<T>())
|
|
{
|
|
if (member.MatchesOptions(matchOptions))
|
|
{
|
|
returnValue.Add(member);
|
|
}
|
|
}
|
|
}
|
|
|
|
return returnValue;
|
|
}
|
|
|
|
/// <summary>
|
|
/// The number of elements in this collection.
|
|
/// </summary>
|
|
internal int Count
|
|
{
|
|
get
|
|
{
|
|
if (_members == null)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
lock (_members)
|
|
{
|
|
return _members.Count;
|
|
}
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// The number of elements in this collection not marked as Hidden.
|
|
/// </summary>
|
|
internal int VisibleCount
|
|
{
|
|
get
|
|
{
|
|
if (_members == null)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
lock (_members)
|
|
{
|
|
return _members.Count - _countHidden;
|
|
}
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Returns the 0 based member identified by index.
|
|
/// </summary>
|
|
/// <param name="index">Index of the member to retrieve.</param>
|
|
/// <exception cref="ArgumentException">For invalid arguments.</exception>
|
|
internal T this[int index]
|
|
{
|
|
get
|
|
{
|
|
if (_members == null)
|
|
{
|
|
return null;
|
|
}
|
|
|
|
lock (_members)
|
|
{
|
|
return _members[index] as T;
|
|
}
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets the specific enumerator for this collection.
|
|
/// This virtual works around the difficulty of implementing
|
|
/// interfaces virtually.
|
|
/// </summary>
|
|
/// <returns>The enumerator for this collection.</returns>
|
|
public override IEnumerator<T> GetEnumerator()
|
|
{
|
|
if (_members == null)
|
|
{
|
|
return Enumerable.Empty<T>().GetEnumerator();
|
|
}
|
|
|
|
lock (_members)
|
|
{
|
|
// Copy the members to a list so that iteration can be performed without holding a lock.
|
|
return _members.Values.OfType<T>().ToList().GetEnumerator();
|
|
}
|
|
}
|
|
}
|
|
|
|
#region CollectionEntry
|
|
|
|
internal class CollectionEntry<T> where T : PSMemberInfo
|
|
{
|
|
internal delegate PSMemberInfoInternalCollection<T> GetMembersDelegate(PSObject obj);
|
|
|
|
internal delegate T GetMemberDelegate(PSObject obj, string name);
|
|
|
|
internal CollectionEntry(GetMembersDelegate getMembers, GetMemberDelegate getMember,
|
|
bool shouldReplicateWhenReturning, bool shouldCloneWhenReturning, string collectionNameForTracing)
|
|
{
|
|
GetMembers = getMembers;
|
|
GetMember = getMember;
|
|
ShouldReplicateWhenReturning = shouldReplicateWhenReturning;
|
|
ShouldCloneWhenReturning = shouldCloneWhenReturning;
|
|
CollectionNameForTracing = collectionNameForTracing;
|
|
}
|
|
|
|
internal GetMembersDelegate GetMembers { get; }
|
|
|
|
internal GetMemberDelegate GetMember { get; }
|
|
|
|
internal bool ShouldReplicateWhenReturning { get; }
|
|
|
|
internal bool ShouldCloneWhenReturning { get; }
|
|
|
|
internal string CollectionNameForTracing { get; }
|
|
}
|
|
|
|
#endregion CollectionEntry
|
|
|
|
internal static class ReservedNameMembers
|
|
{
|
|
private static object GenerateMemberSet(string name, object obj)
|
|
{
|
|
PSObject mshOwner = PSObject.AsPSObject(obj);
|
|
var memberSet = mshOwner.InstanceMembers[name];
|
|
if (memberSet == null)
|
|
{
|
|
memberSet = new PSInternalMemberSet(name, mshOwner)
|
|
{
|
|
ShouldSerialize = false,
|
|
IsHidden = true,
|
|
IsReservedMember = true
|
|
};
|
|
mshOwner.InstanceMembers.Add(memberSet);
|
|
memberSet.instance = mshOwner;
|
|
}
|
|
|
|
return memberSet;
|
|
}
|
|
|
|
internal static object GeneratePSBaseMemberSet(object obj)
|
|
{
|
|
return GenerateMemberSet(PSObject.BaseObjectMemberSetName, obj);
|
|
}
|
|
|
|
internal static object GeneratePSAdaptedMemberSet(object obj)
|
|
{
|
|
return GenerateMemberSet(PSObject.AdaptedMemberSetName, obj);
|
|
}
|
|
|
|
internal static object GeneratePSObjectMemberSet(object obj)
|
|
{
|
|
return GenerateMemberSet(PSObject.PSObjectMemberSetName, obj);
|
|
}
|
|
|
|
internal static object GeneratePSExtendedMemberSet(object obj)
|
|
{
|
|
PSObject mshOwner = PSObject.AsPSObject(obj);
|
|
var memberSet = mshOwner.InstanceMembers[PSObject.ExtendedMemberSetName];
|
|
if (memberSet == null)
|
|
{
|
|
memberSet = new PSMemberSet(PSObject.ExtendedMemberSetName, mshOwner)
|
|
{
|
|
ShouldSerialize = false,
|
|
IsHidden = true,
|
|
IsReservedMember = true
|
|
};
|
|
memberSet.ReplicateInstance(mshOwner);
|
|
memberSet.instance = mshOwner;
|
|
mshOwner.InstanceMembers.Add(memberSet);
|
|
}
|
|
|
|
return memberSet;
|
|
}
|
|
|
|
// This is the implementation of the PSTypeNames CodeProperty.
|
|
public static Collection<string> PSTypeNames(PSObject o)
|
|
{
|
|
return o.TypeNames;
|
|
}
|
|
|
|
internal static void GeneratePSTypeNames(object obj)
|
|
{
|
|
PSObject mshOwner = PSObject.AsPSObject(obj);
|
|
if (mshOwner.InstanceMembers[PSObject.PSTypeNames] != null)
|
|
{
|
|
// PSTypeNames member set is already generated..just return.
|
|
return;
|
|
}
|
|
|
|
PSCodeProperty codeProperty = new PSCodeProperty(PSObject.PSTypeNames, CachedReflectionInfo.ReservedNameMembers_PSTypeNames)
|
|
{
|
|
ShouldSerialize = false,
|
|
instance = mshOwner,
|
|
IsHidden = true,
|
|
IsReservedMember = true
|
|
};
|
|
mshOwner.InstanceMembers.Add(codeProperty);
|
|
}
|
|
}
|
|
|
|
internal class PSMemberInfoIntegratingCollection<T> : PSMemberInfoCollection<T>, IEnumerable<T> where T : PSMemberInfo
|
|
{
|
|
#region reserved names
|
|
|
|
private void GenerateAllReservedMembers()
|
|
{
|
|
if (!_mshOwner.hasGeneratedReservedMembers)
|
|
{
|
|
_mshOwner.hasGeneratedReservedMembers = true;
|
|
ReservedNameMembers.GeneratePSExtendedMemberSet(_mshOwner);
|
|
ReservedNameMembers.GeneratePSBaseMemberSet(_mshOwner);
|
|
ReservedNameMembers.GeneratePSObjectMemberSet(_mshOwner);
|
|
ReservedNameMembers.GeneratePSAdaptedMemberSet(_mshOwner);
|
|
ReservedNameMembers.GeneratePSTypeNames(_mshOwner);
|
|
}
|
|
}
|
|
|
|
#endregion reserved names
|
|
|
|
#region Constructor, fields and properties
|
|
|
|
internal Collection<CollectionEntry<T>> Collections { get; }
|
|
|
|
private readonly PSObject _mshOwner;
|
|
private readonly PSMemberSet _memberSetOwner;
|
|
|
|
internal PSMemberInfoIntegratingCollection(object owner, Collection<CollectionEntry<T>> collections)
|
|
{
|
|
if (owner == null)
|
|
{
|
|
throw PSTraceSource.NewArgumentNullException("owner");
|
|
}
|
|
|
|
_mshOwner = owner as PSObject;
|
|
_memberSetOwner = owner as PSMemberSet;
|
|
if (_mshOwner == null && _memberSetOwner == null)
|
|
{
|
|
throw PSTraceSource.NewArgumentException("owner");
|
|
}
|
|
|
|
if (collections == null)
|
|
{
|
|
throw PSTraceSource.NewArgumentNullException("collections");
|
|
}
|
|
|
|
Collections = collections;
|
|
}
|
|
|
|
#endregion Constructor, fields and properties
|
|
|
|
#region overrides
|
|
|
|
/// <summary>
|
|
/// Adds member to the collection.
|
|
/// </summary>
|
|
/// <param name="member">Member to be added.</param>
|
|
/// <exception cref="ExtendedTypeSystemException">
|
|
/// When
|
|
/// member is an PSProperty or PSMethod
|
|
/// adding a member to a MemberSet with a reserved name
|
|
/// adding a member with a reserved member name or
|
|
/// adding a member with a type not compatible with this collection
|
|
/// a member with this name already exists
|
|
/// trying to add a member to a static memberset
|
|
/// </exception>
|
|
/// <exception cref="ArgumentException">For invalid arguments.</exception>
|
|
public override void Add(T member)
|
|
{
|
|
Add(member, false);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Adds member to the collection.
|
|
/// </summary>
|
|
/// <param name="member">Member to be added.</param>
|
|
/// <param name="preValidated">flag to indicate that validation has already been done
|
|
/// on this new member. Use only when you can guarantee that the input will not
|
|
/// cause any of the errors normally caught by this method.</param>
|
|
/// <exception cref="ExtendedTypeSystemException">
|
|
/// When
|
|
/// member is an PSProperty or PSMethod
|
|
/// adding a member to a MemberSet with a reserved name
|
|
/// adding a member with a reserved member name or
|
|
/// adding a member with a type not compatible with this collection
|
|
/// a member with this name already exists
|
|
/// trying to add a member to a static memberset
|
|
/// </exception>
|
|
/// <exception cref="ArgumentException">For invalid arguments.</exception>
|
|
public override void Add(T member, bool preValidated)
|
|
{
|
|
if (member == null)
|
|
{
|
|
throw PSTraceSource.NewArgumentNullException("member");
|
|
}
|
|
|
|
if (!preValidated)
|
|
{
|
|
if (member.MemberType == PSMemberTypes.Property || member.MemberType == PSMemberTypes.Method)
|
|
{
|
|
throw new ExtendedTypeSystemException(
|
|
"CannotAddMethodOrProperty",
|
|
null,
|
|
ExtendedTypeSystem.CannotAddPropertyOrMethod);
|
|
}
|
|
|
|
if (_memberSetOwner != null && _memberSetOwner.IsReservedMember)
|
|
{
|
|
throw new ExtendedTypeSystemException("CannotAddToReservedNameMemberset",
|
|
null,
|
|
ExtendedTypeSystem.CannotChangeReservedMember,
|
|
_memberSetOwner.Name);
|
|
}
|
|
}
|
|
|
|
AddToReservedMemberSet(member, preValidated);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Auxiliary to add members from types.xml.
|
|
/// </summary>
|
|
/// <param name="member"></param>
|
|
/// <param name="preValidated"></param>
|
|
internal void AddToReservedMemberSet(T member, bool preValidated)
|
|
{
|
|
if (!preValidated)
|
|
{
|
|
if (_memberSetOwner != null && !_memberSetOwner.IsInstance)
|
|
{
|
|
throw new ExtendedTypeSystemException("RemoveMemberFromStaticMemberSet",
|
|
null,
|
|
ExtendedTypeSystem.ChangeStaticMember,
|
|
member.Name);
|
|
}
|
|
}
|
|
|
|
AddToTypesXmlCache(member, preValidated);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Adds member to the collection.
|
|
/// </summary>
|
|
/// <param name="member">Member to be added.</param>
|
|
/// <param name="preValidated">flag to indicate that validation has already been done
|
|
/// on this new member. Use only when you can guarantee that the input will not
|
|
/// cause any of the errors normally caught by this method.</param>
|
|
/// <exception cref="ExtendedTypeSystemException">
|
|
/// When
|
|
/// adding a member with a reserved member name or
|
|
/// adding a member with a type not compatible with this collection
|
|
/// a member with this name already exists
|
|
/// trying to add a member to a static memberset
|
|
/// </exception>
|
|
/// <exception cref="ArgumentException">For invalid arguments.</exception>
|
|
internal void AddToTypesXmlCache(T member, bool preValidated)
|
|
{
|
|
if (member == null)
|
|
{
|
|
throw PSTraceSource.NewArgumentNullException("member");
|
|
}
|
|
|
|
if (!preValidated)
|
|
{
|
|
if (IsReservedName(member.Name))
|
|
{
|
|
throw new ExtendedTypeSystemException("PSObjectMembersMembersAddReservedName",
|
|
null,
|
|
ExtendedTypeSystem.ReservedMemberName,
|
|
member.Name);
|
|
}
|
|
}
|
|
|
|
PSMemberInfo memberToBeAdded = member.Copy();
|
|
|
|
if (_mshOwner != null)
|
|
{
|
|
if (!preValidated)
|
|
{
|
|
TypeTable typeTable = _mshOwner.GetTypeTable();
|
|
if (typeTable != null)
|
|
{
|
|
var typesXmlMembers = typeTable.GetMembers(_mshOwner.InternalTypeNames);
|
|
var typesXmlMember = typesXmlMembers[member.Name];
|
|
if (typesXmlMember is T)
|
|
{
|
|
throw new ExtendedTypeSystemException(
|
|
"AlreadyPresentInTypesXml",
|
|
null,
|
|
ExtendedTypeSystem.MemberAlreadyPresentFromTypesXml,
|
|
member.Name);
|
|
}
|
|
}
|
|
}
|
|
|
|
memberToBeAdded.ReplicateInstance(_mshOwner);
|
|
_mshOwner.InstanceMembers.Add(memberToBeAdded, preValidated);
|
|
|
|
// All member binders may need to invalidate dynamic sites, and now must generate
|
|
// different binding restrictions (specifically, must check for an instance member
|
|
// before considering the type table or adapted members.)
|
|
PSGetMemberBinder.SetHasInstanceMember(memberToBeAdded.Name);
|
|
PSVariableAssignmentBinder.NoteTypeHasInstanceMemberOrTypeName(PSObject.Base(_mshOwner).GetType());
|
|
|
|
return;
|
|
}
|
|
|
|
_memberSetOwner.InternalMembers.Add(memberToBeAdded, preValidated);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Removes the member named name from the collection.
|
|
/// </summary>
|
|
/// <param name="name">Name of the member to be removed.</param>
|
|
/// <exception cref="ExtendedTypeSystemException">
|
|
/// When trying to remove a member with a type not compatible with this collection
|
|
/// When trying to remove a member from a static memberset
|
|
/// When trying to remove a member from a MemberSet with a reserved name
|
|
/// </exception>
|
|
/// <exception cref="ArgumentException">For invalid arguments.</exception>
|
|
public override void Remove(string name)
|
|
{
|
|
if (string.IsNullOrEmpty(name))
|
|
{
|
|
throw PSTraceSource.NewArgumentException("name");
|
|
}
|
|
|
|
if (_mshOwner != null)
|
|
{
|
|
_mshOwner.InstanceMembers.Remove(name);
|
|
return;
|
|
}
|
|
|
|
if (!_memberSetOwner.IsInstance)
|
|
{
|
|
throw new ExtendedTypeSystemException("AddMemberToStaticMemberSet",
|
|
null,
|
|
ExtendedTypeSystem.ChangeStaticMember,
|
|
name);
|
|
}
|
|
|
|
if (IsReservedName(_memberSetOwner.Name))
|
|
{
|
|
throw new ExtendedTypeSystemException("CannotRemoveFromReservedNameMemberset",
|
|
null,
|
|
ExtendedTypeSystem.CannotChangeReservedMember,
|
|
_memberSetOwner.Name);
|
|
}
|
|
|
|
_memberSetOwner.InternalMembers.Remove(name);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Method which checks if the <paramref name="name"/> is reserved and if so
|
|
/// it will ensure that the particular reserved member is loaded into the
|
|
/// objects member collection.
|
|
///
|
|
/// Caller should ensure that name is not null or empty.
|
|
/// </summary>
|
|
/// <param name="name">
|
|
/// Name of the member to check and load (if needed).
|
|
/// </param>
|
|
private void EnsureReservedMemberIsLoaded(string name)
|
|
{
|
|
Diagnostics.Assert(!string.IsNullOrEmpty(name),
|
|
"Name cannot be null or empty");
|
|
|
|
// Length >= psbase (shortest special member)
|
|
if (name.Length >= 6 && (name[0] == 'p' || name[0] == 'P') && (name[1] == 's' || name[1] == 'S'))
|
|
{
|
|
switch (name.ToLowerInvariant())
|
|
{
|
|
case PSObject.BaseObjectMemberSetName:
|
|
ReservedNameMembers.GeneratePSBaseMemberSet(_mshOwner);
|
|
break;
|
|
case PSObject.AdaptedMemberSetName:
|
|
ReservedNameMembers.GeneratePSAdaptedMemberSet(_mshOwner);
|
|
break;
|
|
case PSObject.ExtendedMemberSetName:
|
|
ReservedNameMembers.GeneratePSExtendedMemberSet(_mshOwner);
|
|
break;
|
|
case PSObject.PSObjectMemberSetName:
|
|
ReservedNameMembers.GeneratePSObjectMemberSet(_mshOwner);
|
|
break;
|
|
case PSObject.PSTypeNames:
|
|
ReservedNameMembers.GeneratePSTypeNames(_mshOwner);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Returns the name corresponding to name or null if it is not present.
|
|
/// </summary>
|
|
/// <param name="name">Name of the member to return.</param>
|
|
/// <exception cref="ArgumentException">For invalid arguments.</exception>
|
|
public override T this[string name]
|
|
{
|
|
get
|
|
{
|
|
using (PSObject.memberResolution.TraceScope("Lookup"))
|
|
{
|
|
if (string.IsNullOrEmpty(name))
|
|
{
|
|
throw PSTraceSource.NewArgumentException("name");
|
|
}
|
|
|
|
PSMemberInfo member;
|
|
object delegateOwner;
|
|
if (_mshOwner != null)
|
|
{
|
|
// this will check if name is a reserved name like PSBase, PSTypeNames
|
|
// if it is a reserved name, ensures the value is loaded.
|
|
EnsureReservedMemberIsLoaded(name);
|
|
delegateOwner = _mshOwner;
|
|
PSMemberInfoInternalCollection<PSMemberInfo> instanceMembers;
|
|
if (PSObject.HasInstanceMembers(_mshOwner, out instanceMembers))
|
|
{
|
|
member = instanceMembers[name];
|
|
if (member is T memberAsT)
|
|
{
|
|
PSObject.memberResolution.WriteLine("Found PSObject instance member: {0}.", name);
|
|
return memberAsT;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
member = _memberSetOwner.InternalMembers[name];
|
|
delegateOwner = _memberSetOwner.instance;
|
|
if (member is T memberAsT)
|
|
{
|
|
// In membersets we cannot replicate the instance when adding
|
|
// since the memberset might not yet have an associated PSObject.
|
|
// We replicate the instance when returning the members of the memberset.
|
|
PSObject.memberResolution.WriteLine("Found PSMemberSet member: {0}.", name);
|
|
member.ReplicateInstance(delegateOwner);
|
|
return memberAsT;
|
|
}
|
|
}
|
|
|
|
if (delegateOwner == null)
|
|
return null;
|
|
|
|
delegateOwner = PSObject.AsPSObject(delegateOwner);
|
|
foreach (CollectionEntry<T> collection in Collections)
|
|
{
|
|
Diagnostics.Assert(delegateOwner != null, "all integrating collections with non empty collections have an associated PSObject");
|
|
T memberAsT = collection.GetMember((PSObject) delegateOwner, name);
|
|
if (memberAsT != null)
|
|
{
|
|
if (collection.ShouldCloneWhenReturning)
|
|
{
|
|
memberAsT = (T) memberAsT.Copy();
|
|
}
|
|
|
|
if (collection.ShouldReplicateWhenReturning)
|
|
{
|
|
memberAsT.ReplicateInstance(delegateOwner);
|
|
}
|
|
|
|
return memberAsT;
|
|
}
|
|
}
|
|
|
|
return null;
|
|
}
|
|
}
|
|
}
|
|
|
|
private PSMemberInfoInternalCollection<T> GetIntegratedMembers(MshMemberMatchOptions matchOptions)
|
|
{
|
|
using (PSObject.memberResolution.TraceScope("Generating the total list of members"))
|
|
{
|
|
PSMemberInfoInternalCollection<T> returnValue = new PSMemberInfoInternalCollection<T>();
|
|
object delegateOwner;
|
|
if (_mshOwner != null)
|
|
{
|
|
delegateOwner = _mshOwner;
|
|
foreach (PSMemberInfo member in _mshOwner.InstanceMembers)
|
|
{
|
|
if (member.MatchesOptions(matchOptions))
|
|
{
|
|
if (member is T memberAsT)
|
|
{
|
|
returnValue.Add(memberAsT);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
delegateOwner = _memberSetOwner.instance;
|
|
foreach (PSMemberInfo member in _memberSetOwner.InternalMembers)
|
|
{
|
|
if (member.MatchesOptions(matchOptions))
|
|
{
|
|
if (member is T memberAsT)
|
|
{
|
|
member.ReplicateInstance(delegateOwner);
|
|
returnValue.Add(memberAsT);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (delegateOwner == null)
|
|
return returnValue;
|
|
|
|
delegateOwner = PSObject.AsPSObject(delegateOwner);
|
|
foreach (CollectionEntry<T> collection in Collections)
|
|
{
|
|
PSMemberInfoInternalCollection<T> members = collection.GetMembers((PSObject) delegateOwner);
|
|
foreach (T member in members)
|
|
{
|
|
PSMemberInfo previousMember = returnValue[member.Name];
|
|
if (previousMember != null)
|
|
{
|
|
PSObject.memberResolution.WriteLine("Member \"{0}\" of type \"{1}\" has been ignored because a member with the same name and type \"{2}\" is already present.",
|
|
member.Name, member.MemberType, previousMember.MemberType);
|
|
continue;
|
|
}
|
|
|
|
if (!member.MatchesOptions(matchOptions))
|
|
{
|
|
PSObject.memberResolution.WriteLine("Skipping hidden member \"{0}\".", member.Name);
|
|
continue;
|
|
}
|
|
|
|
T memberToAdd;
|
|
if (collection.ShouldCloneWhenReturning)
|
|
{
|
|
memberToAdd = (T) member.Copy();
|
|
}
|
|
else
|
|
{
|
|
memberToAdd = member;
|
|
}
|
|
|
|
if (collection.ShouldReplicateWhenReturning)
|
|
{
|
|
memberToAdd.ReplicateInstance(delegateOwner);
|
|
}
|
|
|
|
returnValue.Add(memberToAdd);
|
|
}
|
|
}
|
|
|
|
return returnValue;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Returns all members in the collection matching name.
|
|
/// </summary>
|
|
/// <param name="name">Name of the members to be return. May contain wildcard characters.</param>
|
|
/// <returns>All members in the collection matching name.</returns>
|
|
/// <exception cref="ArgumentException">For invalid arguments.</exception>
|
|
public override ReadOnlyPSMemberInfoCollection<T> Match(string name)
|
|
{
|
|
if (string.IsNullOrEmpty(name))
|
|
{
|
|
throw PSTraceSource.NewArgumentException("name");
|
|
}
|
|
|
|
return Match(name, PSMemberTypes.All, MshMemberMatchOptions.None);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Returns all members in the collection matching name and types.
|
|
/// </summary>
|
|
/// <param name="name">Name of the members to be return. May contain wildcard characters.</param>
|
|
/// <param name="memberTypes">Type of the members to be searched.</param>
|
|
/// <returns>All members in the collection matching name and types.</returns>
|
|
/// <exception cref="ArgumentException">For invalid arguments.</exception>
|
|
public override ReadOnlyPSMemberInfoCollection<T> Match(string name, PSMemberTypes memberTypes)
|
|
{
|
|
if (string.IsNullOrEmpty(name))
|
|
{
|
|
throw PSTraceSource.NewArgumentException("name");
|
|
}
|
|
|
|
return Match(name, memberTypes, MshMemberMatchOptions.None);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Returns all members in the collection matching name and types.
|
|
/// </summary>
|
|
/// <param name="name">Name of the members to be return. May contain wildcard characters.</param>
|
|
/// <param name="memberTypes">Type of the members to be searched.</param>
|
|
/// <param name="matchOptions">Search options.</param>
|
|
/// <returns>All members in the collection matching name and types.</returns>
|
|
/// <exception cref="ArgumentException">For invalid arguments.</exception>
|
|
internal override ReadOnlyPSMemberInfoCollection<T> Match(string name, PSMemberTypes memberTypes, MshMemberMatchOptions matchOptions)
|
|
{
|
|
using (PSObject.memberResolution.TraceScope("Matching \"{0}\"", name))
|
|
{
|
|
if (string.IsNullOrEmpty(name))
|
|
{
|
|
throw PSTraceSource.NewArgumentException("name");
|
|
}
|
|
|
|
if (_mshOwner != null)
|
|
{
|
|
GenerateAllReservedMembers();
|
|
}
|
|
|
|
WildcardPattern nameMatch = MemberMatch.GetNamePattern(name);
|
|
PSMemberInfoInternalCollection<T> allMembers = GetIntegratedMembers(matchOptions);
|
|
ReadOnlyPSMemberInfoCollection<T> returnValue = new ReadOnlyPSMemberInfoCollection<T>(MemberMatch.Match(allMembers, name, nameMatch, memberTypes));
|
|
PSObject.memberResolution.WriteLine("{0} total matches.", returnValue.Count);
|
|
return returnValue;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets the specific enumerator for this collection.
|
|
/// This virtual works around the difficulty of implementing
|
|
/// interfaces virtually.
|
|
/// </summary>
|
|
/// <returns>The enumerator for this collection.</returns>
|
|
public override IEnumerator<T> GetEnumerator()
|
|
{
|
|
return new Enumerator<T>(this);
|
|
}
|
|
|
|
#endregion overrides
|
|
|
|
/// <summary>
|
|
/// Enumerable for this class.
|
|
/// </summary>
|
|
internal struct Enumerator<S> : IEnumerator<S> where S : PSMemberInfo
|
|
{
|
|
private S _current;
|
|
private int _currentIndex;
|
|
private readonly PSMemberInfoInternalCollection<S> _allMembers;
|
|
|
|
/// <summary>
|
|
/// Constructs this instance to enumerate over members.
|
|
/// </summary>
|
|
/// <param name="integratingCollection">Members we are enumerating.</param>
|
|
internal Enumerator(PSMemberInfoIntegratingCollection<S> integratingCollection)
|
|
{
|
|
using (PSObject.memberResolution.TraceScope("Enumeration Start"))
|
|
{
|
|
_currentIndex = -1;
|
|
_current = null;
|
|
_allMembers = integratingCollection.GetIntegratedMembers(MshMemberMatchOptions.None);
|
|
if (integratingCollection._mshOwner != null)
|
|
{
|
|
integratingCollection.GenerateAllReservedMembers();
|
|
PSObject.memberResolution.WriteLine("Enumerating PSObject with type \"{0}\".", integratingCollection._mshOwner.ImmediateBaseObject.GetType().FullName);
|
|
PSObject.memberResolution.WriteLine("PSObject instance members: {0}", _allMembers.VisibleCount);
|
|
}
|
|
else
|
|
{
|
|
PSObject.memberResolution.WriteLine("Enumerating PSMemberSet \"{0}\".", integratingCollection._memberSetOwner.Name);
|
|
PSObject.memberResolution.WriteLine("MemberSet instance members: {0}", _allMembers.VisibleCount);
|
|
}
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Moves to the next element in the enumeration.
|
|
/// </summary>
|
|
/// <returns>
|
|
/// false if there are no more elements to enumerate
|
|
/// true otherwise
|
|
/// </returns>
|
|
public bool MoveNext()
|
|
{
|
|
_currentIndex++;
|
|
|
|
S member = null;
|
|
while (_currentIndex < _allMembers.Count)
|
|
{
|
|
member = _allMembers[_currentIndex];
|
|
if (!member.IsHidden)
|
|
{
|
|
break;
|
|
}
|
|
|
|
_currentIndex++;
|
|
}
|
|
|
|
if (_currentIndex < _allMembers.Count)
|
|
{
|
|
_current = member;
|
|
return true;
|
|
}
|
|
|
|
_current = null;
|
|
return false;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Current PSMemberInfo in the enumeration.
|
|
/// </summary>
|
|
/// <exception cref="ArgumentException">For invalid arguments.</exception>
|
|
S IEnumerator<S>.Current
|
|
{
|
|
get
|
|
{
|
|
if (_currentIndex == -1)
|
|
{
|
|
throw PSTraceSource.NewInvalidOperationException();
|
|
}
|
|
|
|
return _current;
|
|
}
|
|
}
|
|
|
|
object IEnumerator.Current => ((IEnumerator<S>) this).Current;
|
|
|
|
void IEnumerator.Reset()
|
|
{
|
|
_currentIndex = -1;
|
|
_current = null;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Not supported.
|
|
/// </summary>
|
|
public void Dispose()
|
|
{
|
|
}
|
|
}
|
|
}
|
|
|
|
#endregion Member collection classes and its auxiliary classes
|
|
}
|
|
|
|
#pragma warning restore 56503
|