/********************************************************************++ Copyright (c) Microsoft Corporation. All rights reserved. --********************************************************************/ using System; using System.Collections; using System.Collections.Concurrent; using System.Collections.Generic; using System.Collections.ObjectModel; using System.Data; using System.Dynamic; using System.Linq; using System.Linq.Expressions; using System.Management.Automation.Language; using System.Reflection; using System.Globalization; using System.Runtime.CompilerServices; using System.Text; using System.Xml; using System.Management.Automation; using System.Management.Automation.Internal; using System.Management.Automation.Runspaces; using System.Runtime.Serialization; using System.ComponentModel; using System.Diagnostics.CodeAnalysis; using Microsoft.Management.Infrastructure; #if !CORECLR using System.DirectoryServices; #endif #pragma warning disable 1634, 1691 // Stops compiler from warning about unknown warnings #pragma warning disable 56500 namespace System.Management.Automation { /// /// Wraps an object providing alternate views of the available members /// and ways to extend them. Members can be methods, properties, /// parameterized properties, etc. /// /// /// It is permitted to subclass /// but there is no established scenario for doing this, nor has it been tested. /// [TypeDescriptionProvider(typeof(PSObjectTypeDescriptionProvider))] [Serializable] public class PSObject : IFormattable, IComparable, ISerializable, IDynamicMetaObjectProvider { #region constructors #region private to the constructors internal TypeTable GetTypeTable() { TypeTable typeTable; if (_typeTable != null && _typeTable.TryGetTarget(out typeTable)) { return typeTable; } ExecutionContext context = LocalPipeline.GetExecutionContextFromTLS(); if (context != null) { return context.TypeTable; } return null; } internal static T TypeTableGetMemberDelegate(PSObject msjObj, string name) where T : PSMemberInfo { TypeTable table = msjObj.GetTypeTable(); return TypeTableGetMemberDelegate(msjObj, table, name); } private static T TypeTableGetMemberDelegate(PSObject msjObj, TypeTable typeTableToUse, string name) where T : PSMemberInfo { if (typeTableToUse == null) { return null; } PSMemberInfoInternalCollection allMembers = typeTableToUse.GetMembers(msjObj.InternalTypeNames); PSMemberInfo member = allMembers[name]; if (member == null) { PSObject.memberResolution.WriteLine("\"{0}\" NOT present in type table.", name); return null; } T memberAsT = member as T; if (memberAsT != null) { 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}.", name, member.GetType(), typeof(T)); return null; } internal static PSMemberInfoInternalCollection TypeTableGetMembersDelegate(PSObject msjObj) where T : PSMemberInfo { TypeTable table = msjObj.GetTypeTable(); return TypeTableGetMembersDelegate(msjObj, table); } internal static PSMemberInfoInternalCollection TypeTableGetMembersDelegate(PSObject msjObj, TypeTable typeTableToUse) where T : PSMemberInfo { if (typeTableToUse == null) { return new PSMemberInfoInternalCollection(); } PSMemberInfoInternalCollection members = typeTableToUse.GetMembers(msjObj.InternalTypeNames); PSObject.memberResolution.WriteLine("Type table members: {0}.", members.Count); return members; } private static T AdapterGetMemberDelegate(PSObject msjObj, string name) where T : PSMemberInfo { if (msjObj.isDeserialized) { 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); return adaptedMember; } T retValue = msjObj.InternalAdapter.BaseGetMember(msjObj._immediateBaseObject, name); PSObject.memberResolution.WriteLine("Adapted member: {0}.", retValue == null ? "not found" : retValue.Name); return retValue; } internal static PSMemberInfoInternalCollection TransformMemberInfoCollection(PSMemberInfoCollection source) where T : PSMemberInfo where U : PSMemberInfo { if (typeof(T) == typeof(U)) { // If the types are the same, don't make a copy, return the cached collection. return source as PSMemberInfoInternalCollection; } PSMemberInfoInternalCollection returnValue = new PSMemberInfoInternalCollection(); foreach (T member in source) { U tAsU = member as U; if (tAsU != null) { returnValue.Add(tAsU); } } return returnValue; } private static PSMemberInfoInternalCollection AdapterGetMembersDelegate(PSObject msjObj) where T : PSMemberInfo { if (msjObj.isDeserialized) { if (msjObj.adaptedMembers == null) { return new PSMemberInfoInternalCollection(); } 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); return retValue; } private static PSMemberInfoInternalCollection DotNetGetMembersDelegate(PSObject msjObj) where T : PSMemberInfo { // Don't lookup dotnet members if the object doesn't insist. if (null != msjObj.InternalAdapterSet.DotNetAdapter) { PSMemberInfoInternalCollection retValue = msjObj.InternalAdapterSet.DotNetAdapter.BaseGetMembers(msjObj._immediateBaseObject); PSObject.memberResolution.WriteLine("DotNet members: {0}.", retValue.VisibleCount); return retValue; } return new PSMemberInfoInternalCollection(); } private static T DotNetGetMemberDelegate(PSObject msjObj, string name) where T : PSMemberInfo { // Don't lookup dotnet member if the object doesn't insist. if (null != msjObj.InternalAdapterSet.DotNetAdapter) { T retValue = msjObj.InternalAdapterSet.DotNetAdapter.BaseGetMember(msjObj._immediateBaseObject, name); PSObject.memberResolution.WriteLine("DotNet member: {0}.", retValue == null ? "not found" : retValue.Name); return retValue; } return null; } private static Collection> s_memberCollection = GetMemberCollection(PSMemberViewTypes.All); private static Collection> s_methodCollection = GetMethodCollection(); private static Collection> s_propertyCollection = GetPropertyCollection(PSMemberViewTypes.All); /// /// A collection of delegates to get Extended/Adapted/Dotnet members based on the /// /// /// /// A filter to select Extended/Adapted/Dotnet view of the object /// /// internal static Collection> GetMemberCollection(PSMemberViewTypes viewType) { return GetMemberCollection(viewType, null); } /// /// A collection of delegates to get Extended/Adapted/Dotnet members based on the /// /// /// /// A filter to select Extended/Adapted/Dotnet view of the object /// /// /// Backup type table to use if there is no execution context associated with the current thread /// /// internal static Collection> GetMemberCollection( PSMemberViewTypes viewType, TypeTable backupTypeTable) { Collection> returnValue = new Collection>(); if ((viewType & PSMemberViewTypes.Extended) == PSMemberViewTypes.Extended) { if (backupTypeTable == null) { returnValue.Add(new CollectionEntry( PSObject.TypeTableGetMembersDelegate, PSObject.TypeTableGetMemberDelegate, true, true, "type table members")); } else { returnValue.Add(new CollectionEntry( delegate (PSObject msjObj) { return TypeTableGetMembersDelegate(msjObj, backupTypeTable); }, delegate (PSObject msjObj, string name) { return TypeTableGetMemberDelegate(msjObj, backupTypeTable, name); }, true, true, "type table members")); } } if ((viewType & PSMemberViewTypes.Adapted) == PSMemberViewTypes.Adapted) { returnValue.Add(new CollectionEntry( PSObject.AdapterGetMembersDelegate, PSObject.AdapterGetMemberDelegate, false, false, "adapted members")); } if ((viewType & PSMemberViewTypes.Base) == PSMemberViewTypes.Base) { returnValue.Add(new CollectionEntry( PSObject.DotNetGetMembersDelegate, PSObject.DotNetGetMemberDelegate, false, false, "clr members")); } return returnValue; } private static Collection> GetMethodCollection() { Collection> returnValue = new Collection>(); returnValue.Add(new CollectionEntry( PSObject.TypeTableGetMembersDelegate, PSObject.TypeTableGetMemberDelegate, true, true, "type table members")); returnValue.Add(new CollectionEntry( PSObject.AdapterGetMembersDelegate, PSObject.AdapterGetMemberDelegate, false, false, "adapted members")); returnValue.Add(new CollectionEntry( PSObject.DotNetGetMembersDelegate, PSObject.DotNetGetMemberDelegate, false, false, "clr members")); return returnValue; } /// /// A collection of delegates to get Extended/Adapted/Dotnet properties based on the /// /// /// /// A filter to select Extended/Adapted/Dotnet view of the object /// /// internal static Collection> GetPropertyCollection( PSMemberViewTypes viewType) { return GetPropertyCollection(viewType, null); } /// /// A collection of delegates to get Extended/Adapted/Dotnet properties based on the /// /// /// /// A filter to select Extended/Adapted/Dotnet view of the object /// /// /// Backup type table to use if there is no execution context associated with the current thread /// /// internal static Collection> GetPropertyCollection( PSMemberViewTypes viewType, TypeTable backupTypeTable) { Collection> returnValue = new Collection>(); if ((viewType & PSMemberViewTypes.Extended) == PSMemberViewTypes.Extended) { if (backupTypeTable == null) { returnValue.Add(new CollectionEntry( PSObject.TypeTableGetMembersDelegate, PSObject.TypeTableGetMemberDelegate, true, true, "type table members")); } else { returnValue.Add(new CollectionEntry( delegate (PSObject msjObj) { return TypeTableGetMembersDelegate(msjObj, backupTypeTable); }, delegate (PSObject msjObj, string name) { return TypeTableGetMemberDelegate(msjObj, backupTypeTable, name); }, true, true, "type table members")); } } if ((viewType & PSMemberViewTypes.Adapted) == PSMemberViewTypes.Adapted) { returnValue.Add(new CollectionEntry( PSObject.AdapterGetMembersDelegate, PSObject.AdapterGetMemberDelegate, false, false, "adapted members")); } if ((viewType & PSMemberViewTypes.Base) == PSMemberViewTypes.Base) { returnValue.Add(new CollectionEntry( PSObject.DotNetGetMembersDelegate, PSObject.DotNetGetMemberDelegate, false, false, "clr members")); } return returnValue; } private void CommonInitialization(object obj) { Diagnostics.Assert(obj != null, "checked by callers"); if (obj is PSCustomObject) { this.immediateBaseObjectIsEmpty = true; } _immediateBaseObject = obj; var context = LocalPipeline.GetExecutionContextFromTLS(); _typeTable = context != null ? context.TypeTableWeakReference : null; } 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 PSObject.AdapterSet s_cimInstanceAdapter = new PSObject.AdapterSet(new ThirdPartyAdapter(typeof(Microsoft.Management.Infrastructure.CimInstance), new Microsoft.PowerShell.Cim.CimInstanceAdapter()), PSObject.dotNetInstanceAdapter); #if !CORECLR // WMIv1/ADSI Adapters Not Supported in PowerShell Core private static readonly AdapterSet managementObjectAdapter = new AdapterSet(new ManagementObjectAdapter(), dotNetInstanceAdapter); private static readonly AdapterSet managementClassAdapter = new AdapterSet(new ManagementClassApdapter(), dotNetInstanceAdapter); private static readonly AdapterSet directoryEntryAdapter = new AdapterSet(new DirectoryEntryAdapter(), dotNetInstanceAdapter); #endif private static readonly AdapterSet dataRowViewAdapter = new AdapterSet(new DataRowViewAdapter(), s_baseAdapterForAdaptedObjects); private static readonly AdapterSet 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(); private static readonly List> s_adapterSetMappers = new List> { MappedInternalAdapterSet }; internal static void RegisterAdapterMapping(Func mapper) { lock (s_adapterSetMappers) { s_adapterSetMappers.Add(mapper); } } private static AdapterSet MappedInternalAdapterSet(object obj) { if (obj is PSMemberSet) { return PSObject.s_mshMemberSetAdapter; } if (obj is PSObject) { return PSObject.s_mshObjectAdapter; } if (obj is CimInstance) { return PSObject.s_cimInstanceAdapter; } #if !CORECLR // WMIv1/ADSI Adapters Not Supported in PowerShell Core if (obj is ManagementClass) { return PSObject.managementClassAdapter; } if (obj is ManagementBaseObject) { return PSObject.managementObjectAdapter; } if (obj is DirectoryEntry) { return PSObject.directoryEntryAdapter; } #endif if (obj is DataRowView) { return PSObject.dataRowViewAdapter; } if (obj is DataRow) { return PSObject.dataRowAdapter; } if (obj is XmlNode) { return PSObject.s_xmlNodeAdapter; } return null; } /// /// Returns the adapter corresponding to obj.GetType() /// /// the adapter set corresponding to obj.GetType() internal static AdapterSet GetMappedAdapter(object obj, TypeTable typeTable) { Type objectType = obj.GetType(); if (typeTable != null) { PSObject.AdapterSet adapter = typeTable.GetTypeAdapter(objectType); if (adapter != null) { // We don't cache results found via the type table because type tables may differ b/w runspaces, // our cache is app domain wide, and the key is simply the type. return adapter; } } AdapterSet result; if (s_adapterMapping.TryGetValue(objectType, out result)) { return result; } lock (s_adapterSetMappers) { foreach (var mapper in s_adapterSetMappers) { result = mapper(obj); if (result != null) { break; } } } if (result == null) { if (objectType.IsCOMObject) { // All WinRT types are COM types. // All WinRT types would contain the TypeAttributes flag being set to WindowsRunTime. if (WinRTHelper.IsWinRTType(objectType)) { result = PSObject.s_dotNetInstanceAdapterSet; } // We are not using IsAssignableFrom because we want to avoid // using the COM adapters for Primary Interop Assembly types // and they derive from System.__ComObject. // We are not using Type.Equals because System.__ComObject is // not public. else if (objectType.FullName.Equals("System.__ComObject")) { // We don't cache the adapter set for COM objects because the adapter varies depending on the COM object, but often // (typically), the runtime type is always the same. That's why this if statement is here and returns. ComTypeInfo info = ComTypeInfo.GetDispatchTypeInfo(obj); return info != null ? new AdapterSet(new ComAdapter(info), dotNetInstanceAdapter) : PSObject.s_dotNetInstanceAdapterSet; } else { ComTypeInfo info = ComTypeInfo.GetDispatchTypeInfo(obj); result = info != null ? new AdapterSet(new DotNetAdapterWithComTypeName(info), null) : PSObject.s_dotNetInstanceAdapterSet; } } else { result = PSObject.s_dotNetInstanceAdapterSet; } } var existingOrNew = s_adapterMapping.GetOrAdd(objectType, result); Diagnostics.Assert(existingOrNew == result, "There is a logic error in caching adapter sets."); return result; } #endregion internal static AdapterSet CreateThirdPartyAdapterSet(Type adaptedType, PSPropertyAdapter adapter) { return new AdapterSet(new ThirdPartyAdapter(adaptedType, adapter), s_baseAdapterForAdaptedObjects); } #endregion private to the constructors /// /// Initializes a new instance of PSObject with an PSCustomObject BaseObject /// public PSObject() { CommonInitialization(PSCustomObject.SelfInstance); } /// /// Initializes a new instance of PSObject wrapping obj (accessible through BaseObject). /// /// object we are wrapping /// if is null [SuppressMessage("Microsoft.Naming", "CA1720:IdentifiersShouldNotContainTypeNames", MessageId = "obj", Justification = "This is shipped as part of V1. Retaining this for backward compatibility.")] public PSObject(object obj) { if (obj == null) { throw PSTraceSource.NewArgumentNullException("obj"); } CommonInitialization(obj); } /// /// Creates a PSObject from an ISerializable context /// /// Serialization information for this instance /// The streaming context for this instance protected PSObject(SerializationInfo info, StreamingContext context) { if (info == null) { throw PSTraceSource.NewArgumentNullException("info"); } string serializedData = info.GetValue("CliXml", typeof(string)) as string; if (serializedData == null) { throw PSTraceSource.NewArgumentNullException("info"); } PSObject result = PSObject.AsPSObject(PSSerializer.Deserialize(serializedData)); CommonInitialization(result.ImmediateBaseObject); CopyDeserializerFields(source: result, target: this); } internal static PSObject ConstructPSObjectFromSerializationInfo(SerializationInfo info, StreamingContext context) { return new PSObject(info, context); } #endregion constructors #region fields #region instance fields private object _lockObject = new Object(); /// /// If this is non-null return this string as the ToString() for this wrapped object. /// internal string TokenText; /// /// This is the main field in the class representing /// the System.Object we are encapsulating /// private object _immediateBaseObject; private WeakReference _typeTable; /// /// This is the adapter that will depend on the type of baseObject. /// internal Adapter InternalAdapter { get { return InternalAdapterSet.OriginalAdapter; } set { InternalAdapterSet.OriginalAdapter = value; } } /// /// This is the adapter that is used to resolve the base dotnet members for an /// adapted object. If an object is not adapted, this will be null. /// /// /// If an object is not adapted, InternalAdapter will use the dotnet adapter. /// So there is no point falling back to the same dotnet adapter. /// /// If an object is adapted, this adapter will be used to resolve the dotnet /// members. /// internal Adapter InternalBaseDotNetAdapter { get { return InternalAdapterSet.DotNetAdapter; } } /// /// This is the adapter set that will contain the adapter of the baseObject /// and the ultimate .net member lookup adapter. /// See for explanation. /// private AdapterSet InternalAdapterSet { get { if (null == _adapterSet) { lock (_lockObject) { if (null == _adapterSet) { _adapterSet = GetMappedAdapter(_immediateBaseObject, GetTypeTable()); } } } return _adapterSet; } } 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) { var psobj = obj as PSObject; if (psobj != null) { 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 = false; /// /// 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 /// public PSMemberInfoCollection Members { get { if (_members == null) { lock (_lockObject) { if (_members == null) { _members = new PSMemberInfoIntegratingCollection(this, s_memberCollection); } } } return _members; } } private PSMemberInfoIntegratingCollection _members; /// /// Gets the Property collection, or the members that are actually properties. /// public PSMemberInfoCollection Properties { get { if (_properties == null) { lock (_lockObject) { if (_properties == null) { _properties = new PSMemberInfoIntegratingCollection(this, s_propertyCollection); } } } return _properties; } } private PSMemberInfoIntegratingCollection _properties; /// /// Gets the Method collection, or the members that are actually methods. /// public PSMemberInfoCollection Methods { get { if (_methods == null) { lock (_lockObject) { if (_methods == null) { _methods = new PSMemberInfoIntegratingCollection(this, s_methodCollection); } } } return _methods; } } private PSMemberInfoIntegratingCollection _methods; /// /// Gets the object we are directly wrapping. /// /// If the ImmediateBaseObject is another PSObject, /// that PSObject will be returned. public object ImmediateBaseObject { get { return _immediateBaseObject; } } /// /// Gets the object we are wrapping. /// /// If the ImmediateBaseObject is another PSObject, this property /// will return its BaseObject. public object BaseObject { get { object returnValue = null; PSObject mshObj = this; do { returnValue = mshObj._immediateBaseObject; mshObj = returnValue as PSObject; } while (mshObj != null); return returnValue; } } /// /// Gets the type names collection initially containing the object type hierarchy. /// public Collection TypeNames { get { var result = InternalTypeNames; if (result.IsReadOnly) { lock (_lockObject) { // Check again after acquiring the lock to ensure some other thread didn't make the copy. if (result.IsReadOnly) { _typeNames = s_typeNamesResurrectionTable.GetValue( GetKeyForResurrectionTables(this), _ => new ConsolidatedString(_typeNames)); object baseObj = BaseObject; // In most cases, the TypeNames will be modified after it's returned if (baseObj != null) { PSVariableAssignmentBinder.NoteTypeHasInstanceMemberOrTypeName(baseObj.GetType()); } return _typeNames; } } } return result; } } internal ConsolidatedString InternalTypeNames { get { if (_typeNames == null) { lock (_lockObject) { if (_typeNames == null) { if (!s_typeNamesResurrectionTable.TryGetValue(GetKeyForResurrectionTables(this), out _typeNames)) { // We don't cache this typename in the resurrection table because it's cached in the psobject, // and the assumption is we'll usually get the value directly from the PSObject, so it's not // needed in the resurrection table. // // If we hand out the typename to a client that could change it (through the public property // TypeNames), then we'll need to store the copy in the resurrection table. _typeNames = InternalAdapter.BaseGetTypeNameHierarchy(_immediateBaseObject); } } } } return _typeNames; } set { _typeNames = value; } } internal static ConsolidatedString GetTypeNames(object obj) { var psobj = obj as PSObject; if (psobj != null) { return psobj.InternalTypeNames; } ConsolidatedString result; if (HasInstanceTypeName(obj, out result)) { return result; } return PSObject.GetMappedAdapter(obj, null).OriginalAdapter.BaseGetTypeNameHierarchy(obj); } internal static bool HasInstanceTypeName(object obj, out ConsolidatedString result) { return s_typeNamesResurrectionTable.TryGetValue(GetKeyForResurrectionTables(obj), out result); } private ConsolidatedString _typeNames; private static readonly ConditionalWeakTable s_typeNamesResurrectionTable = new ConditionalWeakTable(); #endregion properties #region static methods /// /// /// /// /// public static implicit operator PSObject(int valueToConvert) { return PSObject.AsPSObject(valueToConvert); } /// /// /// /// /// public static implicit operator PSObject(string valueToConvert) { return PSObject.AsPSObject(valueToConvert); } /// /// /// /// /// public static implicit operator PSObject(Hashtable valueToConvert) { return PSObject.AsPSObject(valueToConvert); } /// /// /// /// /// public static implicit operator PSObject(double valueToConvert) { return PSObject.AsPSObject(valueToConvert); } /// /// /// /// /// public static implicit operator PSObject(bool valueToConvert) { return PSObject.AsPSObject(valueToConvert); } /// /// If obj is not an PSObject, it is returned. Otherwise, retrieves /// the first non PSObject or PSObject with CustomBaseObject /// in the PSObject - BaseObject chain. /// internal static object Base(object obj) { PSObject mshObj = obj as PSObject; if (mshObj == null) { return obj; } if (mshObj == AutomationNull.Value) return null; if (mshObj.immediateBaseObjectIsEmpty) { return obj; } object returnValue = null; do { returnValue = mshObj._immediateBaseObject; mshObj = returnValue as PSObject; } while ((mshObj != null) && (!mshObj.immediateBaseObjectIsEmpty)); return returnValue; } internal static PSMemberInfo GetStaticCLRMember(object obj, string methodName) { obj = PSObject.Base(obj); if (obj == null || methodName == null || methodName.Length == 0) { return null; } var objType = obj as Type ?? obj.GetType(); return dotNetStaticAdapter.BaseGetMember(objType, methodName); } /// /// If obj is an PSObject it will be returned as is, otherwise /// a new PSObject will be created based on obj. /// /// object to be wrapped /// /// obj or a new PSObject whose BaseObject is obj /// /// if is null [SuppressMessage("Microsoft.Naming", "CA1720:IdentifiersShouldNotContainTypeNames", MessageId = "obj", Justification = "This is shipped as part of V1. Retaining this for backward compatibility.")] public static PSObject AsPSObject(object obj) { return AsPSObject(obj, false); } /// /// If obj is a PSObject, it will be returned as is, otherwise a new /// PSObject will be created on obj. Its InstanceMembers and TypeNames /// will be initialized if we are not going to use the ResurrectionTables /// for this PSObject instance. /// /// /// /// [SuppressMessage("Microsoft.Naming", "CA1720:IdentifiersShouldNotContainTypeNames", MessageId = "obj", Justification = "AsPSObject is shipped as part of V1. This is a new overload method.")] internal static PSObject AsPSObject(object obj, bool storeTypeNameAndInstanceMembersLocally) { if (obj == null) { throw PSTraceSource.NewArgumentNullException("obj"); } PSObject so = obj as PSObject; if (so != null) { return so; } return new PSObject(obj) { _storeTypeNameAndInstanceMembersLocally = storeTypeNameAndInstanceMembersLocally }; } /// /// Returns an object that should be used as a key for getting 1) instance members and 2) type names /// - If base object is a PSCustomObject or a string or a type /// then the most nested wrapping PSObject is returned (the PSObject where immediateBaseObject=PSCustomObject/string/type) /// - Otherwise the base object is returned /// This is a temporary fix for Win8 : 254345 - Job Failed By Throwing ExtendedTypeSystemException. /// /// internal static object GetKeyForResurrectionTables(object obj) { var pso = obj as PSObject; if (pso == null) { return obj; } PSObject psObjectAboveBase = pso; while (psObjectAboveBase.ImmediateBaseObject is PSObject) { psObjectAboveBase = (PSObject)psObjectAboveBase.ImmediateBaseObject; } if (psObjectAboveBase.ImmediateBaseObject is PSCustomObject || psObjectAboveBase.ImmediateBaseObject is string || pso._storeTypeNameAndInstanceMembersLocally) { return psObjectAboveBase; } return psObjectAboveBase.ImmediateBaseObject; } #endregion static methods #region instance methods #region ToString private static string GetSeparator(ExecutionContext context, string separator) { if (separator != null) { return separator; } if (context != null) { object obj = context.GetVariableValue(SpecialVariables.OFSVarPath); if (obj != null) { return obj.ToString(); } } return " "; } internal static string ToStringEnumerator(ExecutionContext context, IEnumerator enumerator, string separator, string format, IFormatProvider formatProvider) { StringBuilder returnValue = new StringBuilder(); string separatorToUse = GetSeparator(context, separator); while (enumerator.MoveNext()) { object obj = enumerator.Current; returnValue.Append(PSObject.ToString(context, obj, separator, format, formatProvider, false, false)); returnValue.Append(separatorToUse); } if (returnValue.Length == 0) { return String.Empty; } int separatorLength = separatorToUse.Length; returnValue.Remove(returnValue.Length - separatorLength, separatorLength); return returnValue.ToString(); } internal static string ToStringEnumerable(ExecutionContext context, IEnumerable enumerable, string separator, string format, IFormatProvider formatProvider) { StringBuilder returnValue = new StringBuilder(); string separatorToUse = GetSeparator(context, separator); foreach (object obj in enumerable) { if (obj != null) { PSObject mshObj = PSObject.AsPSObject(obj); returnValue.Append(PSObject.ToString(context, mshObj, separator, format, formatProvider, false, false)); } returnValue.Append(separatorToUse); } if (returnValue.Length == 0) { return String.Empty; } int separatorLength = separatorToUse.Length; returnValue.Remove(returnValue.Length - separatorLength, separatorLength); return returnValue.ToString(); } private static string ToStringEmptyBaseObject(ExecutionContext context, PSObject mshObj, string separator, string format, IFormatProvider formatProvider) { StringBuilder returnValue = new StringBuilder("@{"); bool isFirst = true; foreach (PSPropertyInfo property in mshObj.Properties) { if (!isFirst) { returnValue.Append("; "); } isFirst = false; returnValue.Append(property.Name); returnValue.Append("="); // Don't evaluate script properties during a ToString() operation. Object propertyValue = null; if (property is PSScriptProperty) { propertyValue = property.GetType().FullName; } else { propertyValue = property.Value; } returnValue.Append(PSObject.ToString(context, propertyValue, separator, format, formatProvider, false, false)); } if (isFirst) { return string.Empty; } returnValue.Append("}"); return returnValue.ToString(); } /// /// Returns the string representation of obj. /// /// ExecutionContext used to fetch the separator. /// /// object we are trying to call ToString on. If this is not an PSObject we try /// enumerating and if that fails we call obj.ToString. /// If this is an PSObject, we look for a brokered ToString. /// If it is not present, and the BaseObject is null we try listing the properties. /// If the BaseObject is not null we try enumerating. If that fails we try the BaseObject's ToString. /// /// A string representation of the object /// /// When there is a brokered ToString but it failed, or when the ToString on obj throws an exception. /// internal static string ToStringParser(ExecutionContext context, object obj) { try { return ToString(context, obj, null, null, CultureInfo.InvariantCulture, true, true); } catch (ExtendedTypeSystemException etse) { throw new PSInvalidCastException("InvalidCastFromAnyTypeToString", etse.InnerException, ExtendedTypeSystem.InvalidCastCannotRetrieveString); } } /// /// Called from an PSObject instance ToString to provide a string representation for an object /// /// /// ExecutionContext used to fetch the separator. /// Typically either this or separator will be null. /// If both are null, " " is used. /// /// /// object we are trying to call ToString on. If this is not an PSObject we try /// enumerating and if that fails we call obj.ToString. /// If this is an PSObject, we look for a brokered ToString. /// If it is not present, and the BaseObject is null we try listing the properties. /// If the BaseObject is not null we try enumerating. If that fails we try the BaseObject's ToString. /// /// The separator between elements, if this is an enumeration /// the format to be passed to ToString /// the formatProvider to be passed to ToString /// true if we should enumerate values or properties which would cause recursive /// calls to this method. Such recursive calls will have recurse set to false, limiting the depth. /// If recurse is false, this parameter is not considered. If it is true /// this parameter will determine how enumerators are going to be treated. /// /// A string representation of the object /// /// When there is a brokered ToString but it failed, or when the ToString on obj throws an exception. /// internal static string ToString(ExecutionContext context, object obj, string separator, string format, IFormatProvider formatProvider, bool recurse, bool unravelEnumeratorOnRecurse) { PSObject mshObj = obj as PSObject; #region plain object if (mshObj == null) { if (obj == null) { return String.Empty; } // Fast-track the primitive types... Type objType = obj.GetType(); TypeCode code = objType.GetTypeCode(); switch (code) { case TypeCode.String: return (string)obj; case TypeCode.Byte: case TypeCode.SByte: case TypeCode.Int16: case TypeCode.UInt16: case TypeCode.Int32: case TypeCode.UInt32: case TypeCode.Int64: case TypeCode.UInt64: return obj.ToString(); case TypeCode.DateTime: DateTime dt = (DateTime)obj; return dt.ToString(formatProvider); case TypeCode.Decimal: Decimal dec = (Decimal)obj; return dec.ToString(formatProvider); case TypeCode.Double: double dbl = (double)obj; return dbl.ToString(formatProvider); case TypeCode.Single: float sgl = (float)obj; return sgl.ToString(formatProvider); } #region recurse if (recurse) { IEnumerable enumerable = LanguagePrimitives.GetEnumerable(obj); if (enumerable != null) { try { return ToStringEnumerable(context, enumerable, separator, format, formatProvider); } catch (Exception) { // We do want to ignore exceptions here to try the regular ToString below. } } if (unravelEnumeratorOnRecurse) { IEnumerator enumerator = LanguagePrimitives.GetEnumerator(obj); if (enumerator != null) { try { return ToStringEnumerator(context, enumerator, separator, format, formatProvider); } catch (Exception) { // We do want to ignore exceptions here to try the regular ToString below. } } } } #endregion recurse #region object ToString IFormattable objFormattable = obj as IFormattable; try { if (objFormattable == null) { Type type = obj as Type; if (type != null) { return Microsoft.PowerShell.ToStringCodeMethods.Type(type); } return obj.ToString(); } return objFormattable.ToString(format, formatProvider); } catch (Exception e) { throw new ExtendedTypeSystemException("ToStringObjectBasicException", e, ExtendedTypeSystem.ToStringException, e.Message); } #endregion object ToString } #endregion plain object #region PSObject #region brokered ToString // A brokered ToString has precedence over any other attempts. // If it fails we let the exception go because the caller must be notified. PSMethodInfo method = null; PSMemberInfoInternalCollection instanceMembers; if (PSObject.HasInstanceMembers(mshObj, out instanceMembers)) { method = instanceMembers["ToString"] as PSMethodInfo; } if (method == null) { if (mshObj.InternalTypeNames.Count != 0) { TypeTable table = mshObj.GetTypeTable(); if (table != null) { method = table.GetMembers(mshObj.InternalTypeNames)["ToString"]; if (method != null) { method = (PSMethodInfo)method.Copy(); method.instance = mshObj; } } } } if (method != null) { object retObj; try { // Even if a format specifier has been provided, if there is only one overload // then it can't take a format specified... if (formatProvider != null && method.OverloadDefinitions.Count > 1) { retObj = method.Invoke(format, formatProvider); return retObj != null ? retObj.ToString() : string.Empty; } retObj = method.Invoke(); return retObj != null ? retObj.ToString() : string.Empty; } catch (MethodException e) { throw new ExtendedTypeSystemException("MethodExceptionNullFormatProvider", e, ExtendedTypeSystem.ToStringException, e.Message); } } #endregion brokered ToString #region recurse // 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) { try { return PSObject.ToStringEmptyBaseObject(context, mshObj, separator, format, formatProvider); } catch (Exception) { // We do want to ignore exceptions here to try the regular ToString below. } } IEnumerable enumerable = LanguagePrimitives.GetEnumerable(mshObj); if (enumerable != null) { try { return ToStringEnumerable(context, enumerable, separator, format, formatProvider); } catch (Exception) { // We do want to ignore exceptions here to try the regular ToString below. } } if (unravelEnumeratorOnRecurse) { IEnumerator enumerator = LanguagePrimitives.GetEnumerator(mshObj); if (enumerator != null) { try { return ToStringEnumerator(context, enumerator, separator, format, formatProvider); } catch (Exception) { // We do want to ignore exceptions here to try the regular ToString below. } } } } #endregion recurse #region mshObject's BaseObject ToString // If we've cached a string representation for this object, use that. This // is used to preserve the original string for numeric literals. if (mshObj.TokenText != null) { return mshObj.TokenText; } // Since we don't have a brokered ToString and the enumerations were not necessary or failed // we try the BaseObject's ToString object baseObject = mshObj._immediateBaseObject; IFormattable msjObjFormattable = baseObject as IFormattable; try { string result; if (msjObjFormattable == null) { result = baseObject.ToString(); } else { result = msjObjFormattable.ToString(format, formatProvider); } return result ?? String.Empty; } catch (Exception e) { throw new ExtendedTypeSystemException("ToStringPSObjectBasicException", e, ExtendedTypeSystem.ToStringException, e.Message); } #endregion mshObject's BaseObject ToString #endregion PSObject } /// /// Returns the string representation for this object. A ToString /// CodeMethod or ScriptMethod will be used, if available. Enumerations items are /// concatenated using $ofs. /// /// the string representation for baseObject /// if an exception was thrown by the BaseObject's ToString public override string ToString() { //If ToString value from deserialization is available, //simply return it. if (_toStringFromDeserialization != null) { return _toStringFromDeserialization; } return PSObject.ToString(null, this, null, null, null, true, false); } /// /// Returns the string representation for this object. A ToString /// CodeMethod or ScriptMethod will be used, if available. Enumerations items are /// concatenated using $ofs. /// /// repassed to baseObject's IFormattable if present /// repassed to baseObject's IFormattable if present /// the string representation for baseObject /// if an exception was thrown by the BaseObject's ToString public string ToString(string format, IFormatProvider formatProvider) { //If ToString value from deserialization is available, //simply return it. if (_toStringFromDeserialization != null) { return _toStringFromDeserialization; } return PSObject.ToString(null, this, null, format, formatProvider, true, false); } private string PrivateToString() { string result; try { result = this.ToString(); } catch (ExtendedTypeSystemException) { result = this.BaseObject.GetType().FullName; } return result; } #endregion ToString #region Clone /// /// Returns a copy of this PSObject. This will copy the BaseObject if /// it is a value type, and use BaseObject.Clone() for the new PSObject, /// if the BaseObject is ICloneable. /// /// a copy of this object public virtual PSObject Copy() { PSObject returnValue = (PSObject)this.MemberwiseClone(); if (this.BaseObject is PSCustomObject) { returnValue._immediateBaseObject = PSCustomObject.SelfInstance; returnValue.immediateBaseObjectIsEmpty = true; } else { returnValue._immediateBaseObject = _immediateBaseObject; returnValue.immediateBaseObjectIsEmpty = false; } // Instance members will be recovered as necessary through the resurrection table. returnValue._instanceMembers = null; // The typename is not resurrected. A common reason to copy a PSObject is to change the TypeName, // especially on a PSCustomObject - e.g. to a help object where we want to force a different view. returnValue._typeNames = null; returnValue._members = new PSMemberInfoIntegratingCollection(returnValue, s_memberCollection); returnValue._properties = new PSMemberInfoIntegratingCollection(returnValue, s_propertyCollection); returnValue._methods = new PSMemberInfoIntegratingCollection(returnValue, s_methodCollection); returnValue._adapterSet = GetMappedAdapter(returnValue._immediateBaseObject, returnValue.GetTypeTable()); ICloneable cloneableBase = returnValue._immediateBaseObject as ICloneable; if (cloneableBase != null) { returnValue._immediateBaseObject = cloneableBase.Clone(); } if (returnValue._immediateBaseObject is ValueType) { returnValue._immediateBaseObject = CopyValueType(returnValue._immediateBaseObject); } // needToReAddInstanceMembersAndTypeNames = returnValue will have a different key (different from "this") returned from GetKeyForResurrectionTables bool needToReAddInstanceMembersAndTypeNames = !object.ReferenceEquals(GetKeyForResurrectionTables(this), GetKeyForResurrectionTables(returnValue)); if (needToReAddInstanceMembersAndTypeNames) { Diagnostics.Assert(!returnValue.InstanceMembers.Any(), "needToReAddInstanceMembersAndTypeNames should mean that the new object has a fresh/empty list of instance members"); foreach (PSMemberInfo member in this.InstanceMembers) { if (member.IsHidden) { continue; } // Add will clone the member returnValue.Members.Add(member); } returnValue.TypeNames.Clear(); foreach (string typeName in this.InternalTypeNames) { returnValue.TypeNames.Add(typeName); } } returnValue.hasGeneratedReservedMembers = false; return returnValue; } internal static object CopyValueType(object obj) { // this will force boxing and unboxing in a new object that will cause // the copy of the value type var newBaseArray = Array.CreateInstance(obj.GetType(), 1); newBaseArray.SetValue(obj, 0); return newBaseArray.GetValue(0); } #endregion Clone #region IComparable /// /// Compares the current instance with another object of the same type. /// /// An object to compare with this instance. /// A 32-bit signed integer that indicates the relative order of the comparands. /// The return value has these meanings: /// Value Meaning /// Less than zero This instance is less than obj. /// Zero This instance is equal to obj. /// Greater than zero This instance is greater than obj. /// /// If has a different type /// than this instance's BaseObject or if the BaseObject does not implement IComparable. /// public int CompareTo(object obj) { // This ReferenceEquals is not just an optimization. // It is necessary so that mshObject.Equals(mshObject) returns 0. // Please see the comments inside the Equals implementation. if (Object.ReferenceEquals(this, obj)) { return 0; } try { // PSObject.Base instead of BaseObject could cause an infinite // loop with LP.Compare calling this Compare. return LanguagePrimitives.Compare(this.BaseObject, obj); } catch (ArgumentException e) { throw new ExtendedTypeSystemException("PSObjectCompareTo", e, ExtendedTypeSystem.NotTheSameTypeOrNotIcomparable, this.PrivateToString(), PSObject.AsPSObject(obj).ToString(), "IComparable"); } } #endregion IComparable #region Equals and GetHashCode /// /// Determines whether the specified Object is equal to the current Object. /// /// The Object to compare with the current Object. /// true if the specified Object is equal to the current Object; otherwise, false. public override bool Equals(object obj) { // There is a slight difference between BaseObject and PSObject.Base. // PSObject.Base returns the containing PSObject that wraps an MshCustomBaseObject. // BaseObject returns the MshCustomBaseObject. // Because we have to call BaseObject here, and LP.Compare uses PSObject.Base // we need the reference equals below so that mshObject.Equals(mshObject) returns true. if (Object.ReferenceEquals(this, obj)) { return true; } // The above check validates if we are comparing with the same object references // This check "shortcuts" the comparison if the first object is a CustomObject // since 2 custom objects are not equal. if (Object.ReferenceEquals(this.BaseObject, PSCustomObject.SelfInstance)) { return false; } // PSObject.Base instead of BaseObject could cause an infinite // loop with LP.Equals calling this Equals. return LanguagePrimitives.Equals(this.BaseObject, obj); } /// /// Serves as a hash function for a particular type, suitable for use in hashing algorithms and data structures like a hash table. /// /// A hash code for the current Object public override int GetHashCode() { return this.BaseObject.GetHashCode(); } #endregion Equals and GetHashCode internal void AddOrSetProperty(string memberName, object value) { PSMemberInfo memberInfo; if (PSGetMemberBinder.TryGetInstanceMember(this, memberName, out memberInfo) && memberInfo is PSPropertyInfo) { memberInfo.Value = value; } else { Properties.Add(new PSNoteProperty(memberName, value)); } } internal void AddOrSetProperty(PSNoteProperty property) { PSMemberInfo memberInfo; if (PSGetMemberBinder.TryGetInstanceMember(this, property.Name, out memberInfo) && memberInfo is PSPropertyInfo) { memberInfo.Value = property.Value; } else { Properties.Add(property); } } #endregion instance methods #region public const strings /// /// The name of the member set for adapted members /// /// /// This needs to be Lower cased as it saves some comparison time elsewhere. /// public const string AdaptedMemberSetName = "psadapted"; /// /// The name of the member set for extended members /// /// /// This needs to be Lower cased as it saves some comparison time elsewhere. /// public const string ExtendedMemberSetName = "psextended"; /// /// The name of the member set for the BaseObject's members /// /// /// This needs to be Lower cased as it saves some comparison time elsewhere. /// public const string BaseObjectMemberSetName = "psbase"; /// /// The PSObject's properties /// /// /// This needs to be Lower cased as it saves some comparison time elsewhere. /// internal const string PSObjectMemberSetName = "psobject"; /// /// a shortcut to .PSObject.TypeNames /// /// /// This needs to be Lower cased as it saves some comparison time elsewhere. /// internal const string PSTypeNames = "pstypenames"; #endregion public const strings #region serialization /// /// Implements the ISerializable contract for serializing a PSObject /// /// Serialization information for this instance /// The streaming context for this instance public virtual void GetObjectData(SerializationInfo info, StreamingContext context) { if (info == null) { throw PSTraceSource.NewArgumentNullException("info"); } // We create a wrapper PSObject, so that we can successfully deserialize it string serializedContent = null; if (this.immediateBaseObjectIsEmpty) { PSObject serializeTarget = new PSObject(this); serializedContent = PSSerializer.Serialize(serializeTarget); } else { serializedContent = PSSerializer.Serialize(this); } info.AddValue("CliXml", serializedContent); } /// /// Used in the serialization duplicate entry hashtable to detect when an PSObject has been serialized /// /// The System.Object implementation of GetHashCode internal int GetReferenceHashCode() { return base.GetHashCode(); } /// /// /// /// /// /// /// /// /// true to make this PSObject as the owner of the memberset. /// /// /// PSObject to be used while replicating the owner for PSMemberSet /// /// internal static object GetNoteSettingValue(PSMemberSet settings, string noteName, object defaultValue, Type expectedType, bool shouldReplicateInstance, PSObject ownerObject) { if (settings == null) { return defaultValue; } if (shouldReplicateInstance) { settings.ReplicateInstance(ownerObject); } PSNoteProperty note = settings.Members[noteName] as PSNoteProperty; if (note == null) { return defaultValue; } object noteValue = note.Value; if (noteValue == null || noteValue.GetType() != expectedType) { return defaultValue; } return note.Value; } internal int GetSerializationDepth(TypeTable backupTypeTable) { int result = 0; TypeTable typeTable = backupTypeTable ?? this.GetTypeTable(); if (null != typeTable) { PSMemberSet standardMemberSet = TypeTableGetMemberDelegate(this, typeTable, TypeTable.PSStandardMembers); result = (int)GetNoteSettingValue(standardMemberSet, TypeTable.SerializationDepth, 0, typeof(int), true, this); } return result; } /// /// /// /// TypeTable to use if this PSObject.GetTypeTable() returns null. This will happen /// in the remoting scenario on the client/server side (where a LocalRunspace may not be /// present). /// /// internal PSPropertyInfo GetStringSerializationSource(TypeTable backupTypeTable) { PSMemberInfo result = this.GetPSStandardMember(backupTypeTable, TypeTable.StringSerializationSource); return result as PSPropertyInfo; } /// /// /// /// TypeTable to use if this PSObject.GetTypeTable() returns null. This will happen /// in the remoting scenario on the client/server side (where a LocalRunspace may not be /// present). /// /// internal SerializationMethod GetSerializationMethod(TypeTable backupTypeTable) { SerializationMethod result = TypeTable.defaultSerializationMethod; TypeTable typeTable = backupTypeTable ?? this.GetTypeTable(); if (null != typeTable) { PSMemberSet standardMemberSet = TypeTableGetMemberDelegate(this, typeTable, TypeTable.PSStandardMembers); result = (SerializationMethod)GetNoteSettingValue(standardMemberSet, TypeTable.SerializationMethodNode, TypeTable.defaultSerializationMethod, typeof(SerializationMethod), true, this); } return result; } internal PSMemberSet PSStandardMembers { get { PSMemberSet retVal = null; retVal = TypeTableGetMemberDelegate(this, TypeTable.PSStandardMembers); if (retVal != null) { retVal = (PSMemberSet)retVal.Copy(); retVal.ReplicateInstance(this); return retVal; } retVal = this.InstanceMembers[TypeTable.PSStandardMembers] as PSMemberSet; return retVal; } } internal PSMemberInfo GetPSStandardMember(TypeTable backupTypeTable, string memberName) { PSMemberInfo result = null; TypeTable typeTable = backupTypeTable ?? this.GetTypeTable(); if (typeTable != null) { PSMemberSet standardMemberSet = TypeTableGetMemberDelegate( this, typeTable, TypeTable.PSStandardMembers); if (null != standardMemberSet) { standardMemberSet.ReplicateInstance(this); PSMemberInfoIntegratingCollection members = new PSMemberInfoIntegratingCollection( standardMemberSet, GetMemberCollection(PSMemberViewTypes.All, backupTypeTable)); result = members[memberName]; } } return result ?? InstanceMembers[TypeTable.PSStandardMembers] as PSMemberSet; } /// /// Used by Deserializer to deserialize a serialized object to a given type /// (as specified in the a types.ps1xml file) /// /// /// TypeTable to use if this PSObject.GetTypeTable() returns null. This will happen /// in the remoting scenario on the client/server side (where a LocalRunspace may not be /// present). /// internal Type GetTargetTypeForDeserialization(TypeTable backupTypeTable) { PSMemberInfo targetType = this.GetPSStandardMember(backupTypeTable, TypeTable.TargetTypeForDeserialization); if (null != targetType) { return (targetType.Value as Type); } else { return null; } } /// /// This is only going to be called if SerializationMethod is SpecificProperties /// /// /// TypeTable to use if this PSObject.GetTypeTable() returns null. This will happen /// in the remoting scenario on the client side (where a LocalRunspace may not be /// present). /// /// A collection with only the specific properties to serialize internal Collection GetSpecificPropertiesToSerialize(TypeTable backupTypeTable) { TypeTable typeTable = backupTypeTable ?? this.GetTypeTable(); if (null != typeTable) { Collection tmp = typeTable.GetSpecificProperties(this.InternalTypeNames); return tmp; } return new Collection(new List()); } internal bool ShouldSerializeAdapter() { if (this.isDeserialized) { return this.adaptedMembers != null; } return !this.immediateBaseObjectIsEmpty; } internal bool ShouldSerializeBase() { if (this.isDeserialized) { return this.adaptedMembers != this.clrMembers; } if (this.immediateBaseObjectIsEmpty) { return false; } return (this.InternalAdapter.GetType() != typeof(DotNetAdapter)); } internal PSMemberInfoInternalCollection GetAdaptedProperties() { return GetProperties(this.adaptedMembers, this.InternalAdapter); } internal PSMemberInfoInternalCollection GetBaseProperties() { return GetProperties(this.clrMembers, dotNetInstanceAdapter); } private PSMemberInfoInternalCollection GetProperties(PSMemberInfoInternalCollection serializedMembers, Adapter particularAdapter) { if (this.isDeserialized) { return serializedMembers; } PSMemberInfoInternalCollection returnValue = new PSMemberInfoInternalCollection(); foreach (PSPropertyInfo member in particularAdapter.BaseGetMembers(_immediateBaseObject)) { returnValue.Add(member); } return returnValue; } /// /// This flag is set to true in deserialized shellobject /// internal bool isDeserialized = false; internal static void CopyDeserializerFields(PSObject source, PSObject target) { if (!target.isDeserialized) { target.isDeserialized = source.isDeserialized; target.adaptedMembers = source.adaptedMembers; target.clrMembers = source.clrMembers; } if (target._toStringFromDeserialization == null) { target._toStringFromDeserialization = source._toStringFromDeserialization; target.TokenText = source.TokenText; } } /// /// Set base object /// /// object which is set as core /// If true, overwrite the type information ///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(value != null, "known objects are never null"); this.immediateBaseObjectIsEmpty = false; _immediateBaseObject = value; _adapterSet = GetMappedAdapter(_immediateBaseObject, GetTypeTable()); if (overrideTypeInfo) { this.InternalTypeNames = this.InternalAdapter.BaseGetTypeNameHierarchy(value); } } //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 { return _toStringFromDeserialization; } set { _toStringFromDeserialization = value; } } #endregion serialization /// /// This class is solely used by PSObject to support .net member lookup for all the /// adapters except for dotNetInstanceAdapter, mshMemberSetAdapter and mshObjectAdapter. /// If the original adapter is not one of those, then .net members are also exposed /// on the PSObject. This will have the following effect: /// /// 1. Every adapted object like xml, wmi, adsi will show adapted members as well as /// .net members. /// 2. Users will not need to access PSBase to access original .net members. /// 3. This will fix v1.0 ADSI adapter where most of the complaints were about /// discovering original .net members. /// /// Use of this class will allow us to customize the ultimate .net member lookup. /// For example, XML adapter already exposes .net methods. /// Using this class you can choose exact .net adapter to support .net /// member lookup and avoid lookup duplication. /// /// /// This class is intended for internal use only. /// internal class AdapterSet { #region Private Data // original adapter like Xml, ManagementClass, DirectoryEntry etc. // .net adapter #endregion #region Properties /// /// This property can be accessed only internally and hence /// no checks are performed on input. /// internal Adapter OriginalAdapter { get; set; } internal DotNetAdapter DotNetAdapter { get; } #endregion #region Constructors internal AdapterSet(Adapter adapter, DotNetAdapter dotnetAdapter) { OriginalAdapter = adapter; DotNetAdapter = dotnetAdapter; } #endregion } #region Dynamic metaobject implementation internal class PSDynamicMetaObject : DynamicMetaObject { internal PSDynamicMetaObject(Expression expression, PSObject value) : base(expression, BindingRestrictions.Empty, value) { } private new PSObject Value { get { return (PSObject)base.Value; } } private DynamicMetaObject GetUnwrappedObject() { return new DynamicMetaObject(Expression.Call(CachedReflectionInfo.PSObject_Base, this.Expression), this.Restrictions, PSObject.Base(Value)); } public override IEnumerable GetDynamicMemberNames() { return (from member in Value.Members select member.Name); } private bool MustDeferIDMOP() { var baseObject = PSObject.Base(Value); return baseObject is IDynamicMetaObjectProvider && !(baseObject is PSObject); } private DynamicMetaObject DeferForIDMOP(DynamicMetaObjectBinder binder, params DynamicMetaObject[] args) { Diagnostics.Assert(MustDeferIDMOP(), "Defer only works for idmop wrapped PSObjects"); Expression[] exprs = new Expression[args.Length + 1]; BindingRestrictions restrictions = this.Restrictions == BindingRestrictions.Empty ? this.PSGetTypeRestriction() : this.Restrictions; exprs[0] = Expression.Call(CachedReflectionInfo.PSObject_Base, this.Expression.Cast(typeof(object))); for (int i = 0; i < args.Length; i++) { exprs[i + 1] = args[i].Expression; restrictions = restrictions.Merge(args[i].Restrictions == BindingRestrictions.Empty ? args[i].PSGetTypeRestriction() : args[i].Restrictions); } return new DynamicMetaObject(DynamicExpression.Dynamic(binder, binder.ReturnType, exprs), restrictions); } public override DynamicMetaObject BindBinaryOperation(BinaryOperationBinder binder, DynamicMetaObject arg) { if (MustDeferIDMOP()) { return DeferForIDMOP(binder, arg); } return binder.FallbackBinaryOperation(GetUnwrappedObject(), arg); } public override DynamicMetaObject BindConvert(ConvertBinder binder) { if (MustDeferIDMOP()) { return DeferForIDMOP(binder); } // This will invoke the language binder, meaning we might not invoke PowerShell conversions. This // is an interesting design choice, which, if revisited, needs some care because there are multiple // binders that PowerShell uses for conversion, the normal one, and the one used when we want to // attempt enumeration of some object that might or might not implement IEnumerable. return binder.FallbackConvert(this); } public override DynamicMetaObject BindDeleteIndex(DeleteIndexBinder binder, DynamicMetaObject[] indexes) { if (MustDeferIDMOP()) { return DeferForIDMOP(binder, indexes); } return binder.FallbackDeleteIndex(GetUnwrappedObject(), indexes); } public override DynamicMetaObject BindDeleteMember(DeleteMemberBinder binder) { if (MustDeferIDMOP()) { return DeferForIDMOP(binder); } return binder.FallbackDeleteMember(GetUnwrappedObject()); } public override DynamicMetaObject BindGetIndex(GetIndexBinder binder, DynamicMetaObject[] indexes) { if (MustDeferIDMOP()) { return DeferForIDMOP(binder, indexes); } return binder.FallbackGetIndex(GetUnwrappedObject(), indexes); } public override DynamicMetaObject BindInvoke(InvokeBinder binder, DynamicMetaObject[] args) { if (MustDeferIDMOP()) { return DeferForIDMOP(binder, args); } return binder.FallbackInvoke(GetUnwrappedObject(), args); } public override DynamicMetaObject BindSetIndex(SetIndexBinder binder, DynamicMetaObject[] indexes, DynamicMetaObject value) { if (MustDeferIDMOP()) { return DeferForIDMOP(binder, indexes.Append(value).ToArray()); } return binder.FallbackSetIndex(GetUnwrappedObject(), indexes, value); } public override DynamicMetaObject BindUnaryOperation(UnaryOperationBinder binder) { if (MustDeferIDMOP()) { return DeferForIDMOP(binder); } return binder.FallbackUnaryOperation(GetUnwrappedObject()); } public override DynamicMetaObject BindInvokeMember(InvokeMemberBinder binder, DynamicMetaObject[] args) { if (MustDeferIDMOP()) { return DeferForIDMOP(binder, args); } return (binder as PSInvokeMemberBinder ?? (InvokeMemberBinder)(binder as PSInvokeBaseCtorBinder) ?? PSInvokeMemberBinder.Get(binder.Name, binder.CallInfo, false, false, null, null)).FallbackInvokeMember(this, args); } public override DynamicMetaObject BindGetMember(GetMemberBinder binder) { if (MustDeferIDMOP()) { return DeferForIDMOP(binder); } return (binder as PSGetMemberBinder ?? PSGetMemberBinder.Get(binder.Name, (Type)null, false)).FallbackGetMember(this); } public override DynamicMetaObject BindSetMember(SetMemberBinder binder, DynamicMetaObject value) { if (MustDeferIDMOP()) { return DeferForIDMOP(binder, value); } return (binder as PSSetMemberBinder ?? PSSetMemberBinder.Get(binder.Name, (Type)null, false)).FallbackSetMember(this, value); } } DynamicMetaObject IDynamicMetaObjectProvider.GetMetaObject(Expression parameter) { return new PSDynamicMetaObject(parameter, this); } #endregion #region Help formatting internal bool IsHelpObject { get { return _isHelpObject; } set { _isHelpObject = value; } } private bool _isHelpObject = false; #endregion } /// /// Serves as a placeholder BaseObject when PSObject's /// constructor with no parameters is used. /// public class PSCustomObject { /// /// To prevent other instances than SelfInstance /// private PSCustomObject() { } internal static PSCustomObject SelfInstance = new PSCustomObject(); /// /// Returns an empty string /// public override string ToString() { return ""; } } /// /// /// /// /// Please keep in sync with SerializationMethod from /// C:\e\win7_powershell\admin\monad\nttargets\assemblies\logging\ETW\Manifests\Microsoft-Windows-PowerShell-Instrumentation.man /// internal enum SerializationMethod { AllPublicProperties = 0, String = 1, SpecificProperties = 2 }; } #pragma warning restore 56500 namespace Microsoft.PowerShell { /// /// Contains auxiliary ToString CodeMethod implementations for some types /// public static partial class ToStringCodeMethods { private static void AddGenericArguments(StringBuilder sb, Type[] genericArguments, bool dropNamespaces) { sb.Append('['); for (int i = 0; i < genericArguments.Length; i++) { if (i > 0) { sb.Append(','); } sb.Append(Type(genericArguments[i], dropNamespaces)); } sb.Append(']'); } internal static string Type(Type type, bool dropNamespaces = false, string key = null) { if (type == null) return String.Empty; string result; TypeInfo typeinfo = type.GetTypeInfo(); if (typeinfo.IsGenericType && !typeinfo.IsGenericTypeDefinition) { string genericDefinition = Type(type.GetGenericTypeDefinition(), dropNamespaces); // For regular generic types, we find the backtick character, for example: // System.Collections.Generic.List`1[T] -> // System.Collections.Generic.List[string] // For nested generic types, we find the left bracket character, for example: // System.Collections.Generic.Dictionary`2+Enumerator[TKey, TValue] -> // System.Collections.Generic.Dictionary`2+Enumerator[string,string] int backtickOrLeftBracketIndex = genericDefinition.LastIndexOf(typeinfo.IsNested ? '[' : '`'); var sb = new StringBuilder(genericDefinition, 0, backtickOrLeftBracketIndex, 512); AddGenericArguments(sb, type.GetGenericArguments(), dropNamespaces); result = sb.ToString(); } else if (typeinfo.IsArray) { string elementDefinition = Type(type.GetElementType(), dropNamespaces); var sb = new StringBuilder(elementDefinition, elementDefinition.Length + 10); sb.Append("["); for (int i = 0; i < type.GetArrayRank() - 1; ++i) { sb.Append(","); } sb.Append("]"); result = sb.ToString(); } else { result = TypeAccelerators.FindBuiltinAccelerator(type, key); if (result == null) { if (type == typeof(PSCustomObject)) { return type.Name; } if (dropNamespaces) { if (typeinfo.IsNested) { // For nested types, we should return OuterType+InnerType. For example, // System.Environment+SpecialFolder -> Environment+SpecialFolder string fullName = type.ToString(); result = type.Namespace == null ? fullName : fullName.Substring(type.Namespace.Length + 1); } else { result = type.Name; } } else { result = type.ToString(); } } } // We can't round trip anything with a generic parameter. // We also can't round trip if we're dropping the namespace. if (!typeinfo.IsGenericParameter && !typeinfo.ContainsGenericParameters && !dropNamespaces && !typeinfo.Assembly.GetCustomAttributes(typeof(DynamicClassImplementationAssemblyAttribute)).Any()) { Type roundTripType; TypeResolver.TryResolveType(result, out roundTripType); if (roundTripType != type) { result = type.AssemblyQualifiedName; } } return result; } /// /// ToString implementation for Type /// /// instance of PSObject wrapping a Type public static string Type(PSObject instance) { if (instance == null) return String.Empty; return Type((Type)instance.BaseObject); } /// /// ToString implementation for XmlNode /// /// instance of PSObject wrapping an XmlNode public static string XmlNode(PSObject instance) { if (instance == null) return String.Empty; XmlNode node = (XmlNode)instance.BaseObject; if (node == null) return String.Empty; return node.LocalName; } /// /// ToString implementation for XmlNodeList /// /// instance of PSObject wrapping an XmlNodeList public static string XmlNodeList(PSObject instance) { if (instance == null) return String.Empty; XmlNodeList nodes = (XmlNodeList)instance.BaseObject; if (nodes == null) return String.Empty; if (nodes.Count == 1) { if (nodes[0] == null) { return String.Empty; } return PSObject.AsPSObject(nodes[0]).ToString(); } return PSObject.ToStringEnumerable(null, (IEnumerable)nodes, null, null, null); } } }