diff --git a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/CustomSerialization.cs b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/CustomSerialization.cs index 0553ee9dbf..b286d645c2 100644 --- a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/CustomSerialization.cs +++ b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/CustomSerialization.cs @@ -342,7 +342,7 @@ namespace System.Management.Automation bool sourceHandled = false; PSObject moSource = source as PSObject; - if (moSource != null && !moSource.immediateBaseObjectIsEmpty) + if (moSource != null && !moSource.ImmediateBaseObjectIsEmpty) { // Check if baseObject is primitive known type object baseObject = moSource.ImmediateBaseObject; @@ -367,7 +367,7 @@ namespace System.Management.Automation IDictionary dictionary = null; // If passed in object is PSObject with no baseobject, return false. - if (mshSource != null && mshSource.immediateBaseObjectIsEmpty) + if (mshSource != null && mshSource.ImmediateBaseObjectIsEmpty) { return false; } @@ -411,7 +411,7 @@ namespace System.Management.Automation // We serialize properties of enumerable and on deserialization mark the object // as Deserialized. So if object is marked deserialized, we should write properties. // Note: we do not serialize the properties of IEnumerable if depth is zero. - if (depth != 0 && (ct == ContainerType.Enumerable || (mshSource != null && mshSource.isDeserialized))) + if (depth != 0 && (ct == ContainerType.Enumerable || (mshSource != null && mshSource.IsDeserialized))) { // Note:Depth is the depth for serialization of baseObject. // Depth for serialization of each property is one less. @@ -595,7 +595,7 @@ namespace System.Management.Automation bool isEnum = false; bool isPSObject = false; - if (!source.immediateBaseObjectIsEmpty) + if (!source.ImmediateBaseObjectIsEmpty) { isEnum = source.ImmediateBaseObject is Enum; isPSObject = source.ImmediateBaseObject is PSObject; diff --git a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/GetMember.cs b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/GetMember.cs index 4ba3536118..a40ccf798b 100644 --- a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/GetMember.cs +++ b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/GetMember.cs @@ -150,7 +150,7 @@ namespace Microsoft.PowerShell.Commands Adapter staticAdapter = null; if (this.Static == true) { - staticAdapter = PSObject.dotNetStaticAdapter; + staticAdapter = PSObject.DotNetStaticAdapter; object baseObject = this.InputObject.BaseObject; baseObjectAsType = baseObject as System.Type ?? baseObject.GetType(); typeName = baseObjectAsType.FullName; diff --git a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/WebCmdlet/JsonObject.cs b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/WebCmdlet/JsonObject.cs index 4ac980430b..8e9d63eb34 100644 --- a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/WebCmdlet/JsonObject.cs +++ b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/WebCmdlet/JsonObject.cs @@ -551,7 +551,7 @@ namespace Microsoft.PowerShell.Commands { if (currentDepth > context.MaxDepth) { - if (pso != null && pso.immediateBaseObjectIsEmpty) + if (pso != null && pso.ImmediateBaseObjectIsEmpty) { // The obj is a pure PSObject, we convert the original PSObject to a string, // instead of its base object in this case diff --git a/src/Microsoft.PowerShell.ConsoleHost/host/msh/ConsoleHost.cs b/src/Microsoft.PowerShell.ConsoleHost/host/msh/ConsoleHost.cs index c7113f3f1b..7f8ea16553 100644 --- a/src/Microsoft.PowerShell.ConsoleHost/host/msh/ConsoleHost.cs +++ b/src/Microsoft.PowerShell.ConsoleHost/host/msh/ConsoleHost.cs @@ -1981,9 +1981,10 @@ namespace Microsoft.PowerShell error = (object)new ErrorRecord(e, "ConsoleHost.ReportException", ErrorCategory.NotSpecified, null); } - PSObject wrappedError = new PSObject(error); - PSNoteProperty note = new PSNoteProperty("writeErrorStream", true); - wrappedError.Properties.Add(note); + PSObject wrappedError = new PSObject(error) + { + WriteStream = WriteStreamType.Error + }; Exception e1 = null; diff --git a/src/System.Management.Automation/FormatAndOutput/common/BaseFormattingCommand.cs b/src/System.Management.Automation/FormatAndOutput/common/BaseFormattingCommand.cs index e95aa8da32..396fb327b8 100644 --- a/src/System.Management.Automation/FormatAndOutput/common/BaseFormattingCommand.cs +++ b/src/System.Management.Automation/FormatAndOutput/common/BaseFormattingCommand.cs @@ -463,7 +463,7 @@ namespace Microsoft.PowerShell.Commands.Internal.Format { Diagnostics.Assert(so != null, "object so cannot be null"); FormatEntryData fed = _viewManager.ViewGenerator.GeneratePayload(so, _enumerationLimit); - fed.SetStreamTypeFromPSObject(so); + fed.writeStream = so.WriteStream; this.WriteObject(fed); List errors = _viewManager.ViewGenerator.ErrorManager.DrainFailedResultList(); diff --git a/src/System.Management.Automation/FormatAndOutput/common/FormatViewManager.cs b/src/System.Management.Automation/FormatAndOutput/common/FormatViewManager.cs index baed478cc5..9f2de82a3a 100644 --- a/src/System.Management.Automation/FormatAndOutput/common/FormatViewManager.cs +++ b/src/System.Management.Automation/FormatAndOutput/common/FormatViewManager.cs @@ -464,66 +464,20 @@ namespace Microsoft.PowerShell.Commands.Internal.Format /// internal static class OutOfBandFormatViewManager { - internal static bool IsPropertyLessObject(PSObject so) + private static bool IsNotRemotingProperty(string name) { - List allProperties = AssociationManager.ExpandAll(so); - - if (allProperties.Count == 0) - { - return true; - } - - if (allProperties.Count == 3) - { - foreach (MshResolvedExpressionParameterAssociation property in allProperties) - { - if (!property.ResolvedExpression.ToString().Equals(RemotingConstants.ComputerNameNoteProperty, StringComparison.OrdinalIgnoreCase) && - !property.ResolvedExpression.ToString().Equals(RemotingConstants.ShowComputerNameNoteProperty, StringComparison.OrdinalIgnoreCase) && - !property.ResolvedExpression.ToString().Equals(RemotingConstants.RunspaceIdNoteProperty, StringComparison.OrdinalIgnoreCase)) - { - return false; - } - } - - return true; - } - - if (allProperties.Count == 4) - { - foreach (MshResolvedExpressionParameterAssociation property in allProperties) - { - if (!property.ResolvedExpression.ToString().Equals(RemotingConstants.ComputerNameNoteProperty, StringComparison.OrdinalIgnoreCase) && - !property.ResolvedExpression.ToString().Equals(RemotingConstants.ShowComputerNameNoteProperty, StringComparison.OrdinalIgnoreCase) && - !property.ResolvedExpression.ToString().Equals(RemotingConstants.RunspaceIdNoteProperty, StringComparison.OrdinalIgnoreCase) - && !property.ResolvedExpression.ToString().Equals(RemotingConstants.SourceJobInstanceId, StringComparison.OrdinalIgnoreCase)) - { - return false; - } - } - - return true; - } - - if (allProperties.Count == 5) - { - foreach (MshResolvedExpressionParameterAssociation property in allProperties) - { - if (!property.ResolvedExpression.ToString().Equals(RemotingConstants.ComputerNameNoteProperty, StringComparison.OrdinalIgnoreCase) && - !property.ResolvedExpression.ToString().Equals(RemotingConstants.ShowComputerNameNoteProperty, StringComparison.OrdinalIgnoreCase) && - !property.ResolvedExpression.ToString().Equals(RemotingConstants.RunspaceIdNoteProperty, StringComparison.OrdinalIgnoreCase) && - !property.ResolvedExpression.ToString().Equals(RemotingConstants.SourceJobInstanceId, StringComparison.OrdinalIgnoreCase) && - !property.ResolvedExpression.ToString().Equals(RemotingConstants.SourceLength, StringComparison.OrdinalIgnoreCase)) - { - return false; - } - } - - return true; - } - - return false; + var isRemotingPropertyName = name.Equals(RemotingConstants.ComputerNameNoteProperty, StringComparison.OrdinalIgnoreCase) + || name.Equals(RemotingConstants.ShowComputerNameNoteProperty, StringComparison.OrdinalIgnoreCase) + || name.Equals(RemotingConstants.RunspaceIdNoteProperty, StringComparison.OrdinalIgnoreCase) + || name.Equals(RemotingConstants.SourceJobInstanceId, StringComparison.OrdinalIgnoreCase) + || name.Equals(RemotingConstants.SourceLength, StringComparison.OrdinalIgnoreCase); + return !isRemotingPropertyName; } + private static readonly MemberNamePredicate NameIsNotRemotingProperty = IsNotRemotingProperty; + + private static bool HasNonRemotingProperties(PSObject so) => so.GetFirstPropertyOrDefault(NameIsNotRemotingProperty) != null; + internal static FormatEntryData GenerateOutOfBandData(TerminatingErrorContext errorContext, PSPropertyExpressionFactory expressionFactory, TypeInfoDataBase db, PSObject so, int enumerationLimit, bool useToStringFallback, out List errors) { @@ -549,8 +503,8 @@ namespace Microsoft.PowerShell.Commands.Internal.Format } else { - if (DefaultScalarTypes.IsTypeInList(typeNames) || - IsPropertyLessObject(so)) + if (DefaultScalarTypes.IsTypeInList(typeNames) + || !HasNonRemotingProperties(so)) { // we force a ToString() on well known types return GenerateOutOfBandObjectAsToString(so); @@ -575,7 +529,7 @@ namespace Microsoft.PowerShell.Commands.Internal.Format FormatEntryData fed = outOfBandViewGenerator.GeneratePayload(so, enumerationLimit); fed.outOfBand = true; - fed.SetStreamTypeFromPSObject(so); + fed.writeStream = so.WriteStream; errors = outOfBandViewGenerator.ErrorManager.DrainFailedResultList(); diff --git a/src/System.Management.Automation/FormatAndOutput/common/FormattingObjects.cs b/src/System.Management.Automation/FormatAndOutput/common/FormattingObjects.cs index 6b611d2255..bc9cc0565a 100644 --- a/src/System.Management.Automation/FormatAndOutput/common/FormattingObjects.cs +++ b/src/System.Management.Automation/FormatAndOutput/common/FormattingObjects.cs @@ -23,6 +23,7 @@ // using System.Collections.Generic; +using System.Management.Automation; namespace Microsoft.PowerShell.Commands.Internal.Format { @@ -149,40 +150,6 @@ namespace Microsoft.PowerShell.Commands.Internal.Format public bool outOfBand = false; public WriteStreamType writeStream = WriteStreamType.None; internal bool isHelpObject = false; - - /// - /// Helper method to set the WriteStreamType property - /// based on note properties of a PSObject object. - /// - /// PSObject. - internal void SetStreamTypeFromPSObject( - System.Management.Automation.PSObject so) - { - if (PSObjectHelper.IsWriteErrorStream(so)) - { - writeStream = WriteStreamType.Error; - } - else if (PSObjectHelper.IsWriteWarningStream(so)) - { - writeStream = WriteStreamType.Warning; - } - else if (PSObjectHelper.IsWriteVerboseStream(so)) - { - writeStream = WriteStreamType.Verbose; - } - else if (PSObjectHelper.IsWriteDebugStream(so)) - { - writeStream = WriteStreamType.Debug; - } - else if (PSObjectHelper.IsWriteInformationStream(so)) - { - writeStream = WriteStreamType.Information; - } - else - { - writeStream = WriteStreamType.None; - } - } } #endregion diff --git a/src/System.Management.Automation/FormatAndOutput/common/ILineOutput.cs b/src/System.Management.Automation/FormatAndOutput/common/ILineOutput.cs index d4534a18fd..b8db88610c 100644 --- a/src/System.Management.Automation/FormatAndOutput/common/ILineOutput.cs +++ b/src/System.Management.Automation/FormatAndOutput/common/ILineOutput.cs @@ -109,19 +109,6 @@ namespace Microsoft.PowerShell.Commands.Internal.Format } - /// - /// Specifies special stream write processing. - /// - internal enum WriteStreamType - { - None, - Error, - Warning, - Verbose, - Debug, - Information - } - /// /// Base class providing information about the screen device capabilities /// and used to write the output strings to the text output device. diff --git a/src/System.Management.Automation/FormatAndOutput/common/Utilities/MshObjectUtil.cs b/src/System.Management.Automation/FormatAndOutput/common/Utilities/MshObjectUtil.cs index e201f2479c..1877677e58 100644 --- a/src/System.Management.Automation/FormatAndOutput/common/Utilities/MshObjectUtil.cs +++ b/src/System.Management.Automation/FormatAndOutput/common/Utilities/MshObjectUtil.cs @@ -35,81 +35,6 @@ namespace Microsoft.PowerShell.Commands.Internal.Format return string.Equals(typeNames[1], "System.Enum", StringComparison.Ordinal); } - /// - /// WriteError adds a note property called WriteErrorStream to the error - /// record wrapped in an PSObject and set its value to true. When F and O detects - /// this note exists and its value is set to true, WriteErrorLine will be used - /// to emit the error; otherwise, F and O actions are regular. - /// - /// - /// - internal static bool IsWriteErrorStream(PSObject so) - { - return IsStreamType(so, "WriteErrorStream"); - } - - /// - /// Checks for WriteWarningStream property on object, indicating that - /// it is a warning stream. Used by F and O. - /// - /// - /// - internal static bool IsWriteWarningStream(PSObject so) - { - return IsStreamType(so, "WriteWarningStream"); - } - - /// - /// Checks for WriteVerboseStream property on object, indicating that - /// it is a verbose stream. Used by F and O. - /// - /// - /// - internal static bool IsWriteVerboseStream(PSObject so) - { - return IsStreamType(so, "WriteVerboseStream"); - } - - /// - /// Checks for WriteDebugStream property on object, indicating that - /// it is a debug stream. Used by F and O. - /// - /// - /// - internal static bool IsWriteDebugStream(PSObject so) - { - return IsStreamType(so, "WriteDebugStream"); - } - - /// - /// Checks for WriteInformationStream property on object, indicating that - /// it is an informational stream. Used by F and O. - /// - /// - /// - internal static bool IsWriteInformationStream(PSObject so) - { - return IsStreamType(so, "WriteInformationStream"); - } - - internal static bool IsStreamType(PSObject so, string streamFlag) - { - try - { - PSPropertyInfo streamProperty = so.Properties[streamFlag]; - if (streamProperty != null && streamProperty.Value is bool) - { - return (bool)streamProperty.Value; - } - - return false; - } - catch (ExtendedTypeSystemException) - { - return false; - } - } - /// /// Retrieve the display name. It looks for a well known property and, /// if not found, it uses some heuristics to get a "close" match. diff --git a/src/System.Management.Automation/cimSupport/other/ciminstancetypeadapter.cs b/src/System.Management.Automation/cimSupport/other/ciminstancetypeadapter.cs index bc41b2d56c..0fe0569261 100644 --- a/src/System.Management.Automation/cimSupport/other/ciminstancetypeadapter.cs +++ b/src/System.Management.Automation/cimSupport/other/ciminstancetypeadapter.cs @@ -133,6 +133,44 @@ namespace Microsoft.PowerShell.Cim return null; } + /// + public override PSAdaptedProperty GetFirstPropertyOrDefault(object baseObject, MemberNamePredicate predicate) + { + if (predicate == null) + { + throw new PSArgumentNullException(nameof(predicate)); + } + + // baseObject should never be null + CimInstance cimInstance = baseObject as CimInstance; + if (cimInstance == null) + { + string msg = string.Format( + CultureInfo.InvariantCulture, + CimInstanceTypeAdapterResources.BaseObjectNotCimInstance, + "baseObject", + typeof(CimInstance).ToString()); + throw new PSInvalidOperationException(msg); + } + + if (predicate(RemotingConstants.ComputerNameNoteProperty)) + { + PSAdaptedProperty prop = GetPSComputerNameAdapter(cimInstance); + return prop; + } + + foreach (CimProperty cimProperty in cimInstance.CimInstanceProperties) + { + if (cimProperty != null && predicate(cimProperty.Name)) + { + PSAdaptedProperty prop = GetCimPropertyAdapter(cimProperty, baseObject, cimProperty.Name); + return prop; + } + } + + return null; + } + internal static string CimTypeToTypeNameDisplayString(CimType cimType) { switch (cimType) diff --git a/src/System.Management.Automation/engine/COM/ComAdapter.cs b/src/System.Management.Automation/engine/COM/ComAdapter.cs index afee12b539..b4548246fb 100644 --- a/src/System.Management.Automation/engine/COM/ComAdapter.cs +++ b/src/System.Management.Automation/engine/COM/ComAdapter.cs @@ -84,6 +84,48 @@ namespace System.Management.Automation return null; } + /// + /// Returns the first PSMemberInfo whose name matches the specified . + /// + protected override T GetFirstMemberOrDefault(object obj, MemberNamePredicate predicate) + { + bool lookingForProperties = typeof(T).IsAssignableFrom(typeof(PSProperty)); + bool lookingForParameterizedProperties = typeof(T).IsAssignableFrom(typeof(PSParameterizedProperty)); + if (lookingForProperties || lookingForParameterizedProperties) + { + foreach (ComProperty prop in _comTypeInfo.Properties.Values) + { + if (prop.IsParameterized + && lookingForParameterizedProperties + && predicate(prop.Name)) + { + return new PSParameterizedProperty(prop.Name, this, obj, prop) as T; + } + + if (lookingForProperties && predicate(prop.Name)) + { + return new PSProperty(prop.Name, this, obj, prop) as T; + } + } + } + + bool lookingForMethods = typeof(T).IsAssignableFrom(typeof(PSMethod)); + + if (lookingForMethods) + { + foreach (ComMethod method in _comTypeInfo.Methods.Values) + { + if (predicate(method.Name)) + { + var mshMethod = new PSMethod(method.Name, this, obj, method); + return mshMethod as T; + } + } + } + + return null; + } + /// /// Retrieves all the members available in the object. /// The adapter implementation is encouraged to cache all properties/methods available diff --git a/src/System.Management.Automation/engine/CommandCompletion/CompletionCompleters.cs b/src/System.Management.Automation/engine/CommandCompletion/CompletionCompleters.cs index b340f8ac12..d117e09428 100644 --- a/src/System.Management.Automation/engine/CommandCompletion/CompletionCompleters.cs +++ b/src/System.Management.Automation/engine/CommandCompletion/CompletionCompleters.cs @@ -6564,7 +6564,7 @@ namespace System.Management.Automation return; } - members = PSObject.dotNetStaticAdapter.BaseGetMembers(type); + members = PSObject.DotNetStaticAdapter.BaseGetMembers(type); } else { diff --git a/src/System.Management.Automation/engine/CoreAdapter.cs b/src/System.Management.Automation/engine/CoreAdapter.cs index f2e745e4b4..119557dd79 100644 --- a/src/System.Management.Automation/engine/CoreAdapter.cs +++ b/src/System.Management.Automation/engine/CoreAdapter.cs @@ -89,6 +89,15 @@ namespace System.Management.Automation /// The PSMemberInfo corresponding to memberName from obj. protected abstract T GetMember(object obj, string memberName) where T : PSMemberInfo; + /// + /// Returns the first PSMemberInfo whose name matches the specified . + /// Otherwise, return null. + /// + /// Object to retrieve the PSMemberInfo from. + /// The predicate to find the matching member. + /// The PSMemberInfo corresponding to the predicate match. + protected abstract T GetFirstMemberOrDefault(object obj, MemberNamePredicate predicate) where T : PSMemberInfo; + /// /// Retrieves all the members available in the object. /// The adapter implementation is encouraged to cache all properties/methods available @@ -212,6 +221,7 @@ namespace System.Management.Automation #endregion method #region parameterized property + /// /// Returns the name of the type corresponding to the property's value. /// @@ -326,8 +336,12 @@ namespace System.Management.Automation #region private - private static Exception NewException(Exception e, string errorId, - string targetErrorId, string resourceString, params object[] parameters) + private static Exception NewException( + Exception e, + string errorId, + string targetErrorId, + string resourceString, + params object[] parameters) { object[] newParameters = new object[parameters.Length + 1]; for (int i = 0; i < parameters.Length; i++) @@ -356,6 +370,7 @@ namespace System.Management.Automation #endregion private #region member + internal ConsolidatedString BaseGetTypeNameHierarchy(object obj) { try @@ -384,6 +399,20 @@ namespace System.Management.Automation } } + internal T BaseGetFirstMemberOrDefault(object obj, MemberNamePredicate predicate) where T : PSMemberInfo + { + try + { + return this.GetFirstMemberOrDefault(obj, predicate); + } + catch (ExtendedTypeSystemException) { throw; } + catch (Exception e) + { + throw NewException(e, "CatchFromBaseGetMember", "CatchFromBaseGetMemberTI", + ExtendedTypeSystem.ExceptionGettingMember, nameof(predicate)); + } + } + internal PSMemberInfoInternalCollection BaseGetMembers(object obj) where T : PSMemberInfo { try @@ -1537,7 +1566,7 @@ namespace System.Management.Automation internal static void SetReferences(object[] arguments, MethodInformation methodInformation, object[] originalArguments) { - using (PSObject.memberResolution.TraceScope("Checking for possible references.")) + using (PSObject.MemberResolution.TraceScope("Checking for possible references.")) { ParameterInformation[] parameters = methodInformation.parameters; for (int i = 0; (i < originalArguments.Length) && (i < parameters.Length) && (i < arguments.Length); i++) @@ -1567,7 +1596,7 @@ namespace System.Management.Automation } object argument = arguments[i]; - PSObject.memberResolution.WriteLine("Argument '{0}' was a reference so it will be set to \"{1}\".", i + 1, argument); + PSObject.MemberResolution.WriteLine("Argument '{0}' was a reference so it will be set to \"{1}\".", i + 1, argument); originalArgumentReference.Value = argument; } } @@ -1741,7 +1770,7 @@ namespace System.Management.Automation bool isParameterByRef, int parameterIndex, Type resultType, IFormatProvider formatProvider) { - using (PSObject.memberResolution.TraceScope("Method argument conversion.")) + using (PSObject.MemberResolution.TraceScope("Method argument conversion.")) { if (resultType == null) { @@ -1772,7 +1801,7 @@ namespace System.Management.Automation PSReference reference = obj as PSReference; if (reference != null) { - PSObject.memberResolution.WriteLine("Parameter was a reference."); + PSObject.MemberResolution.WriteLine("Parameter was a reference."); isArgumentByRef = true; return reference.Value; } @@ -1785,7 +1814,7 @@ namespace System.Management.Automation if (reference != null) { - PSObject.memberResolution.WriteLine("Parameter was an PSObject containing a reference."); + PSObject.MemberResolution.WriteLine("Parameter was an PSObject containing a reference."); isArgumentByRef = true; return reference.Value; } @@ -1796,7 +1825,7 @@ namespace System.Management.Automation internal static object PropertySetAndMethodArgumentConvertTo(object valueToConvert, Type resultType, IFormatProvider formatProvider) { - using (PSObject.memberResolution.TraceScope("Converting parameter \"{0}\" to \"{1}\".", valueToConvert, resultType)) + using (PSObject.MemberResolution.TraceScope("Converting parameter \"{0}\" to \"{1}\".", valueToConvert, resultType)) { if (resultType == null) { @@ -1808,7 +1837,7 @@ namespace System.Management.Automation { if (resultType == typeof(object)) { - PSObject.memberResolution.WriteLine("Parameter was an PSObject and will be converted to System.Object."); + PSObject.MemberResolution.WriteLine("Parameter was an PSObject and will be converted to System.Object."); // we use PSObject.Base so we don't return // PSCustomObject return PSObject.Base(mshObj); @@ -1904,6 +1933,19 @@ namespace System.Management.Automation return this.memberCollection[indexObj]; } } + + internal object GetFirstOrDefault(MemberNamePredicate predicate) + { + foreach (var entry in _indexes) + { + if (predicate(entry.Key)) + { + return this.memberCollection[entry.Value]; + } + } + + return null; + } } /// @@ -3433,7 +3475,7 @@ namespace System.Management.Automation return t == typeof(PSMemberInfo) || t == typeof(PSParameterizedProperty); } - internal T GetDotNetProperty(object obj, string propertyName) where T : PSMemberInfo + private T GetDotNetPropertyImpl(object obj, string propertyName, MemberNamePredicate predicate) where T : PSMemberInfo { bool lookingForProperties = typeof(T).IsAssignableFrom(typeof(PSProperty)); bool lookingForParameterizedProperties = IsTypeParameterizedProperty(typeof(T)); @@ -3443,36 +3485,31 @@ namespace System.Management.Automation } CacheTable typeTable = _isStatic - ? GetStaticPropertyReflectionTable((Type)obj) - : GetInstancePropertyReflectionTable(obj.GetType()); + ? GetStaticPropertyReflectionTable((Type)obj) + : GetInstancePropertyReflectionTable(obj.GetType()); - object entry = typeTable[propertyName]; - if (entry == null) + object entry = predicate != null + ? typeTable.GetFirstOrDefault(predicate) + : typeTable[propertyName]; + switch (entry) { - return null; - } + case null: return null; + case PropertyCacheEntry cacheEntry when lookingForProperties: + { + var isHidden = cacheEntry.member.GetCustomAttributes(typeof(HiddenAttribute), false).Any(); + return new PSProperty(cacheEntry.member.Name, this, obj, cacheEntry) { IsHidden = isHidden } as T; + } + case ParameterizedPropertyCacheEntry paramCacheEntry when lookingForParameterizedProperties: - var propertyEntry = entry as PropertyCacheEntry; - if (propertyEntry != null && lookingForProperties) - { - var isHidden = propertyEntry.member.GetCustomAttributes(typeof(HiddenAttribute), false).Any(); - return new PSProperty(propertyEntry.member.Name, this, obj, propertyEntry) { IsHidden = isHidden } as T; + // TODO: check for HiddenAttribute + // We can't currently write a parameterized property in a PowerShell class so this isn't too important, + // but if someone added the attribute to their C#, it'd be good to set isHidden correctly here. + return new PSParameterizedProperty(paramCacheEntry.propertyName, this, obj, paramCacheEntry) as T; + default: return null; } - - var parameterizedPropertyEntry = entry as ParameterizedPropertyCacheEntry; - if (parameterizedPropertyEntry != null && lookingForParameterizedProperties) - { - // TODO: check for HiddenAttribute - // We can't currently write a parameterized property in a PowerShell class so this isn't too important, - // but if someone added the attribute to their C#, it'd be good to set isHidden correctly here. - return new PSParameterizedProperty(parameterizedPropertyEntry.propertyName, - this, obj, parameterizedPropertyEntry) as T; - } - - return null; } - internal T GetDotNetMethod(object obj, string methodName) where T : PSMemberInfo + private T GetDotNetMethodImpl(object obj, string methodName, MemberNamePredicate predicate) where T : PSMemberInfo { if (!typeof(T).IsAssignableFrom(typeof(PSMethod))) { @@ -3480,10 +3517,13 @@ namespace System.Management.Automation } CacheTable typeTable = _isStatic - ? GetStaticMethodReflectionTable((Type)obj) - : GetInstanceMethodReflectionTable(obj.GetType()); + ? GetStaticMethodReflectionTable((Type)obj) + : GetInstanceMethodReflectionTable(obj.GetType()); + + var methods = predicate != null + ? (MethodCacheEntry)typeTable.GetFirstOrDefault(predicate) + : (MethodCacheEntry)typeTable[methodName]; - var methods = (MethodCacheEntry)typeTable[methodName]; if (methods == null) { return null; @@ -3504,6 +3544,72 @@ namespace System.Management.Automation return PSMethod.Create(methods[0].method.Name, this, obj, methods, isSpecial, isHidden) as T; } + internal T GetDotNetProperty(object obj, string propertyName) where T : PSMemberInfo + { + return GetDotNetPropertyImpl(obj, propertyName, predicate: null); + } + + internal T GetDotNetMethod(object obj, string methodName) where T : PSMemberInfo + { + return GetDotNetMethodImpl(obj, methodName, predicate: null); + } + + protected T GetFirstDotNetPropertyOrDefault(object obj, MemberNamePredicate predicate) where T : PSMemberInfo + { + return GetDotNetPropertyImpl(obj, propertyName: null, predicate); + } + + protected T GetFirstDotNetMethodOrDefault(object obj, MemberNamePredicate predicate) where T : PSMemberInfo + { + return GetDotNetMethodImpl(obj, methodName: null, predicate); + } + + protected T GetFirstDotNetEventOrDefault(object obj, MemberNamePredicate predicate) where T : PSMemberInfo + { + if (!typeof(T).IsAssignableFrom(typeof(PSEvent))) + { + return null; + } + + var table = _isStatic + ? GetStaticEventReflectionTable((Type)obj) + : GetInstanceEventReflectionTable(obj.GetType()); + + foreach (var psEvent in table.Values) + { + if (predicate(psEvent.events[0].Name)) + { + return new PSEvent(psEvent.events[0]) as T; + } + } + + return null; + } + + protected T GetFirstDynamicMemberOrDefault(object obj, MemberNamePredicate predicate) where T : PSMemberInfo + { + var idmop = obj as IDynamicMetaObjectProvider; + if (idmop == null || obj is PSObject) + { + return null; + } + + if (!typeof(T).IsAssignableFrom(typeof(PSDynamicMember))) + { + return null; + } + + foreach (var name in idmop.GetMetaObject(Expression.Variable(idmop.GetType())).GetDynamicMemberNames()) + { + if (predicate(name)) + { + return new PSDynamicMember(name) as T; + } + } + + return null; + } + internal void AddAllProperties(object obj, PSMemberInfoInternalCollection members, bool ignoreDuplicates) where T : PSMemberInfo { bool lookingForProperties = typeof(T).IsAssignableFrom(typeof(PSProperty)); @@ -3519,8 +3625,7 @@ namespace System.Management.Automation for (int i = 0; i < table.memberCollection.Count; i++) { - var propertyEntry = table.memberCollection[i] as PropertyCacheEntry; - if (propertyEntry != null) + if (table.memberCollection[i] is PropertyCacheEntry propertyEntry) { if (lookingForProperties) { @@ -3676,6 +3781,15 @@ namespace System.Management.Automation return GetDotNetMethod(obj, memberName); } + /// + /// Get the first .NET member whose name matches the specified . + /// + protected override T GetFirstMemberOrDefault(object obj, MemberNamePredicate predicate) + { + return GetFirstDotNetPropertyOrDefault(obj, predicate) ?? GetFirstDotNetMethodOrDefault(obj, predicate) ?? + GetFirstDotNetEventOrDefault(obj, predicate) ?? GetFirstDynamicMemberOrDefault(obj, predicate); + } + /// /// Retrieves all the members available in the object. /// The adapter implementation is encouraged to cache all properties/methods available @@ -4061,7 +4175,7 @@ namespace System.Management.Automation string methodDefinition = bestMethod.methodDefinition; ScriptTrace.Trace(1, "TraceMethodCall", ParserStrings.TraceMethodCall, methodDefinition); - PSObject.memberResolution.WriteLine("Calling Method: {0}", methodDefinition); + PSObject.MemberResolution.WriteLine("Calling Method: {0}", methodDefinition); return AuxiliaryMethodInvoke(target, newArguments, bestMethod, arguments); } @@ -4084,9 +4198,9 @@ namespace System.Management.Automation private static object InvokeResolvedConstructor(MethodInformation bestMethod, object[] newArguments, object[] arguments) { - if ((PSObject.memberResolution.Options & PSTraceSourceOptions.WriteLine) != 0) + if ((PSObject.MemberResolution.Options & PSTraceSourceOptions.WriteLine) != 0) { - PSObject.memberResolution.WriteLine("Calling Constructor: {0}", DotNetAdapter.GetMethodInfoOverloadDefinition(null, + PSObject.MemberResolution.WriteLine("Calling Constructor: {0}", DotNetAdapter.GetMethodInfoOverloadDefinition(null, bestMethod.method, 0)); } @@ -4107,7 +4221,7 @@ namespace System.Management.Automation // of all parameters but the last one object[] newArguments; MethodInformation bestMethod = GetBestMethodAndArguments(propertyName, methodInformation, arguments, out newArguments); - PSObject.memberResolution.WriteLine("Calling Set Method: {0}", bestMethod.methodDefinition); + PSObject.MemberResolution.WriteLine("Calling Set Method: {0}", bestMethod.methodDefinition); ParameterInfo[] bestMethodParameters = bestMethod.method.GetParameters(); Type propertyType = bestMethodParameters[bestMethodParameters.Length - 1].ParameterType; @@ -4378,6 +4492,7 @@ namespace System.Management.Automation } #region DotNetAdapterWithOnlyPropertyLookup + /// /// This is used by PSObject to support dotnet member lookup for the adapted /// objects. @@ -4391,9 +4506,6 @@ namespace System.Management.Automation /// Return a collection representing the object's /// members as returned by CLR reflection. /// - /// - /// - /// protected override PSMemberInfoInternalCollection GetMembers(object obj) { PSMemberInfoInternalCollection returnValue = new PSMemberInfoInternalCollection(); @@ -4407,10 +4519,6 @@ namespace System.Management.Automation /// /// Returns a member representing the as given by CLR reflection. /// - /// - /// - /// - /// protected override T GetMember(object obj, string memberName) { PSProperty property = base.GetDotNetProperty(obj, memberName); @@ -4447,6 +4555,49 @@ namespace System.Management.Automation return null; } + + /// + /// Returns the first reflection member whose name matches the specified . + /// Otherwise, return null. + /// + protected override T GetFirstMemberOrDefault(object obj, MemberNamePredicate predicate) + { + PSProperty property = base.GetFirstDotNetPropertyOrDefault(obj, predicate); + if (typeof(T).IsAssignableFrom(typeof(PSProperty)) && property != null) + { + return property as T; + } + + // In order to not break v1..base dotnet adapter should not return methods + // when accessed with T as PSMethod.. accessing method with PSMemberInfo + // is ok as property always gets precedence over methods and duplicates + // are ignored. + if (typeof(T) == typeof(PSMemberInfo)) + { + T returnValue = base.GetFirstDotNetMethodOrDefault(obj, predicate); + + // We only return a method if there is no property by the same name + // to match the behavior we have in GetMembers + if (returnValue != null && property == null) + { + return returnValue; + } + } + + if (IsTypeParameterizedProperty(typeof(T))) + { + var parameterizedProperty = base.GetFirstDotNetPropertyOrDefault(obj, predicate); + + // We only return a parameterized property if there is no property by the same name + // to match the behavior we have in GetMembers + if (parameterizedProperty != null && property == null) + { + return parameterizedProperty as T; + } + } + + return null; + } } #endregion @@ -4628,6 +4779,15 @@ namespace System.Management.Automation return ((PSObject)obj).Members[memberName] as T; } + /// + /// Returns the first PSMemberInfo whose name matches the specified . + /// Otherwise, return null. + /// + protected override T GetFirstMemberOrDefault(object obj, MemberNamePredicate predicate) + { + return ((PSObject)obj).GetFirstPropertyOrDefault(predicate) as T; + } + /// /// Retrieves all the members available in the object. /// The adapter implementation is encouraged to cache all properties/methods available @@ -4687,6 +4847,23 @@ namespace System.Management.Automation return ((PSMemberSet)obj).Members[memberName] as T; } + /// + /// Returns the first PSMemberInfo whose name matches the specified . + /// Otherwise, return null. + /// + protected override T GetFirstMemberOrDefault(object obj, MemberNamePredicate predicate) + { + foreach (var member in ((PSMemberSet)obj).Members) + { + if (predicate(member.Name)) + { + return member as T; + } + } + + return null; + } + /// /// Retrieves all the members available in the object. /// The adapter implementation is encouraged to cache all properties/methods available @@ -4756,6 +4933,15 @@ namespace System.Management.Automation /// The PSProperty corresponding to propertyName from obj. protected abstract PSProperty DoGetProperty(object obj, string propertyName); + /// + /// Returns the first PSProperty whose name matches the specified . + /// Otherwise, return null. + /// + /// Object to retrieve the PSProperty from. + /// The predicate to find the matching member. + /// The first PSProperty whose name matches the . + protected abstract PSProperty DoGetFirstPropertyOrDefault(object obj, MemberNamePredicate predicate); + /// /// Retrieves all the properties available in the object. /// @@ -4804,6 +4990,50 @@ namespace System.Management.Automation return null; } + /// + /// Returns the first PSMemberInfo whose name matches the specified . + /// Otherwise, return null. + /// + /// A subtype of . + /// Object to retrieve the PSMemberInfo from. + /// A name matching predicate. + /// The PSMemberInfo corresponding to the predicate match. + protected override T GetFirstMemberOrDefault(object obj, MemberNamePredicate predicate) + { + PSProperty property = DoGetFirstPropertyOrDefault(obj, predicate); + + if (typeof(T).IsAssignableFrom(typeof(PSProperty))) + { + return property as T; + } + + if (typeof(T).IsAssignableFrom(typeof(PSMethod))) + { + T returnValue = base.GetFirstDotNetMethodOrDefault(obj, predicate); + + // We only return a method if there is no property by the same name + // to match the behavior we have in GetMembers + if (returnValue != null && property == null) + { + return returnValue; + } + } + + if (IsTypeParameterizedProperty(typeof(T))) + { + var parameterizedProperty = base.GetFirstDotNetPropertyOrDefault(obj, predicate); + + // We only return a parameterized property if there is no property by the same name + // to match the behavior we have in GetMembers + if (parameterizedProperty != null && property == null) + { + return parameterizedProperty as T; + } + } + + return null; + } + /// /// Retrieves all the members available in the object. /// The adapter implementation is encouraged to cache all properties/methods available @@ -4959,6 +5189,12 @@ namespace System.Management.Automation return new PSProperty(nodes[0].LocalName, this, obj, nodes); } + protected override PSProperty DoGetFirstPropertyOrDefault(object obj, MemberNamePredicate predicate) + { + XmlNode node = FindNode(obj, predicate); + return node == null ? null : new PSProperty(node.LocalName, this, obj, node); + } + /// /// Returns true if the property is settable. /// @@ -5200,6 +5436,38 @@ namespace System.Management.Automation return retValue.ToArray(); } + + private static XmlNode FindNode(object obj, MemberNamePredicate predicate) + { + var node = (XmlNode)obj; + + if (node.Attributes != null) + { + foreach (XmlNode attribute in node.Attributes) + { + if (predicate(attribute.LocalName)) + { + return attribute; + } + } + } + + foreach (XmlNode childNode in node.ChildNodes) + { + if (childNode is XmlWhitespace) + { + // Win8: 437544 ignore whitespace + continue; + } + + if (predicate(childNode.LocalName)) + { + return childNode; + } + } + + return null; + } } /// @@ -5208,6 +5476,7 @@ namespace System.Management.Automation internal class DataRowAdapter : PropertyOnlyAdapter { #region virtual + /// /// Retrieves all the properties available in the object. /// @@ -5249,6 +5518,21 @@ namespace System.Management.Automation return new PSProperty(columnName, this, obj, columnName); } + protected override PSProperty DoGetFirstPropertyOrDefault(object obj, MemberNamePredicate predicate) + { + DataRow dataRow = (DataRow)obj; + + foreach (DataColumn property in dataRow.Table.Columns) + { + if (predicate(property.ColumnName)) + { + return new PSProperty(property.ColumnName, this, obj, property.ColumnName); + } + } + + return null; + } + /// /// Returns the name of the type corresponding to the property. /// @@ -5356,6 +5640,22 @@ namespace System.Management.Automation return new PSProperty(columnName, this, obj, columnName); } + protected override PSProperty DoGetFirstPropertyOrDefault(object obj, MemberNamePredicate predicate) + { + DataRowView dataRowView = (DataRowView)obj; + + foreach (DataColumn column in dataRowView.Row.Table.Columns) + { + string columnName = column.ColumnName; + if (predicate(columnName)) + { + return new PSProperty(columnName, this, obj, columnName); + } + } + + return null; + } + /// /// Returns the name of the type corresponding to the property. /// diff --git a/src/System.Management.Automation/engine/ManagementObjectAdapter.cs b/src/System.Management.Automation/engine/ManagementObjectAdapter.cs index b07f85c313..d191ce5a9b 100644 --- a/src/System.Management.Automation/engine/ManagementObjectAdapter.cs +++ b/src/System.Management.Automation/engine/ManagementObjectAdapter.cs @@ -198,6 +198,17 @@ namespace System.Management.Automation return null; } + protected override T GetFirstMemberOrDefault(object obj, MemberNamePredicate predicate) + { + if (obj is ManagementBaseObject wmiObject) + { + return GetFirstOrDefaultProperty(wmiObject, predicate) + ?? GetFirstOrDefaultMethod(wmiObject, predicate); + } + + return null; + } + /// /// Retrieves all the members available in the object. /// The adapter implementation is encouraged to cache all properties/methods available @@ -918,6 +929,16 @@ namespace System.Management.Automation protected abstract PSProperty DoGetProperty(ManagementBaseObject wmiObject, string propertyName); + /// + /// Returns the first property whose name matches the specified + /// + protected abstract T GetFirstOrDefaultProperty(ManagementBaseObject wmiObject, MemberNamePredicate predicate) where T : PSMemberInfo; + + /// + /// Returns the first method whose name matches the specified + /// + protected abstract T GetFirstOrDefaultMethod(ManagementBaseObject wmiObject, MemberNamePredicate predicate) where T : PSMemberInfo; + #endregion #region Private Data @@ -1037,11 +1058,8 @@ namespace System.Management.Automation return null; } - CacheTable typeTable; - WMIMethodCacheEntry method; - - typeTable = GetInstanceMethodTable(wmiObject, true); - method = (WMIMethodCacheEntry)typeTable[methodName]; + CacheTable typeTable = GetInstanceMethodTable(wmiObject, true); + WMIMethodCacheEntry method = (WMIMethodCacheEntry)typeTable[methodName]; if (method == null) { @@ -1050,6 +1068,46 @@ namespace System.Management.Automation return new PSMethod(method.Name, this, wmiObject, method) as T; } + + protected override T GetFirstOrDefaultProperty(ManagementBaseObject wmiObject, MemberNamePredicate predicate) + { + if (!typeof(T).IsAssignableFrom(typeof(PSProperty))) + { + return null; + } + + if (wmiObject.SystemProperties != null) + { + foreach (PropertyData property in wmiObject.SystemProperties) + { + if (predicate(property.Name)) + { + return new PSProperty(property.Name, this, wmiObject, property) as T; + } + } + } + + return null; + } + + protected override T GetFirstOrDefaultMethod(ManagementBaseObject wmiObject, MemberNamePredicate predicate) + { + if (!typeof(T).IsAssignableFrom(typeof(PSMethod))) + { + return null; + } + + CacheTable table = GetInstanceMethodTable(wmiObject, true); + foreach (WMIMethodCacheEntry methodEntry in table.memberCollection) + { + if (predicate(methodEntry.Name)) + { + return new PSMethod(methodEntry.Name, this, wmiObject, methodEntry) as T; + } + } + + return null; + } } /// diff --git a/src/System.Management.Automation/engine/MshCommandRuntime.cs b/src/System.Management.Automation/engine/MshCommandRuntime.cs index ef1cd5fbaa..a169a629cc 100644 --- a/src/System.Management.Automation/engine/MshCommandRuntime.cs +++ b/src/System.Management.Automation/engine/MshCommandRuntime.cs @@ -486,12 +486,9 @@ namespace System.Management.Automation CBhost.InternalUI.WriteDebugInfoBuffers(record); } - // Add note property so that the debug output is formatted correctly. + // Set WriteStream so that the debug output is formatted correctly. PSObject debugWrap = PSObject.AsPSObject(record); - if (debugWrap.Members["WriteDebugStream"] == null) - { - debugWrap.Properties.Add(new PSNoteProperty("WriteDebugStream", true)); - } + debugWrap.WriteStream = WriteStreamType.Debug; DebugOutputPipe.Add(debugWrap); } @@ -577,12 +574,9 @@ namespace System.Management.Automation CBhost.InternalUI.WriteVerboseInfoBuffers(record); } - // Add note property so that the verbose output is formatted correctly. + // Add WriteStream so that the verbose output is formatted correctly. PSObject verboseWrap = PSObject.AsPSObject(record); - if (verboseWrap.Members["WriteVerboseStream"] == null) - { - verboseWrap.Properties.Add(new PSNoteProperty("WriteVerboseStream", true)); - } + verboseWrap.WriteStream = WriteStreamType.Verbose; VerboseOutputPipe.Add(verboseWrap); } @@ -668,12 +662,9 @@ namespace System.Management.Automation CBhost.InternalUI.WriteWarningInfoBuffers(record); } - // Add note property so that the warning output is formatted correctly. + // Add WriteStream so that the warning output is formatted correctly. PSObject warningWrap = PSObject.AsPSObject(record); - if (warningWrap.Members["WriteWarningStream"] == null) - { - warningWrap.Properties.Add(new PSNoteProperty("WriteWarningStream", true)); - } + warningWrap.WriteStream = WriteStreamType.Warning; WarningOutputPipe.AddWithoutAppendingOutVarList(warningWrap); } @@ -733,12 +724,9 @@ namespace System.Management.Automation CBhost.InternalUI.WriteInformationInfoBuffers(record); } - // Add note property so that the information output is formatted correctly. + // Add WriteStream so that the information output is formatted correctly. PSObject informationWrap = PSObject.AsPSObject(record); - if (informationWrap.Members["WriteInformationStream"] == null) - { - informationWrap.Properties.Add(new PSNoteProperty("WriteInformationStream", true)); - } + informationWrap.WriteStream = WriteStreamType.Information; InformationOutputPipe.Add(informationWrap); } @@ -2845,10 +2833,9 @@ namespace System.Management.Automation // when tracing), so don't add the member again. // We don't add a note property on messages that comes from stderr stream. - if (!isNativeError && errorWrap.Members["writeErrorStream"] == null) + if (!isNativeError) { - PSNoteProperty note = new PSNoteProperty("writeErrorStream", true); - errorWrap.Properties.Add(note); + errorWrap.WriteStream = WriteStreamType.Error; } // 2003/11/19-JonN Previously, PSObject instances in ErrorOutputPipe @@ -3566,7 +3553,7 @@ namespace System.Management.Automation CBhost.InternalUI.TranscribeResult(inquireCaption); CBhost.InternalUI.TranscribeResult(inquireMessage); - System.Text.StringBuilder textChoices = new System.Text.StringBuilder(); + Text.StringBuilder textChoices = new Text.StringBuilder(); foreach (ChoiceDescription choice in choices) { if (textChoices.Length > 0) diff --git a/src/System.Management.Automation/engine/MshMemberInfo.cs b/src/System.Management.Automation/engine/MshMemberInfo.cs index aff5e7635a..d6dfd19b68 100644 --- a/src/System.Management.Automation/engine/MshMemberInfo.cs +++ b/src/System.Management.Automation/engine/MshMemberInfo.cs @@ -3115,6 +3115,7 @@ namespace System.Management.Automation returnValue.Add(new CollectionEntry( PSObject.TypeTableGetMembersDelegate, PSObject.TypeTableGetMemberDelegate, + PSObject.TypeTableGetFirstMemberOrDefaultDelegate, true, true, "type table members")); return returnValue; } @@ -3125,6 +3126,7 @@ namespace System.Management.Automation returnValue.Add(new CollectionEntry( PSObject.TypeTableGetMembersDelegate, PSObject.TypeTableGetMemberDelegate, + PSObject.TypeTableGetFirstMemberOrDefaultDelegate, true, true, "type table members")); return returnValue; } @@ -3135,6 +3137,7 @@ namespace System.Management.Automation returnValue.Add(new CollectionEntry( PSObject.TypeTableGetMembersDelegate, PSObject.TypeTableGetMemberDelegate, + PSObject.TypeTableGetFirstMemberOrDefaultDelegate, true, true, "type table members")); return returnValue; } @@ -3329,11 +3332,11 @@ namespace System.Management.Automation private void GenerateInternalMembersFromBase() { - if (_psObject.isDeserialized) + if (_psObject.IsDeserialized) { - if (_psObject.clrMembers != null) + if (_psObject.ClrMembers != null) { - foreach (PSMemberInfo member in _psObject.clrMembers) + foreach (PSMemberInfo member in _psObject.ClrMembers) { internalMembers.Add(member.Copy()); } @@ -3342,7 +3345,7 @@ namespace System.Management.Automation else { foreach (PSMemberInfo member in - PSObject.dotNetInstanceAdapter.BaseGetMembers(_psObject.ImmediateBaseObject)) + PSObject.DotNetInstanceAdapter.BaseGetMembers(_psObject.ImmediateBaseObject)) { internalMembers.Add(member.Copy()); } @@ -3353,11 +3356,11 @@ namespace System.Management.Automation { PSMemberInfoInternalCollection retVal = new PSMemberInfoInternalCollection(); - if (_psObject.isDeserialized) + if (_psObject.IsDeserialized) { - if (_psObject.adaptedMembers != null) + if (_psObject.AdaptedMembers != null) { - foreach (PSMemberInfo member in _psObject.adaptedMembers) + foreach (PSMemberInfo member in _psObject.AdaptedMembers) { retVal.Add(member.Copy()); } @@ -3377,7 +3380,7 @@ namespace System.Management.Automation private void GenerateInternalMembersFromPSObject() { - PSMemberInfoCollection members = PSObject.dotNetInstanceAdapter.BaseGetMembers( + PSMemberInfoCollection members = PSObject.DotNetInstanceAdapter.BaseGetMembers( _psObject); foreach (PSMemberInfo member in members) { @@ -3682,6 +3685,13 @@ namespace System.Management.Automation } } + /// + /// A Predicate that determine if a member name matches a criterion. + /// + /// + /// true if the matches the predicate, otherwise false. + public delegate bool MemberNamePredicate(string memberName); + /// /// Serves as the collection of members in an PSObject or MemberSet. /// @@ -3812,6 +3822,8 @@ namespace System.Management.Automation public abstract IEnumerator GetEnumerator(); #endregion IEnumerable + + internal abstract T FirstOrDefault(MemberNamePredicate predicate); } /// @@ -4261,6 +4273,25 @@ namespace System.Management.Automation return _members.Values.OfType().ToList().GetEnumerator(); } } + + /// + /// Returns the first member that matches the specified . + /// + internal override T FirstOrDefault(MemberNamePredicate predicate) + { + lock (_members) + { + foreach (DictionaryEntry entry in _members) + { + if (predicate((string)entry.Key)) + { + return entry.Value as T; + } + } + } + + return null; + } } #region CollectionEntry @@ -4271,13 +4302,21 @@ namespace System.Management.Automation internal delegate T GetMemberDelegate(PSObject obj, string name); - internal CollectionEntry(GetMembersDelegate getMembers, GetMemberDelegate getMember, - bool shouldReplicateWhenReturning, bool shouldCloneWhenReturning, string collectionNameForTracing) + internal delegate T GetFirstOrDefaultDelegate(PSObject obj, MemberNamePredicate predicate); + + internal CollectionEntry( + GetMembersDelegate getMembers, + GetMemberDelegate getMember, + GetFirstOrDefaultDelegate getFirstOrDefault, + bool shouldReplicateWhenReturning, + bool shouldCloneWhenReturning, + string collectionNameForTracing) { GetMembers = getMembers; GetMember = getMember; - ShouldReplicateWhenReturning = shouldReplicateWhenReturning; - ShouldCloneWhenReturning = shouldCloneWhenReturning; + GetFirstOrDefault = getFirstOrDefault; + _shouldReplicateWhenReturning = shouldReplicateWhenReturning; + _shouldCloneWhenReturning = shouldCloneWhenReturning; CollectionNameForTracing = collectionNameForTracing; } @@ -4285,11 +4324,28 @@ namespace System.Management.Automation internal GetMemberDelegate GetMember { get; } - internal bool ShouldReplicateWhenReturning { get; } - - internal bool ShouldCloneWhenReturning { get; } + internal GetFirstOrDefaultDelegate GetFirstOrDefault { get; } internal string CollectionNameForTracing { get; } + + private readonly bool _shouldReplicateWhenReturning; + + private readonly bool _shouldCloneWhenReturning; + + internal T CloneOrReplicateObject(object owner, T member) + { + if (_shouldCloneWhenReturning) + { + member = (T)member.Copy(); + } + + if (_shouldReplicateWhenReturning) + { + member.ReplicateInstance(owner); + } + + return member; + } } #endregion CollectionEntry @@ -4382,9 +4438,9 @@ namespace System.Management.Automation private void GenerateAllReservedMembers() { - if (!_mshOwner.hasGeneratedReservedMembers) + if (!_mshOwner.HasGeneratedReservedMembers) { - _mshOwner.hasGeneratedReservedMembers = true; + _mshOwner.HasGeneratedReservedMembers = true; ReservedNameMembers.GeneratePSExtendedMemberSet(_mshOwner); ReservedNameMembers.GeneratePSBaseMemberSet(_mshOwner); ReservedNameMembers.GeneratePSObjectMemberSet(_mshOwner); @@ -4676,7 +4732,7 @@ namespace System.Management.Automation { get { - using (PSObject.memberResolution.TraceScope("Lookup")) + using (PSObject.MemberResolution.TraceScope("Lookup")) { if (string.IsNullOrEmpty(name)) { @@ -4697,7 +4753,7 @@ namespace System.Management.Automation member = instanceMembers[name]; if (member is T memberAsT) { - PSObject.memberResolution.WriteLine("Found PSObject instance member: {0}.", name); + PSObject.MemberResolution.WriteLine("Found PSObject instance member: {0}.", name); return memberAsT; } } @@ -4711,7 +4767,7 @@ namespace System.Management.Automation // 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); + PSObject.MemberResolution.WriteLine("Found PSMemberSet member: {0}.", name); member.ReplicateInstance(delegateOwner); return memberAsT; } @@ -4727,17 +4783,7 @@ namespace System.Management.Automation 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 collection.CloneOrReplicateObject(delegateOwner, memberAsT); } } @@ -4748,7 +4794,7 @@ namespace System.Management.Automation private PSMemberInfoInternalCollection GetIntegratedMembers(MshMemberMatchOptions matchOptions) { - using (PSObject.memberResolution.TraceScope("Generating the total list of members")) + using (PSObject.MemberResolution.TraceScope("Generating the total list of members")) { PSMemberInfoInternalCollection returnValue = new PSMemberInfoInternalCollection(); object delegateOwner; @@ -4794,32 +4840,18 @@ namespace System.Management.Automation 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.", + 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); + 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); - } - + T memberToAdd = collection.CloneOrReplicateObject(delegateOwner, member); returnValue.Add(memberToAdd); } } @@ -4871,7 +4903,7 @@ namespace System.Management.Automation /// For invalid arguments. internal override ReadOnlyPSMemberInfoCollection Match(string name, PSMemberTypes memberTypes, MshMemberMatchOptions matchOptions) { - using (PSObject.memberResolution.TraceScope("Matching \"{0}\"", name)) + using (PSObject.MemberResolution.TraceScope("Matching \"{0}\"", name)) { if (string.IsNullOrEmpty(name)) { @@ -4886,7 +4918,7 @@ namespace System.Management.Automation WildcardPattern nameMatch = MemberMatch.GetNamePattern(name); PSMemberInfoInternalCollection allMembers = GetIntegratedMembers(matchOptions); ReadOnlyPSMemberInfoCollection returnValue = new ReadOnlyPSMemberInfoCollection(MemberMatch.Match(allMembers, name, nameMatch, memberTypes)); - PSObject.memberResolution.WriteLine("{0} total matches.", returnValue.Count); + PSObject.MemberResolution.WriteLine("{0} total matches.", returnValue.Count); return returnValue; } } @@ -4902,6 +4934,52 @@ namespace System.Management.Automation return new Enumerator(this); } + internal override T FirstOrDefault(MemberNamePredicate predicate) + { + object delegateOwner; + if (_mshOwner != null) + { + delegateOwner = _mshOwner; + foreach (PSMemberInfo member in _mshOwner.InstanceMembers) + { + if (member is T memberAsT && predicate(memberAsT.Name)) + { + return memberAsT; + } + } + } + else + { + delegateOwner = _memberSetOwner.instance; + foreach (PSMemberInfo member in _memberSetOwner.InternalMembers) + { + if (member is T memberAsT && predicate(memberAsT.Name)) + { + memberAsT.ReplicateInstance(delegateOwner); + return memberAsT; + } + } + } + + if (delegateOwner == null) + { + return null; + } + + var ownerAsPSObj = PSObject.AsPSObject(delegateOwner); + for (int i = 0; i < Collections.Count; i++) + { + var collectionEntry = Collections[i]; + var member = collectionEntry.GetFirstOrDefault(ownerAsPSObj, predicate); + if (member != null) + { + return collectionEntry.CloneOrReplicateObject(ownerAsPSObj, member); + } + } + + return null; + } + #endregion overrides /// @@ -4919,7 +4997,7 @@ namespace System.Management.Automation /// Members we are enumerating. internal Enumerator(PSMemberInfoIntegratingCollection integratingCollection) { - using (PSObject.memberResolution.TraceScope("Enumeration Start")) + using (PSObject.MemberResolution.TraceScope("Enumeration Start")) { _currentIndex = -1; _current = null; @@ -4927,13 +5005,13 @@ namespace System.Management.Automation 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); + 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); + PSObject.MemberResolution.WriteLine("Enumerating PSMemberSet \"{0}\".", integratingCollection._memberSetOwner.Name); + PSObject.MemberResolution.WriteLine("MemberSet instance members: {0}", _allMembers.VisibleCount); } } } diff --git a/src/System.Management.Automation/engine/MshObject.cs b/src/System.Management.Automation/engine/MshObject.cs index b3ae3378e4..edf41c7e34 100644 --- a/src/System.Management.Automation/engine/MshObject.cs +++ b/src/System.Management.Automation/engine/MshObject.cs @@ -78,17 +78,17 @@ namespace System.Management.Automation PSMemberInfo member = allMembers[name]; if (member == null) { - PSObject.memberResolution.WriteLine("\"{0}\" NOT present in type table.", name); + PSObject.MemberResolution.WriteLine("\"{0}\" NOT present in type table.", name); return null; } if (member is T memberAsT) { - PSObject.memberResolution.WriteLine("\"{0}\" present in type table.", name); + PSObject.MemberResolution.WriteLine("\"{0}\" present in type table.", name); return memberAsT; } - PSObject.memberResolution.WriteLine("\"{0}\" from types table ignored because it has type {1} instead of {2}.", + PSObject.MemberResolution.WriteLine("\"{0}\" from types table ignored because it has type {1} instead of {2}.", name, member.GetType(), typeof(T)); return null; } @@ -107,26 +107,59 @@ namespace System.Management.Automation } PSMemberInfoInternalCollection members = typeTableToUse.GetMembers(msjObj.InternalTypeNames); - PSObject.memberResolution.WriteLine("Type table members: {0}.", members.Count); + PSObject.MemberResolution.WriteLine("Type table members: {0}.", members.Count); return members; } + internal static T TypeTableGetFirstMemberOrDefaultDelegate(PSObject msjObj, MemberNamePredicate predicate) where T : PSMemberInfo + { + TypeTable table = msjObj.GetTypeTable(); + return TypeTableGetFirstOrDefaultMemberDelegate(msjObj, table, predicate); + } + + internal static T TypeTableGetFirstOrDefaultMemberDelegate(PSObject msjObj, TypeTable typeTableToUse, MemberNamePredicate predicate) where T : PSMemberInfo + { + return typeTableToUse?.GetFirstMemberOrDefault(msjObj.InternalTypeNames, predicate); + } + private static T AdapterGetMemberDelegate(PSObject msjObj, string name) where T : PSMemberInfo { - if (msjObj.isDeserialized) + if (msjObj.IsDeserialized) { - if (msjObj.adaptedMembers == null) + if (msjObj.AdaptedMembers == null) { return null; } - T adaptedMember = msjObj.adaptedMembers[name] as T; - PSObject.memberResolution.WriteLine("Serialized adapted member: {0}.", adaptedMember == null ? "not found" : adaptedMember.Name); + T adaptedMember = msjObj.AdaptedMembers[name] as T; + PSObject.MemberResolution.WriteLine("Serialized adapted member: {0}.", adaptedMember == null ? "not found" : adaptedMember.Name); return adaptedMember; } - T retValue = msjObj.InternalAdapter.BaseGetMember(msjObj._immediateBaseObject, name); - PSObject.memberResolution.WriteLine("Adapted member: {0}.", retValue == null ? "not found" : retValue.Name); + T retValue = msjObj.InternalAdapter.BaseGetMember(msjObj.ImmediateBaseObject, name); + PSObject.MemberResolution.WriteLine("Adapted member: {0}.", retValue == null ? "not found" : retValue.Name); + return retValue; + } + + private static T AdapterGetFirstMemberOrDefaultDelegate(PSObject msjObj, MemberNamePredicate predicate) where T : PSMemberInfo + { + if (msjObj.IsDeserialized && typeof(T).IsAssignableFrom(typeof(PSPropertyInfo))) + { + if (msjObj.AdaptedMembers == null) + { + return null; + } + + foreach (var adaptedMember in msjObj.AdaptedMembers) + { + if (predicate(adaptedMember.Name)) + { + return adaptedMember as T; + } + } + } + + T retValue = msjObj.InternalAdapter.BaseGetFirstMemberOrDefault(msjObj._immediateBaseObject, predicate); return retValue; } @@ -152,19 +185,19 @@ namespace System.Management.Automation private static PSMemberInfoInternalCollection AdapterGetMembersDelegate(PSObject msjObj) where T : PSMemberInfo { - if (msjObj.isDeserialized) + if (msjObj.IsDeserialized) { - if (msjObj.adaptedMembers == null) + if (msjObj.AdaptedMembers == null) { return new PSMemberInfoInternalCollection(); } - PSObject.memberResolution.WriteLine("Serialized adapted members: {0}.", msjObj.adaptedMembers.Count); - return TransformMemberInfoCollection(msjObj.adaptedMembers); + PSObject.MemberResolution.WriteLine("Serialized adapted members: {0}.", msjObj.AdaptedMembers.Count); + return TransformMemberInfoCollection(msjObj.AdaptedMembers); } PSMemberInfoInternalCollection retValue = msjObj.InternalAdapter.BaseGetMembers(msjObj._immediateBaseObject); - PSObject.memberResolution.WriteLine("Adapted members: {0}.", retValue.VisibleCount); + PSObject.MemberResolution.WriteLine("Adapted members: {0}.", retValue.VisibleCount); return retValue; } @@ -174,7 +207,7 @@ namespace System.Management.Automation if (msjObj.InternalBaseDotNetAdapter != null) { PSMemberInfoInternalCollection retValue = msjObj.InternalBaseDotNetAdapter.BaseGetMembers(msjObj._immediateBaseObject); - PSObject.memberResolution.WriteLine("DotNet members: {0}.", retValue.VisibleCount); + PSObject.MemberResolution.WriteLine("DotNet members: {0}.", retValue.VisibleCount); return retValue; } @@ -187,16 +220,18 @@ namespace System.Management.Automation if (msjObj.InternalBaseDotNetAdapter != null) { T retValue = msjObj.InternalBaseDotNetAdapter.BaseGetMember(msjObj._immediateBaseObject, name); - PSObject.memberResolution.WriteLine("DotNet member: {0}.", retValue == null ? "not found" : retValue.Name); + PSObject.MemberResolution.WriteLine("DotNet member: {0}.", retValue == null ? "not found" : retValue.Name); return retValue; } return null; } - private static readonly Collection> s_memberCollection = GetMemberCollection(PSMemberViewTypes.All); - private static readonly Collection> s_methodCollection = GetMethodCollection(); - private static readonly Collection> s_propertyCollection = GetPropertyCollection(PSMemberViewTypes.All); + private static T DotNetGetFirstMemberOrDefaultDelegate(PSObject msjObj, MemberNamePredicate predicate) where T : PSMemberInfo + { + // Don't lookup dotnet member if the object doesn't insist. + return msjObj.InternalBaseDotNetAdapter?.BaseGetFirstMemberOrDefault(msjObj._immediateBaseObject, predicate); + } /// /// A collection of delegates to get Extended/Adapted/Dotnet members based on the @@ -234,6 +269,7 @@ namespace System.Management.Automation returnValue.Add(new CollectionEntry( PSObject.TypeTableGetMembersDelegate, PSObject.TypeTableGetMemberDelegate, + PSObject.TypeTableGetFirstMemberOrDefaultDelegate, true, true, "type table members")); } else @@ -241,6 +277,7 @@ namespace System.Management.Automation returnValue.Add(new CollectionEntry( msjObj => TypeTableGetMembersDelegate(msjObj, backupTypeTable), (msjObj, name) => TypeTableGetMemberDelegate(msjObj, backupTypeTable, name), + (msjObj, predicate) => TypeTableGetFirstOrDefaultMemberDelegate(msjObj, backupTypeTable, predicate), true, true, "type table members")); } } @@ -250,6 +287,7 @@ namespace System.Management.Automation returnValue.Add(new CollectionEntry( PSObject.AdapterGetMembersDelegate, PSObject.AdapterGetMemberDelegate, + PSObject.AdapterGetFirstMemberOrDefaultDelegate, shouldReplicateWhenReturning: false, shouldCloneWhenReturning: false, collectionNameForTracing: "adapted members")); @@ -260,6 +298,7 @@ namespace System.Management.Automation returnValue.Add(new CollectionEntry( PSObject.DotNetGetMembersDelegate, PSObject.DotNetGetMemberDelegate, + PSObject.DotNetGetFirstMemberOrDefaultDelegate, shouldReplicateWhenReturning: false, shouldCloneWhenReturning: false, collectionNameForTracing: "clr members")); @@ -275,18 +314,21 @@ namespace System.Management.Automation new CollectionEntry( PSObject.TypeTableGetMembersDelegate, PSObject.TypeTableGetMemberDelegate, + PSObject.TypeTableGetFirstMemberOrDefaultDelegate, shouldReplicateWhenReturning: true, shouldCloneWhenReturning: true, collectionNameForTracing: "type table members"), new CollectionEntry( PSObject.AdapterGetMembersDelegate, PSObject.AdapterGetMemberDelegate, + PSObject.AdapterGetFirstMemberOrDefaultDelegate, shouldReplicateWhenReturning: false, shouldCloneWhenReturning: false, collectionNameForTracing: "adapted members"), new CollectionEntry( PSObject.DotNetGetMembersDelegate, PSObject.DotNetGetMemberDelegate, + PSObject.DotNetGetFirstMemberOrDefaultDelegate, shouldReplicateWhenReturning: false, shouldCloneWhenReturning: false, collectionNameForTracing: "clr members") @@ -331,6 +373,7 @@ namespace System.Management.Automation returnValue.Add(new CollectionEntry( PSObject.TypeTableGetMembersDelegate, PSObject.TypeTableGetMemberDelegate, + PSObject.TypeTableGetFirstMemberOrDefaultDelegate, true, true, "type table members")); } else @@ -338,6 +381,7 @@ namespace System.Management.Automation returnValue.Add(new CollectionEntry( msjObj => TypeTableGetMembersDelegate(msjObj, backupTypeTable), (msjObj, name) => TypeTableGetMemberDelegate(msjObj, backupTypeTable, name), + PSObject.TypeTableGetFirstMemberOrDefaultDelegate, true, true, "type table members")); } } @@ -347,6 +391,7 @@ namespace System.Management.Automation returnValue.Add(new CollectionEntry( PSObject.AdapterGetMembersDelegate, PSObject.AdapterGetMemberDelegate, + PSObject.AdapterGetFirstMemberOrDefaultDelegate, false, false, "adapted members")); } @@ -355,6 +400,7 @@ namespace System.Management.Automation returnValue.Add(new CollectionEntry( PSObject.DotNetGetMembersDelegate, PSObject.DotNetGetMemberDelegate, + PSObject.DotNetGetFirstMemberOrDefaultDelegate, false, false, "clr members")); } @@ -366,7 +412,7 @@ namespace System.Management.Automation Diagnostics.Assert(obj != null, "checked by callers"); if (obj is PSCustomObject) { - this.immediateBaseObjectIsEmpty = true; + this.ImmediateBaseObjectIsEmpty = true; } _immediateBaseObject = obj; @@ -374,26 +420,6 @@ namespace System.Management.Automation _typeTable = context?.TypeTableWeakReference; } - internal static readonly DotNetAdapter dotNetInstanceAdapter = new DotNetAdapter(); - private static readonly DotNetAdapter s_baseAdapterForAdaptedObjects = new BaseDotNetAdapterForAdaptedObjects(); - internal static readonly DotNetAdapter dotNetStaticAdapter = new DotNetAdapter(true); - - private static readonly AdapterSet s_dotNetInstanceAdapterSet = new AdapterSet(dotNetInstanceAdapter, null); - private static readonly AdapterSet s_mshMemberSetAdapter = new AdapterSet(new PSMemberSetAdapter(), null); - private static readonly AdapterSet s_mshObjectAdapter = new AdapterSet(new PSObjectAdapter(), null); - private static readonly PSObject.AdapterSet s_cimInstanceAdapter = - new PSObject.AdapterSet(new ThirdPartyAdapter(typeof(Microsoft.Management.Infrastructure.CimInstance), - new Microsoft.PowerShell.Cim.CimInstanceAdapter()), - PSObject.dotNetInstanceAdapter); -#if !UNIX - private static readonly AdapterSet s_managementObjectAdapter = new AdapterSet(new ManagementObjectAdapter(), dotNetInstanceAdapter); - private static readonly AdapterSet s_managementClassAdapter = new AdapterSet(new ManagementClassApdapter(), dotNetInstanceAdapter); - private static readonly AdapterSet s_directoryEntryAdapter = new AdapterSet(new DirectoryEntryAdapter(), dotNetInstanceAdapter); -#endif - private static readonly AdapterSet s_dataRowViewAdapter = new AdapterSet(new DataRowViewAdapter(), s_baseAdapterForAdaptedObjects); - private static readonly AdapterSet s_dataRowAdapter = new AdapterSet(new DataRowAdapter(), s_baseAdapterForAdaptedObjects); - private static readonly AdapterSet s_xmlNodeAdapter = new AdapterSet(new XmlNodeAdapter(), s_baseAdapterForAdaptedObjects); - #region Adapter Mappings private static readonly ConcurrentDictionary s_adapterMapping = new ConcurrentDictionary(); @@ -482,7 +508,7 @@ namespace System.Management.Automation ComTypeInfo info = ComTypeInfo.GetDispatchTypeInfo(obj); return info != null - ? new AdapterSet(new ComAdapter(info), dotNetInstanceAdapter) + ? new AdapterSet(new ComAdapter(info), DotNetInstanceAdapter) : PSObject.s_dotNetInstanceAdapterSet; } else @@ -583,12 +609,10 @@ namespace System.Management.Automation #region fields #region instance fields - private readonly object _lockObject = new Object(); - /// - /// If this is non-null return this string as the ToString() for this wrapped object. - /// - internal string TokenText; + private readonly object _lockObject = new object(); + + private ConsolidatedString _typeNames; /// /// This is the main field in the class representing @@ -597,15 +621,76 @@ namespace System.Management.Automation private object _immediateBaseObject; private WeakReference _typeTable; + private AdapterSet _adapterSet; + private PSMemberInfoInternalCollection _instanceMembers; + private PSMemberInfoIntegratingCollection _members; + private PSMemberInfoIntegratingCollection _properties; + private PSMemberInfoIntegratingCollection _methods; + + private PSObjectFlags _flags; + + #endregion instance fields + + private static readonly PSTraceSource s_memberResolution = PSTraceSource.GetTracer("MemberResolution", "Traces the resolution from member name to the member. A member can be a property, method, etc.", false); + + private static readonly ConditionalWeakTable s_typeNamesResurrectionTable = new ConditionalWeakTable(); + + private static readonly Collection> s_memberCollection = GetMemberCollection(PSMemberViewTypes.All); + private static readonly Collection> s_methodCollection = GetMethodCollection(); + private static readonly Collection> s_propertyCollection = GetPropertyCollection(PSMemberViewTypes.All); + + private static readonly DotNetAdapter s_dotNetInstanceAdapter = new DotNetAdapter(); + private static readonly DotNetAdapter s_baseAdapterForAdaptedObjects = new BaseDotNetAdapterForAdaptedObjects(); + private static readonly DotNetAdapter s_dotNetStaticAdapter = new DotNetAdapter(true); + + private static readonly AdapterSet s_dotNetInstanceAdapterSet = new AdapterSet(DotNetInstanceAdapter, null); + private static readonly AdapterSet s_mshMemberSetAdapter = new AdapterSet(new PSMemberSetAdapter(), null); + private static readonly AdapterSet s_mshObjectAdapter = new AdapterSet(new PSObjectAdapter(), null); + private static readonly PSObject.AdapterSet s_cimInstanceAdapter = + new PSObject.AdapterSet(new ThirdPartyAdapter(typeof(Microsoft.Management.Infrastructure.CimInstance), + new Microsoft.PowerShell.Cim.CimInstanceAdapter()), + PSObject.DotNetInstanceAdapter); +#if !UNIX + private static readonly AdapterSet s_managementObjectAdapter = new AdapterSet(new ManagementObjectAdapter(), DotNetInstanceAdapter); + private static readonly AdapterSet s_managementClassAdapter = new AdapterSet(new ManagementClassApdapter(), DotNetInstanceAdapter); + private static readonly AdapterSet s_directoryEntryAdapter = new AdapterSet(new DirectoryEntryAdapter(), DotNetInstanceAdapter); +#endif + private static readonly AdapterSet s_dataRowViewAdapter = new AdapterSet(new DataRowViewAdapter(), s_baseAdapterForAdaptedObjects); + private static readonly AdapterSet s_dataRowAdapter = new AdapterSet(new DataRowAdapter(), s_baseAdapterForAdaptedObjects); + private static readonly AdapterSet s_xmlNodeAdapter = new AdapterSet(new XmlNodeAdapter(), s_baseAdapterForAdaptedObjects); + + #endregion fields + + #region properties + + internal PSMemberInfoInternalCollection InstanceMembers + { + get + { + if (_instanceMembers == null) + { + lock (_lockObject) + { + if (_instanceMembers == null) + { + _instanceMembers = + s_instanceMembersResurrectionTable.GetValue( + GetKeyForResurrectionTables(this), + _ => new PSMemberInfoInternalCollection()); + } + } + } + + return _instanceMembers; + } + + set => _instanceMembers = value; + } /// /// This is the adapter that will depend on the type of baseObject. /// - internal Adapter InternalAdapter - { - get => InternalAdapterSet.OriginalAdapter; - set => InternalAdapterSet.OriginalAdapter = value; - } + internal Adapter InternalAdapter => InternalAdapterSet.OriginalAdapter; /// /// This is the adapter that is used to resolve the base dotnet members for an @@ -644,97 +729,6 @@ namespace System.Management.Automation } } - private AdapterSet _adapterSet; - - internal bool hasGeneratedReservedMembers; - - internal PSMemberInfoInternalCollection InstanceMembers - { - get - { - if (_instanceMembers == null) - { - lock (_lockObject) - { - if (_instanceMembers == null) - { - _instanceMembers = - s_instanceMembersResurrectionTable.GetValue( - GetKeyForResurrectionTables(this), - _ => new PSMemberInfoInternalCollection()); - } - } - } - - return _instanceMembers; - } - - set => _instanceMembers = value; - } - - private PSMemberInfoInternalCollection _instanceMembers; - - internal static bool HasInstanceMembers(object obj, out PSMemberInfoInternalCollection instanceMembers) - { - if (obj is PSObject psobj) - { - lock (psobj) - { - if (psobj._instanceMembers == null) - { - s_instanceMembersResurrectionTable.TryGetValue(GetKeyForResurrectionTables(psobj), - out psobj._instanceMembers); - } - } - - instanceMembers = psobj._instanceMembers; - } - else if (obj != null) - { - s_instanceMembersResurrectionTable.TryGetValue(GetKeyForResurrectionTables(obj), out instanceMembers); - } - else - { - instanceMembers = null; - } - - return instanceMembers != null && instanceMembers.Count > 0; - } - - private static readonly ConditionalWeakTable> s_instanceMembersResurrectionTable = - new ConditionalWeakTable>(); - - /// - /// Indicate whether we store the instance members and type names locally - /// for this PSObject instance. - /// - private bool _storeTypeNameAndInstanceMembersLocally; - - /// - /// Members from the adapter of the object before it was serialized - /// Null for live objects but not null for deserialized objects. - /// - internal PSMemberInfoInternalCollection adaptedMembers; - - /// - /// Members from the adapter of the object before it was serialized - /// Null for live objects but not null for deserialized objects. - /// - internal PSMemberInfoInternalCollection clrMembers; - - /// - /// Set to true when the BaseObject is PSCustomObject. - /// - internal bool immediateBaseObjectIsEmpty; - - internal static PSTraceSource memberResolution = PSTraceSource.GetTracer("MemberResolution", "Traces the resolution from member name to the member. A member can be a property, method, etc.", false); - - #endregion instance fields - - #endregion fields - - #region properties - /// /// Gets the member collection. /// @@ -757,8 +751,6 @@ namespace System.Management.Automation } } - private PSMemberInfoIntegratingCollection _members; - /// /// Gets the Property collection, or the members that are actually properties. /// @@ -781,7 +773,6 @@ namespace System.Management.Automation } } - private PSMemberInfoIntegratingCollection _properties; /// /// Gets the Method collection, or the members that are actually methods. @@ -805,8 +796,6 @@ namespace System.Management.Automation } } - private PSMemberInfoIntegratingCollection _methods; - /// /// Gets the object we are directly wrapping. /// @@ -917,12 +906,37 @@ namespace System.Management.Automation return s_typeNamesResurrectionTable.TryGetValue(GetKeyForResurrectionTables(obj), out result); } - private ConsolidatedString _typeNames; - private static readonly ConditionalWeakTable s_typeNamesResurrectionTable = new ConditionalWeakTable(); - #endregion properties #region static methods + internal static bool HasInstanceMembers(object obj, out PSMemberInfoInternalCollection instanceMembers) + { + if (obj is PSObject psobj) + { + lock (psobj) + { + if (psobj._instanceMembers == null) + { + s_instanceMembersResurrectionTable.TryGetValue(GetKeyForResurrectionTables(psobj), out psobj._instanceMembers); + } + } + + instanceMembers = psobj._instanceMembers; + } + else if (obj != null) + { + s_instanceMembersResurrectionTable.TryGetValue(GetKeyForResurrectionTables(obj), out instanceMembers); + } + else + { + instanceMembers = null; + } + + return instanceMembers != null && instanceMembers.Count > 0; + } + + private static readonly ConditionalWeakTable> s_instanceMembersResurrectionTable = + new ConditionalWeakTable>(); /// /// @@ -980,7 +994,7 @@ namespace System.Management.Automation if (mshObj == AutomationNull.Value) return null; - if (mshObj.immediateBaseObjectIsEmpty) + if (mshObj.ImmediateBaseObjectIsEmpty) { return obj; } @@ -990,7 +1004,7 @@ namespace System.Management.Automation { returnValue = mshObj._immediateBaseObject; mshObj = returnValue as PSObject; - } while ((mshObj != null) && (!mshObj.immediateBaseObjectIsEmpty)); + } while ((mshObj != null) && (!mshObj.ImmediateBaseObjectIsEmpty)); return returnValue; } @@ -1004,7 +1018,7 @@ namespace System.Management.Automation } var objType = obj as Type ?? obj.GetType(); - return dotNetStaticAdapter.BaseGetMember(objType, methodName); + return DotNetStaticAdapter.BaseGetMember(objType, methodName); } /// @@ -1044,7 +1058,7 @@ namespace System.Management.Automation return so; } - return new PSObject(obj) { _storeTypeNameAndInstanceMembersLocally = storeTypeNameAndInstanceMembersLocally }; + return new PSObject(obj) { StoreTypeNameAndInstanceMembersLocally = storeTypeNameAndInstanceMembersLocally }; } /// @@ -1071,7 +1085,7 @@ namespace System.Management.Automation if (psObjectAboveBase.ImmediateBaseObject is PSCustomObject || psObjectAboveBase.ImmediateBaseObject is string - || pso._storeTypeNameAndInstanceMembersLocally) + || pso.StoreTypeNameAndInstanceMembersLocally) { return psObjectAboveBase; } @@ -1412,7 +1426,7 @@ namespace System.Management.Automation // Since we don't have a brokered ToString, we check for the need to enumerate the object or its properties if (recurse) { - if (mshObj.immediateBaseObjectIsEmpty) + if (mshObj.ImmediateBaseObjectIsEmpty) { try { @@ -1495,9 +1509,9 @@ namespace System.Management.Automation { // If ToString value from deserialization is available, // simply return it. - if (_toStringFromDeserialization != null) + if (ToStringFromDeserialization != null) { - return _toStringFromDeserialization; + return ToStringFromDeserialization; } return PSObject.ToString(null, this, null, null, null, true, false); @@ -1516,9 +1530,9 @@ namespace System.Management.Automation { // If ToString value from deserialization is available, // simply return it. - if (_toStringFromDeserialization != null) + if (ToStringFromDeserialization != null) { - return _toStringFromDeserialization; + return ToStringFromDeserialization; } return PSObject.ToString(null, this, null, format, formatProvider, true, false); @@ -1555,12 +1569,12 @@ namespace System.Management.Automation if (this.BaseObject is PSCustomObject) { returnValue._immediateBaseObject = PSCustomObject.SelfInstance; - returnValue.immediateBaseObjectIsEmpty = true; + returnValue.ImmediateBaseObjectIsEmpty = true; } else { returnValue._immediateBaseObject = _immediateBaseObject; - returnValue.immediateBaseObjectIsEmpty = false; + returnValue.ImmediateBaseObjectIsEmpty = false; } // Instance members will be recovered as necessary through the resurrection table. @@ -1607,7 +1621,8 @@ namespace System.Management.Automation } } - returnValue.hasGeneratedReservedMembers = false; + returnValue.WriteStream = WriteStream; + returnValue.HasGeneratedReservedMembers = false; return returnValue; } @@ -1788,7 +1803,7 @@ namespace System.Management.Automation // We create a wrapper PSObject, so that we can successfully deserialize it string serializedContent; - if (this.immediateBaseObjectIsEmpty) + if (this.ImmediateBaseObjectIsEmpty) { PSObject serializeTarget = new PSObject(this); serializedContent = PSSerializer.Serialize(serializeTarget); @@ -1974,22 +1989,22 @@ namespace System.Management.Automation internal bool ShouldSerializeAdapter() { - if (this.isDeserialized) + if (this.IsDeserialized) { - return this.adaptedMembers != null; + return this.AdaptedMembers != null; } - return !this.immediateBaseObjectIsEmpty; + return !this.ImmediateBaseObjectIsEmpty; } internal PSMemberInfoInternalCollection GetAdaptedProperties() { - return GetProperties(this.adaptedMembers, this.InternalAdapter); + return GetProperties(this.AdaptedMembers, this.InternalAdapter); } private PSMemberInfoInternalCollection GetProperties(PSMemberInfoInternalCollection serializedMembers, Adapter particularAdapter) { - if (this.isDeserialized) + if (this.IsDeserialized) { return serializedMembers; } @@ -2004,23 +2019,18 @@ namespace System.Management.Automation return returnValue; } - /// - /// This flag is set to true in deserialized shellobject. - /// - internal bool isDeserialized; - internal static void CopyDeserializerFields(PSObject source, PSObject target) { - if (!target.isDeserialized) + if (!target.IsDeserialized) { - target.isDeserialized = source.isDeserialized; - target.adaptedMembers = source.adaptedMembers; - target.clrMembers = source.clrMembers; + target.IsDeserialized = source.IsDeserialized; + target.AdaptedMembers = source.AdaptedMembers; + target.ClrMembers = source.ClrMembers; } - if (target._toStringFromDeserialization == null) + if (target.ToStringFromDeserialization == null) { - target._toStringFromDeserialization = source._toStringFromDeserialization; + target.ToStringFromDeserialization = source.ToStringFromDeserialization; target.TokenText = source.TokenText; } } @@ -2033,9 +2043,9 @@ namespace System.Management.Automation ///This method is to be used only by Serialization code internal void SetCoreOnDeserialization(object value, bool overrideTypeInfo) { - Diagnostics.Assert(this.immediateBaseObjectIsEmpty, "BaseObject should be PSCustomObject for deserialized objects"); + Diagnostics.Assert(this.ImmediateBaseObjectIsEmpty, "BaseObject should be PSCustomObject for deserialized objects"); Diagnostics.Assert(value != null, "known objects are never null"); - this.immediateBaseObjectIsEmpty = false; + this.ImmediateBaseObjectIsEmpty = false; _immediateBaseObject = value; _adapterSet = GetMappedAdapter(_immediateBaseObject, GetTypeTable()); if (overrideTypeInfo) @@ -2044,42 +2054,6 @@ namespace System.Management.Automation } } - // This is toString value set on deserialization - private string _toStringFromDeserialization = null; - - internal bool preserveToString = false; - internal bool preserveToStringSet = false; - - internal bool PreserveToString - { - get - { - if (preserveToStringSet) - { - return preserveToString; - } - - preserveToStringSet = true; - if (InternalTypeNames.Count == 0) - { - return false; - } - - preserveToString = false; - - return preserveToString; - } - } - - /// - /// Sets the to string value on deserialization. - /// - internal string ToStringFromDeserialization - { - get => _toStringFromDeserialization; - set => _toStringFromDeserialization = value; - } - #endregion serialization /// @@ -2302,17 +2276,164 @@ namespace System.Management.Automation #endregion - #region Help formatting + internal bool IsDeserialized + { + get => _flags.HasFlag(PSObjectFlags.IsDeserialized); + set + { + if (value) + { + _flags |= PSObjectFlags.IsDeserialized; + } + else + { + _flags &= ~PSObjectFlags.IsDeserialized; + } + } + } + + private bool StoreTypeNameAndInstanceMembersLocally + { + get => _flags.HasFlag(PSObjectFlags.StoreTypeNameAndInstanceMembersLocally); + set + { + if (value) + { + _flags |= PSObjectFlags.StoreTypeNameAndInstanceMembersLocally; + } + else + { + _flags &= ~PSObjectFlags.StoreTypeNameAndInstanceMembersLocally; + } + } + } internal bool IsHelpObject { - get => _isHelpObject; - set => _isHelpObject = value; + get => _flags.HasFlag(PSObjectFlags.IsHelpObject); + set + { + if (value) + { + _flags |= PSObjectFlags.IsHelpObject; + } + else + { + _flags &= ~PSObjectFlags.IsHelpObject; + } + } } - private bool _isHelpObject = false; + internal bool HasGeneratedReservedMembers + { + get => _flags.HasFlag(PSObjectFlags.HasGeneratedReservedMembers); + set + { + if (value) + { + _flags |= PSObjectFlags.HasGeneratedReservedMembers; + } + else + { + _flags &= ~PSObjectFlags.HasGeneratedReservedMembers; + } + } + } - #endregion + internal bool ImmediateBaseObjectIsEmpty + { + get => _flags.HasFlag(PSObjectFlags.ImmediateBaseObjectIsEmpty); + set + { + if (value) + { + _flags |= PSObjectFlags.ImmediateBaseObjectIsEmpty; + } + else + { + _flags &= ~PSObjectFlags.ImmediateBaseObjectIsEmpty; + } + } + } + + /// + /// If 'this' is non-null, return this string as the ToString() for this wrapped object. + /// + internal string TokenText { get; set; } + + /// + /// Sets the 'ToString' value on deserialization. + /// + internal string ToStringFromDeserialization { get; set; } + + /// + /// This property contains a stream type used by the formatting system. + /// + internal WriteStreamType WriteStream { get; set; } + + /// + /// Members from the adapter of the object before it was serialized + /// Null for live objects but not null for deserialized objects. + /// + internal PSMemberInfoInternalCollection AdaptedMembers { get; set; } + + internal static DotNetAdapter DotNetStaticAdapter => s_dotNetStaticAdapter; + + internal static PSTraceSource MemberResolution => s_memberResolution; + + /// + /// Members from the adapter of the object before it was serialized + /// Null for live objects but not null for deserialized objects. + /// + internal PSMemberInfoInternalCollection ClrMembers { get; set; } + + internal static DotNetAdapter DotNetInstanceAdapter => s_dotNetInstanceAdapter; + + /// + /// Gets an instance member if it's name matches the predicate. Otherwise null. + /// + internal PSPropertyInfo GetFirstPropertyOrDefault(MemberNamePredicate predicate) + { + return Properties.FirstOrDefault(predicate); + } + + [Flags] + private enum PSObjectFlags : byte + { + None = 0, + + /// + /// This flag is set in deserialized shellobject. + /// + IsDeserialized = 0b00000001, + + /// + /// Set to true when the BaseObject is PSCustomObject. + /// + HasGeneratedReservedMembers = 0b00000010, + ImmediateBaseObjectIsEmpty = 0b00000100, + IsHelpObject = 0b00001000, + + /// + /// Indicate whether we store the instance members and type names locally + /// for this PSObject instance. + /// + StoreTypeNameAndInstanceMembersLocally = 0b00010000, + } + } + + /// + /// Specifies special stream write processing. + /// + internal enum WriteStreamType : byte + { + None, + Output, + Error, + Warning, + Verbose, + Debug, + Information } /// @@ -2347,7 +2468,7 @@ namespace System.Management.Automation AllPublicProperties = 0, String = 1, SpecificProperties = 2 - }; + } } #pragma warning restore 56500 diff --git a/src/System.Management.Automation/engine/ParameterBinderBase.cs b/src/System.Management.Automation/engine/ParameterBinderBase.cs index 39b08596ec..e25d6df506 100644 --- a/src/System.Management.Automation/engine/ParameterBinderBase.cs +++ b/src/System.Management.Automation/engine/ParameterBinderBase.cs @@ -850,7 +850,7 @@ namespace System.Management.Automation } var psobj = parameterValue as PSObject; - if (psobj != null && !psobj.immediateBaseObjectIsEmpty) + if (psobj != null && !psobj.ImmediateBaseObjectIsEmpty) { // See if the base object is of the same type or // as subclass of the parameter diff --git a/src/System.Management.Automation/engine/SessionStateDriveAPIs.cs b/src/System.Management.Automation/engine/SessionStateDriveAPIs.cs index b2eed9aeb5..b6af867457 100644 --- a/src/System.Management.Automation/engine/SessionStateDriveAPIs.cs +++ b/src/System.Management.Automation/engine/SessionStateDriveAPIs.cs @@ -88,7 +88,7 @@ namespace System.Management.Automation // set the return value to the first drive (should only be one). - if (!successObjects[0].immediateBaseObjectIsEmpty) + if (!successObjects[0].ImmediateBaseObjectIsEmpty) { result = (PSDriveInfo)successObjects[0].BaseObject; } diff --git a/src/System.Management.Automation/engine/ThirdPartyAdapter.cs b/src/System.Management.Automation/engine/ThirdPartyAdapter.cs index 75cfbd6020..8c2d485546 100644 --- a/src/System.Management.Automation/engine/ThirdPartyAdapter.cs +++ b/src/System.Management.Automation/engine/ThirdPartyAdapter.cs @@ -128,6 +128,30 @@ namespace System.Management.Automation return property; } + protected override PSProperty DoGetFirstPropertyOrDefault(object obj, MemberNamePredicate predicate) + { + PSAdaptedProperty property = null; + + try + { + property = _externalAdapter.GetFirstPropertyOrDefault(obj, predicate); + } + catch (Exception exception) + { + throw new ExtendedTypeSystemException( + "PSPropertyAdapter.GetProperty", + exception, + ExtendedTypeSystem.GetProperty, nameof(predicate), obj.ToString()); + } + + if (property != null) + { + InitializeProperty(property, obj); + } + + return property; + } + /// /// Ensures that the adapter and base object are set in the given PSAdaptedProperty. /// @@ -323,5 +347,22 @@ namespace System.Management.Automation /// Returns the type for a given property. /// public abstract string GetPropertyTypeName(PSAdaptedProperty adaptedProperty); + + /// + /// Returns a property if it's name matches the specified , otherwise null. + /// + /// An adapted property if the predicate matches, or null. + public virtual PSAdaptedProperty GetFirstPropertyOrDefault(object baseObject, MemberNamePredicate predicate) + { + foreach (var property in GetProperties(baseObject)) + { + if (predicate(property.Name)) + { + return property; + } + } + + return null; + } } } diff --git a/src/System.Management.Automation/engine/TypeTable.cs b/src/System.Management.Automation/engine/TypeTable.cs index 437cf401e6..d807885caf 100644 --- a/src/System.Management.Automation/engine/TypeTable.cs +++ b/src/System.Management.Automation/engine/TypeTable.cs @@ -3633,6 +3633,11 @@ namespace System.Management.Automation.Runspaces return PSObject.TransformMemberInfoCollection(GetMembers(types)); } + internal T GetFirstMemberOrDefault(ConsolidatedString types, MemberNamePredicate predicate) where T : PSMemberInfo + { + return GetMembers(types).FirstOrDefault(member => member is T && predicate(member.Name)) as T; + } + internal PSMemberInfoInternalCollection GetMembers(ConsolidatedString types) { if ((types == null) || string.IsNullOrEmpty(types.Key)) @@ -3640,8 +3645,7 @@ namespace System.Management.Automation.Runspaces return new PSMemberInfoInternalCollection(); } - PSMemberInfoInternalCollection result = _consolidatedMembers.GetOrAdd(types.Key, _memberFactoryFunc, types); - return result; + return _consolidatedMembers.GetOrAdd(types.Key, _memberFactoryFunc, types); } private PSMemberInfoInternalCollection MemberFactory(string k, ConsolidatedString types) @@ -4527,5 +4531,6 @@ namespace System.Management.Automation.Runspaces } #endregion internal methods + } } diff --git a/src/System.Management.Automation/engine/parser/Compiler.cs b/src/System.Management.Automation/engine/parser/Compiler.cs index 72ede0a445..38be189389 100644 --- a/src/System.Management.Automation/engine/parser/Compiler.cs +++ b/src/System.Management.Automation/engine/parser/Compiler.cs @@ -339,8 +339,8 @@ namespace System.Management.Automation.Language typeof(PSObject).GetMethod(nameof(PSObject.Base), staticFlags); internal static readonly PropertyInfo PSObject_BaseObject = typeof(PSObject).GetProperty(nameof(PSObject.BaseObject)); - internal static readonly FieldInfo PSObject_isDeserialized = - typeof(PSObject).GetField(nameof(PSObject.isDeserialized), instanceFlags); + internal static readonly PropertyInfo PSObject_IsDeserialized = + typeof(PSObject).GetProperty(nameof(PSObject.IsDeserialized), instanceFlags); internal static readonly MethodInfo PSObject_ToStringParser = typeof(PSObject).GetMethod(nameof(PSObject.ToStringParser), staticFlags, null, new[] { typeof(ExecutionContext), typeof(object) }, null); diff --git a/src/System.Management.Automation/engine/parser/TypeInferenceVisitor.cs b/src/System.Management.Automation/engine/parser/TypeInferenceVisitor.cs index a3c095b493..6e0d02d1c1 100644 --- a/src/System.Management.Automation/engine/parser/TypeInferenceVisitor.cs +++ b/src/System.Management.Automation/engine/parser/TypeInferenceVisitor.cs @@ -249,8 +249,8 @@ namespace System.Management.Automation } var members = isStatic - ? PSObject.dotNetStaticAdapter.BaseGetMembers(type) - : PSObject.dotNetInstanceAdapter.GetPropertiesAndMethods(type, false); + ? PSObject.DotNetStaticAdapter.BaseGetMembers(type) + : PSObject.DotNetInstanceAdapter.GetPropertiesAndMethods(type, false); if (filterToCall != null) { diff --git a/src/System.Management.Automation/engine/runtime/Binding/Binders.cs b/src/System.Management.Automation/engine/runtime/Binding/Binders.cs index 18d9449a54..94e6fd4e68 100644 --- a/src/System.Management.Automation/engine/runtime/Binding/Binders.cs +++ b/src/System.Management.Automation/engine/runtime/Binding/Binders.cs @@ -5386,7 +5386,7 @@ namespace System.Management.Automation.Language // If the target value is actually a deserialized PSObject, we should use the original value var psobj = value as PSObject; - if (psobj != null && psobj != AutomationNull.Value && !psobj.isDeserialized) + if (psobj != null && psobj != AutomationNull.Value && !psobj.IsDeserialized) { expr = Expression.Call(CachedReflectionInfo.PSObject_Base, expr); value = PSObject.Base(value); @@ -5650,7 +5650,7 @@ namespace System.Management.Automation.Language // // - If not, we want to use the base object, so that we might generate optimized code. var psobj = target.Value as PSObject; - bool isTargetDeserializedObject = (psobj != null) && (psobj.isDeserialized); + bool isTargetDeserializedObject = (psobj != null) && (psobj.IsDeserialized); object value = isTargetDeserializedObject ? target.Value : PSObject.Base(target.Value); var adapterSet = PSObject.GetMappedAdapter(value, typeTable); @@ -5703,7 +5703,7 @@ namespace System.Management.Automation.Language } } - if (_classScope != null && (target.LimitType == _classScope || target.LimitType.IsSubclassOf(_classScope)) && adapterSet.OriginalAdapter == PSObject.dotNetInstanceAdapter) + if (_classScope != null && (target.LimitType == _classScope || target.LimitType.IsSubclassOf(_classScope)) && adapterSet.OriginalAdapter == PSObject.DotNetInstanceAdapter) { List candidateMethods = null; foreach (var member in _classScope.GetMembers(BindingFlags.Instance | BindingFlags.FlattenHierarchy | BindingFlags.NonPublic)) @@ -5719,7 +5719,7 @@ namespace System.Management.Automation.Language if ((getMethod == null || getMethod.IsFamily || getMethod.IsPublic) && (setMethod == null || setMethod.IsFamily || setMethod.IsPublic)) { - memberInfo = new PSProperty(this.Name, PSObject.dotNetInstanceAdapter, target.Value, new DotNetAdapter.PropertyCacheEntry(propertyInfo)); + memberInfo = new PSProperty(this.Name, PSObject.DotNetInstanceAdapter, target.Value, new DotNetAdapter.PropertyCacheEntry(propertyInfo)); } } else @@ -5729,7 +5729,7 @@ namespace System.Management.Automation.Language { if (fieldInfo.IsFamily) { - memberInfo = new PSProperty(this.Name, PSObject.dotNetInstanceAdapter, target.Value, new DotNetAdapter.PropertyCacheEntry(fieldInfo)); + memberInfo = new PSProperty(this.Name, PSObject.DotNetInstanceAdapter, target.Value, new DotNetAdapter.PropertyCacheEntry(fieldInfo)); } } else @@ -5767,7 +5767,7 @@ namespace System.Management.Automation.Language else { DotNetAdapter.MethodCacheEntry method = new DotNetAdapter.MethodCacheEntry(candidateMethods.ToArray()); - memberInfo = PSMethod.Create(this.Name, PSObject.dotNetInstanceAdapter, null, method); + memberInfo = PSMethod.Create(this.Name, PSObject.DotNetInstanceAdapter, null, method); } } } @@ -5788,7 +5788,7 @@ namespace System.Management.Automation.Language if (isTargetDeserializedObject) { restrictions = restrictions.Merge(BindingRestrictions.GetExpressionRestriction( - Expression.Field(target.Expression.Cast(typeof(PSObject)), CachedReflectionInfo.PSObject_isDeserialized))); + Expression.Property(target.Expression.Cast(typeof(PSObject)), CachedReflectionInfo.PSObject_IsDeserialized))); } if (hasTypeTableMember) @@ -6744,8 +6744,8 @@ namespace System.Management.Automation.Language // If the target value is a PSObject and its base object happens to be a Hashtable or ArrayList, // we might have three interesting cases here: // (1) the target value could be a regular PSObject that wraps the Hashtable/ArrayList, i.e. $target = [PSObject]::AsPSObject($hash) - // (2) the target value could be a deserialized object (PSObject) with the 'isDeserialized' field to be false, i.e. deserialized Hashtable/ArrayList/Dictionary[string, string] - // (3) the target value could be a deserialized object (PSObject) with the 'isDeserialized' field to be true, i.e. deserialized XmlElement + // (2) the target value could be a deserialized object (PSObject) with the 'IsDeserialized' property to be false, i.e. deserialized Hashtable/ArrayList/Dictionary[string, string] + // (3) the target value could be a deserialized object (PSObject) with the 'IsDeserialized' property to be true, i.e. deserialized XmlElement // For the first two cases, it's OK to call a .NET method from the base object, such as $target.Add(). // For the third case, calling a .NET method from the base object is incorrect, because the original type of the deserialized object doesn't have the method. // example: XmlElement derives from IEnumerable, so it's treated as a container object when powershell does the serialization -- using an ArrayList to hold @@ -6761,11 +6761,11 @@ namespace System.Management.Automation.Language // If we get here, then the target value should have 'isDeserialized == false', otherwise we cannot get a .NET methodInfo // from _getMemberBinder.GetPSMemberInfo(). This is because when 'isDeserialized' is true, we use the PSObject to find the // corresponding Adapter -- PSObjectAdapter, which cannot be optimized. - Diagnostics.Assert(psObj.isDeserialized == false, + Diagnostics.Assert(psObj.IsDeserialized == false, "isDeserialized should be false, because if not, we cannot get a .NET method/parameterizedProperty from GetPSMemberInfo"); restrictions = restrictions.Merge(BindingRestrictions.GetExpressionRestriction( - Expression.Not(Expression.Field(target.Expression.Cast(typeof(PSObject)), CachedReflectionInfo.PSObject_isDeserialized)))); + Expression.Not(Expression.Property(target.Expression.Cast(typeof(PSObject)), CachedReflectionInfo.PSObject_IsDeserialized)))); } } @@ -6976,7 +6976,7 @@ namespace System.Management.Automation.Language { MethodInfo result = null; - var psMethod = PSObject.dotNetInstanceAdapter.GetDotNetMethod(PSObject.Base(target.Value), methodName); + var psMethod = PSObject.DotNetInstanceAdapter.GetDotNetMethod(PSObject.Base(target.Value), methodName); if (psMethod != null) { var data = (DotNetAdapter.MethodCacheEntry)psMethod.adapterData; diff --git a/src/System.Management.Automation/engine/runtime/Operations/MiscOps.cs b/src/System.Management.Automation/engine/runtime/Operations/MiscOps.cs index 4dbed59c71..d4c6f8f10a 100644 --- a/src/System.Management.Automation/engine/runtime/Operations/MiscOps.cs +++ b/src/System.Management.Automation/engine/runtime/Operations/MiscOps.cs @@ -659,7 +659,7 @@ namespace System.Management.Automation // GetSteppablePipeline() is called on an arbitrary script block with the intention // of invoking it. So the trustworthiness is defined by the trustworthiness of the // script block's language mode. - bool isTrusted = (scriptBlock.LanguageMode == PSLanguageMode.FullLanguage); + bool isTrusted = scriptBlock.LanguageMode == PSLanguageMode.FullLanguage; foreach (var commandAst in pipelineAst.PipelineElements.Cast()) { @@ -1888,8 +1888,7 @@ namespace System.Management.Automation rte.ErrorRecord.SetInvocationInfo(new InvocationInfo(null, extent, context)); PSObject errorWrap = PSObject.AsPSObject(new ErrorRecord(rte.ErrorRecord, rte)); - PSNoteProperty note = new PSNoteProperty("writeErrorStream", true); - errorWrap.Properties.Add(note); + errorWrap.WriteStream = WriteStreamType.Error; // If this is an error pipe for a hosting application (i.e.: no downstream cmdlet), // and we are logging, then create a temporary PowerShell to log the error. diff --git a/src/System.Management.Automation/engine/serialization.cs b/src/System.Management.Automation/engine/serialization.cs index b69f2434f4..048a7491d7 100644 --- a/src/System.Management.Automation/engine/serialization.cs +++ b/src/System.Management.Automation/engine/serialization.cs @@ -1100,7 +1100,7 @@ namespace System.Management.Automation moSource = source as PSObject; } - if (moSource != null && !moSource.immediateBaseObjectIsEmpty) + if (moSource != null && !moSource.ImmediateBaseObjectIsEmpty) { // check if source is of type secure string secureString = moSource.ImmediateBaseObject as SecureString; @@ -1172,7 +1172,7 @@ namespace System.Management.Automation bool sourceHandled = false; PSObject moSource = source as PSObject; - if (moSource != null && !moSource.immediateBaseObjectIsEmpty) + if (moSource != null && !moSource.ImmediateBaseObjectIsEmpty) { // Check if baseObject is primitive known type object baseObject = moSource.ImmediateBaseObject; @@ -1203,7 +1203,7 @@ namespace System.Management.Automation IDictionary dictionary = null; // If passed in object is PSObject with no baseobject, return false. - if (mshSource != null && mshSource.immediateBaseObjectIsEmpty) + if (mshSource != null && mshSource.ImmediateBaseObjectIsEmpty) { return false; } @@ -1252,7 +1252,7 @@ namespace System.Management.Automation // So on roundtrip it will show up as List. // We serialize properties of enumerable and on deserialization mark the object as Deserialized. // So if object is marked deserialized, we should write properties. - if (ct == ContainerType.Enumerable || (mshSource != null && mshSource.isDeserialized)) + if (ct == ContainerType.Enumerable || (mshSource != null && mshSource.IsDeserialized)) { PSObject sourceAsPSObject = PSObject.AsPSObject(source); PSMemberInfoInternalCollection specificPropertiesToSerialize = SerializationUtilities.GetSpecificPropertiesToSerialize(sourceAsPSObject, AllPropertiesCollection, _typeTable); @@ -1460,7 +1460,7 @@ namespace System.Management.Automation bool isPSObject = false; bool isCimInstance = false; - if (!mshSource.immediateBaseObjectIsEmpty) + if (!mshSource.ImmediateBaseObjectIsEmpty) { do // false loop { @@ -1495,7 +1495,7 @@ namespace System.Management.Automation bool writeToString = true; if (mshSource.ToStringFromDeserialization == null) // continue to write ToString from deserialized objects, but... { - if (mshSource.immediateBaseObjectIsEmpty) // ... don't write ToString for property bags + if (mshSource.ImmediateBaseObjectIsEmpty) // ... don't write ToString for property bags { writeToString = false; } @@ -2327,7 +2327,7 @@ namespace System.Management.Automation if (0 != (_context.options & SerializationOptions.PreserveSerializationSettingOfOriginal)) { - if ((pso.isDeserialized) && (depth <= 0)) + if ((pso.IsDeserialized) && (depth <= 0)) { return 1; } @@ -3463,9 +3463,9 @@ namespace System.Management.Automation } // process properties that were originally "adapted" properties - if (deserializedObject.adaptedMembers != null) + if (deserializedObject.AdaptedMembers != null) { - foreach (PSMemberInfo deserializedMemberInfo in deserializedObject.adaptedMembers) + foreach (PSMemberInfo deserializedMemberInfo in deserializedObject.AdaptedMembers) { PSPropertyInfo deserializedProperty = deserializedMemberInfo as PSPropertyInfo; if (deserializedProperty == null) @@ -3495,7 +3495,7 @@ namespace System.Management.Automation } // skip adapted properties - if ((deserializedObject.adaptedMembers != null) && (deserializedObject.adaptedMembers[deserializedProperty.Name] != null)) + if ((deserializedObject.AdaptedMembers != null) && (deserializedObject.AdaptedMembers[deserializedProperty.Name] != null)) { continue; } @@ -3643,7 +3643,7 @@ namespace System.Management.Automation else if (IsNextElement(SerializationStrings.ToStringElementTag)) { dso.ToStringFromDeserialization = ReadDecodedElementString(SerializationStrings.ToStringElementTag); - dso.InstanceMembers.Add(PSObject.dotNetInstanceAdapter.GetDotNetMethod(dso, "ToString")); + dso.InstanceMembers.Add(PSObject.DotNetInstanceAdapter.GetDotNetMethod(dso, "ToString")); PSGetMemberBinder.SetHasInstanceMember("ToString"); // Fix for Win8:75437 // The TokenText property is used in type conversion and it is not being populated during deserialization @@ -3822,15 +3822,15 @@ namespace System.Management.Automation // Since we are adding baseobject properties as propertybag, // mark the object as deserialized. - dso.isDeserialized = true; - dso.adaptedMembers = new PSMemberInfoInternalCollection(); + dso.IsDeserialized = true; + dso.AdaptedMembers = new PSMemberInfoInternalCollection(); // Add the GetType method to the instance members, so that it works on deserialized psobjects - dso.InstanceMembers.Add(PSObject.dotNetInstanceAdapter.GetDotNetMethod(dso, "GetType")); + dso.InstanceMembers.Add(PSObject.DotNetInstanceAdapter.GetDotNetMethod(dso, "GetType")); PSGetMemberBinder.SetHasInstanceMember("GetType"); // Set Clr members to a collection which is empty - dso.clrMembers = new PSMemberInfoInternalCollection(); + dso.ClrMembers = new PSMemberInfoInternalCollection(); if (ReadStartElementAndHandleEmpty(SerializationStrings.AdapterProperties)) { @@ -3840,7 +3840,7 @@ namespace System.Management.Automation string property = ReadNameAttribute(); object value = ReadOneObject(); PSProperty prop = new PSProperty(property, value); - dso.adaptedMembers.Add(prop); + dso.AdaptedMembers.Add(prop); } ReadEndElement(); diff --git a/test/xUnit/csharp/test_PSObject.cs b/test/xUnit/csharp/test_PSObject.cs new file mode 100644 index 0000000000..46497065fd --- /dev/null +++ b/test/xUnit/csharp/test_PSObject.cs @@ -0,0 +1,127 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using System; +using System.Management.Automation.Language; +using Xunit; + +namespace PSTests.Parallel +{ + using System.Linq; + using System.Management.Automation; + using System.Management.Automation.Runspaces; + using System.Xml; + + using Microsoft.Management.Infrastructure; + + public static class PSObjectTests + { + [Fact] + public static void TestEmptyObjectHasNoProperty() + { + var psObject = new PSObject(); + var actual = psObject.GetFirstPropertyOrDefault(name => true); + Assert.Null(actual); + } + + [Fact] + public static void TestWrappedDateTimeHasReflectedMember() + { + var psObject = new PSObject(DateTime.Now); + var member = psObject.GetFirstPropertyOrDefault(name => name == "DayOfWeek"); + Assert.NotNull(member); + Assert.Equal("DayOfWeek", member.Name); + } + + [Fact] + public static void TestAdaptedMember() + { + var psObject = new PSObject(DateTime.Now); + psObject.Members.Add(new PSNoteProperty("NewMember", "AValue")); + var member = psObject.GetFirstPropertyOrDefault(name => name == "NewMember"); + Assert.NotNull(member); + Assert.Equal("NewMember", member.Name); + } + + [Fact] + public static void TestShadowedMember() + { + var psObject = new PSObject(DateTime.Now); + psObject.Members.Add(new PSNoteProperty("DayOfWeek", "AValue")); + var member = psObject.GetFirstPropertyOrDefault(name => name == "DayOfWeek"); + Assert.NotNull(member); + Assert.Equal("DayOfWeek", member.Name); + Assert.Equal("AValue", member.Value); + } + + [Fact] + public static void TestMemberSetIsNotProperty() + { + var psObject = new PSObject(DateTime.Now); + var psNoteProperty = new PSNoteProperty("NewMember", "AValue"); + psObject.Members.Add(psNoteProperty); + psObject.Members.Add(new PSMemberSet("NewMemberSet", new[] { psNoteProperty })); + + var member = psObject.GetFirstPropertyOrDefault(name => name == "NewMemberSet"); + Assert.Null(member); + } + + [Fact] + public static void TestMemberSet() + { + var psObject = new PSObject(DateTime.Now); + var psNoteProperty = new PSNoteProperty("NewMember", "AValue"); + psObject.Members.Add(psNoteProperty); + psObject.Members.Add(new PSMemberSet("NewMemberSet", new[] { psNoteProperty })); + + var member = psObject.Members.FirstOrDefault(name => name == "NewMemberSet"); + Assert.NotNull(member); + Assert.Equal("NewMemberSet", member.Name); + } + + [Fact] + public static void TextXmlElementMember() + { + var doc = new XmlDocument(); + var root = doc.CreateElement("root"); + doc.AppendChild(root); + var firstChild = doc.CreateElement("elem1"); + root.AppendChild(firstChild); + root.InsertAfter(doc.CreateElement("elem2"), firstChild); + + var psObject = new PSObject(root); + var member = psObject.GetFirstPropertyOrDefault(name => name.StartsWith("elem")); + Assert.Equal("elem1", member.Name); + } + + [Fact] + public static void TextXmlAttributeMember() + { + var doc = new XmlDocument(); + var root = doc.CreateElement("root"); + doc.AppendChild(root); + root.SetAttribute("attr", "value"); + root.AppendChild(doc.CreateElement("elem")); + + var psObject = new PSObject(root); + var member = psObject.GetFirstPropertyOrDefault(name => name.StartsWith("attr")); + Assert.Equal("attr", member.Name); + } + + [SkippableFact] + public static void TestCimInstanceProperty() + { + Skip.IfNot(Platform.IsWindows); + var iss = InitialSessionState.CreateDefault2(); + iss.Commands.Add(new SessionStateCmdletEntry("Get-CimInstance", typeof(Microsoft.Management.Infrastructure.CimCmdlets.GetCimInstanceCommand), null)); + using (var ps = PowerShell.Create(iss)) + { + ps.AddCommand("Get-CimInstance").AddParameter("ClassName", "Win32_BIOS"); + var res = ps.Invoke().FirstOrDefault(); + Assert.NotNull(res); + var member = res.GetFirstPropertyOrDefault(name => name == "Name"); + Assert.NotNull(member); + } + } + } +} diff --git a/test/xUnit/xUnit.tests.csproj b/test/xUnit/xUnit.tests.csproj index 227405930e..ff75f65925 100644 --- a/test/xUnit/xUnit.tests.csproj +++ b/test/xUnit/xUnit.tests.csproj @@ -1,6 +1,6 @@  - + PowerShell xUnit Tests @@ -16,9 +16,10 @@ - - - + + + +