Files
PowerShell/src/System.Management.Automation/engine/serialization.cs
T

7608 lines
298 KiB
C#
Raw Normal View History

// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Diagnostics.CodeAnalysis;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Management.Automation.Internal;
using System.Management.Automation.Language;
2019-04-30 10:25:11 +05:00
using System.Management.Automation.Remoting;
using System.Management.Automation.Runspaces;
using System.Management.Automation.Tracing;
2017-04-17 11:52:38 -07:00
using System.Net.Mail;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Security;
using System.Text;
using System.Xml;
2019-04-30 10:25:11 +05:00
using Microsoft.Management.Infrastructure;
using Microsoft.Management.Infrastructure.Serialization;
using Microsoft.PowerShell.Commands;
2019-04-30 10:25:11 +05:00
using Dbg = System.Management.Automation.Diagnostics;
namespace System.Management.Automation
{
2016-07-29 13:02:49 -07:00
[Flags]
internal enum SerializationOptions
{
None = 0,
UseDepthFromTypes = 1,
NoRootElement = 2,
NoNamespace = 4,
NoObjectRefIds = 8,
PreserveSerializationSettingOfOriginal = 16,
2016-07-29 13:02:49 -07:00
RemotingOptions = UseDepthFromTypes | NoRootElement | NoNamespace | PreserveSerializationSettingOfOriginal,
}
internal class SerializationContext
{
private const int DefaultSerializationDepth = 2;
internal SerializationContext()
: this(DefaultSerializationDepth, true)
{
}
internal SerializationContext(int depth, bool useDepthFromTypes)
: this(
2016-07-29 13:02:49 -07:00
depth,
(useDepthFromTypes ? SerializationOptions.UseDepthFromTypes : SerializationOptions.None) |
2016-07-29 13:02:49 -07:00
SerializationOptions.PreserveSerializationSettingOfOriginal,
null)
{
}
internal SerializationContext(int depth, SerializationOptions options, PSRemotingCryptoHelper cryptoHelper)
{
if (depth < 1)
{
throw PSTraceSource.NewArgumentException("writer", Serialization.DepthOfOneRequired);
}
this.depth = depth;
this.options = options;
this.cryptoHelper = cryptoHelper;
}
2016-07-29 13:02:49 -07:00
internal readonly int depth;
internal readonly SerializationOptions options;
internal readonly PSRemotingCryptoHelper cryptoHelper;
internal readonly CimClassSerializationCache<CimClassSerializationId> cimClassSerializationIdCache = new CimClassSerializationCache<CimClassSerializationId>();
}
2016-07-29 13:02:49 -07:00
/// <summary>
/// This class provides public functionality for serializing a PSObject.
2016-07-29 13:02:49 -07:00
/// </summary>
public class PSSerializer
{
internal PSSerializer() { }
2016-07-29 13:02:49 -07:00
/// <summary>
/// Serializes an object into PowerShell CliXml.
2016-07-29 13:02:49 -07:00
/// </summary>
/// <param name="source">The input object to serialize. Serializes to a default depth of 1.</param>
/// <returns>The serialized object, as CliXml.</returns>
public static string Serialize(object source)
2016-07-29 13:02:49 -07:00
{
return Serialize(source, s_mshDefaultSerializationDepth);
}
2016-07-29 13:02:49 -07:00
/// <summary>
/// Serializes an object into PowerShell CliXml.
2016-07-29 13:02:49 -07:00
/// </summary>
/// <param name="source">The input object to serialize.</param>
/// <param name="depth">The depth of the members to serialize.</param>
/// <returns>The serialized object, as CliXml.</returns>
public static string Serialize(object source, int depth)
2016-07-29 13:02:49 -07:00
{
// Create an xml writer
StringBuilder sb = new StringBuilder();
XmlWriterSettings xmlSettings = new XmlWriterSettings();
xmlSettings.CloseOutput = true;
xmlSettings.Encoding = System.Text.Encoding.Unicode;
xmlSettings.Indent = true;
xmlSettings.OmitXmlDeclaration = true;
XmlWriter xw = XmlWriter.Create(sb, xmlSettings);
2016-07-29 13:02:49 -07:00
// Serialize the objects
Serializer serializer = new Serializer(xw, depth, true);
serializer.Serialize(source);
serializer.Done();
serializer = null;
2016-07-29 13:02:49 -07:00
// Return the output
return sb.ToString();
}
2016-07-29 13:02:49 -07:00
/// <summary>
/// Deserializes PowerShell CliXml into an object.
/// </summary>
/// <param name="source">The CliXml the represents the object to deserialize.</param>
/// <returns>An object that represents the serialized content.</returns>
2016-07-29 13:02:49 -07:00
public static object Deserialize(string source)
{
object[] results = DeserializeAsList(source);
2016-07-29 13:02:49 -07:00
// Return the results
if (results.Length == 0)
{
return null;
}
else if (results.Length == 1)
{
return results[0];
}
else
{
return results;
}
}
2016-07-29 13:02:49 -07:00
/// <summary>
/// Deserializes PowerShell CliXml into a list of objects.
/// </summary>
/// <param name="source">The CliXml the represents the object to deserialize.</param>
/// <returns>An object array represents the serialized content.</returns>
2016-07-29 13:02:49 -07:00
public static object[] DeserializeAsList(string source)
{
List<object> results = new List<object>();
2016-07-29 13:02:49 -07:00
// Create the text reader to hold the content
TextReader textReader = new StringReader(source);
XmlReader xmlReader = XmlReader.Create(textReader, InternalDeserializer.XmlReaderSettingsForCliXml);
2016-07-29 13:02:49 -07:00
// Deserialize the content
Deserializer deserializer = new Deserializer(xmlReader);
while (!deserializer.Done())
{
object result = deserializer.Deserialize();
results.Add(result);
}
2016-07-29 13:02:49 -07:00
return results.ToArray();
}
2016-07-29 13:02:49 -07:00
/// <summary>
/// Default depth of serialization.
2016-07-29 13:02:49 -07:00
/// </summary>
private static readonly int s_mshDefaultSerializationDepth = 1;
2016-07-29 13:02:49 -07:00
}
2016-07-29 13:02:49 -07:00
/// <summary>
/// This class provides functionality for serializing a PSObject.
2016-07-29 13:02:49 -07:00
/// </summary>
internal class Serializer
{
#region constructor
2016-07-29 13:02:49 -07:00
private readonly InternalSerializer _serializer;
2016-07-29 13:02:49 -07:00
/// <summary>
/// Creates a Serializer using default serialization context.
2016-07-29 13:02:49 -07:00
/// </summary>
/// <param name="writer">Writer to be used for serialization.</param>
2016-07-29 13:02:49 -07:00
internal Serializer(XmlWriter writer)
: this(writer, new SerializationContext())
{
}
2016-07-29 13:02:49 -07:00
/// <summary>
/// Creates a Serializer using specified serialization context.
2016-07-29 13:02:49 -07:00
/// </summary>
/// <param name="writer">Writer to be used for serialization.</param>
/// <param name="depth">Depth of serialization.</param>
2016-07-29 13:02:49 -07:00
/// <param name="useDepthFromTypes">
2020-11-24 04:35:46 +00:00
/// if <see langword="true"/> then types.ps1xml can override depth
2016-07-29 13:02:49 -07:00
/// for a particular types (using SerializationDepth property)
/// </param>
internal Serializer(XmlWriter writer, int depth, bool useDepthFromTypes)
: this(writer, new SerializationContext(depth, useDepthFromTypes))
{
}
2016-07-29 13:02:49 -07:00
/// <summary>
/// Creates a Serializer using specified serialization context.
2016-07-29 13:02:49 -07:00
/// </summary>
/// <param name="writer">Writer to be used for serialization.</param>
/// <param name="context">Serialization context.</param>
2016-07-29 13:02:49 -07:00
internal Serializer(XmlWriter writer, SerializationContext context)
{
if (writer == null)
2016-07-29 13:02:49 -07:00
{
2020-05-19 19:57:52 +01:00
throw PSTraceSource.NewArgumentException(nameof(writer));
2016-07-29 13:02:49 -07:00
}
if (context == null)
2016-07-29 13:02:49 -07:00
{
2020-05-19 19:57:52 +01:00
throw PSTraceSource.NewArgumentException(nameof(context));
2016-07-29 13:02:49 -07:00
}
2016-07-29 13:02:49 -07:00
_serializer = new InternalSerializer(writer, context);
_serializer.Start();
}
2016-07-29 13:02:49 -07:00
#endregion constructor
2016-07-29 13:02:49 -07:00
#region public methods / properties
2016-07-29 13:02:49 -07:00
/// <summary>
/// Used by Remoting infrastructure. This TypeTable instance
/// will be used by Serializer if ExecutionContext is not
/// available (to get the ExecutionContext's TypeTable)
2016-07-29 13:02:49 -07:00
/// </summary>
internal TypeTable TypeTable
{
get { return _serializer.TypeTable; }
2016-07-29 13:02:49 -07:00
set { _serializer.TypeTable = value; }
}
2016-07-29 13:02:49 -07:00
/// <summary>
/// Serializes the object.
2016-07-29 13:02:49 -07:00
/// </summary>
/// <param name="source">Object to be serialized.</param>
2016-07-29 13:02:49 -07:00
/// <remarks>
2017-01-16 13:31:14 -08:00
/// Please note that this method shouldn't throw any exceptions.
2016-07-29 13:02:49 -07:00
/// If it throws - please open a bug.
/// </remarks>
internal void Serialize(object source)
{
Serialize(source, null);
}
2016-07-29 13:02:49 -07:00
/// <summary>
/// Serializes passed in object.
2016-07-29 13:02:49 -07:00
/// </summary>
/// <param name="source">
/// object to be serialized
/// </param>
/// <param name="streamName">
/// Stream to which this object belong. Ex: Output, Error etc.
/// </param>
/// <remarks>
2017-01-16 13:31:14 -08:00
/// Please note that this method shouldn't throw any exceptions.
2016-07-29 13:02:49 -07:00
/// If it throws - please open a bug.
/// </remarks>
internal void Serialize(object source, string streamName)
{
_serializer.WriteOneTopLevelObject(source, streamName);
}
2016-07-29 13:02:49 -07:00
/// <summary>
/// Write the end of root element.
2016-07-29 13:02:49 -07:00
/// </summary>
internal void Done()
{
_serializer.End();
}
2016-07-29 13:02:49 -07:00
internal void Stop()
{
_serializer.Stop();
}
2016-07-29 13:02:49 -07:00
#endregion
}
2016-07-29 13:02:49 -07:00
[Flags]
internal enum DeserializationOptions
{
None = 0,
2017-01-16 13:31:14 -08:00
NoRootElement = 256, // I start at 256 to try not to overlap
2016-07-29 13:02:49 -07:00
NoNamespace = 512, // with SerializationOptions and to catch bugs early
DeserializeScriptBlocks = 1024,
2016-07-29 13:02:49 -07:00
RemotingOptions = NoRootElement | NoNamespace,
}
2016-07-29 13:02:49 -07:00
internal class DeserializationContext
{
internal DeserializationContext()
: this(DeserializationOptions.None, null)
{
}
2016-07-29 13:02:49 -07:00
internal DeserializationContext(DeserializationOptions options, PSRemotingCryptoHelper cryptoHelper)
{
this.options = options;
this.cryptoHelper = cryptoHelper;
}
2016-07-29 13:02:49 -07:00
/// <summary>
/// Limits the total data processed by the deserialization context. Deserialization context
/// is used by PriorityReceivedDataCollection (remoting) to process incoming data from the
/// remote end. A value of Null means that the max memory is unlimited.
/// </summary>
2020-11-05 05:05:50 +00:00
internal int? MaximumAllowedMemory { get; set; }
2016-07-29 13:02:49 -07:00
/// <summary>
/// Logs that memory used by deserialized objects is not related to the size of input xml.
/// Used mainly to account for memory usage of cloned TypeNames when calculating memory quota usage.
/// </summary>
/// <param name="amountOfExtraMemory"></param>
internal void LogExtraMemoryUsage(int amountOfExtraMemory)
{
if (amountOfExtraMemory < 0)
{
return;
}
2016-08-02 13:29:09 -07:00
if (MaximumAllowedMemory.HasValue)
2016-07-29 13:02:49 -07:00
{
2016-08-02 13:29:09 -07:00
if (amountOfExtraMemory > (MaximumAllowedMemory.Value - _totalDataProcessedSoFar))
2016-07-29 13:02:49 -07:00
{
2016-08-02 13:29:09 -07:00
string message = StringUtil.Format(Serialization.DeserializationMemoryQuota, ((double)MaximumAllowedMemory.Value) / (1 << 20),
2016-07-29 13:02:49 -07:00
ConfigurationDataFromXML.MAXRCVDOBJSIZETOKEN_CamelCase,
ConfigurationDataFromXML.MAXRCVDCMDSIZETOKEN_CamelCase);
throw new XmlException(message);
}
_totalDataProcessedSoFar += amountOfExtraMemory;
2016-07-29 13:02:49 -07:00
}
}
2016-07-29 13:02:49 -07:00
private int _totalDataProcessedSoFar;
2016-07-29 13:02:49 -07:00
internal readonly DeserializationOptions options;
internal readonly PSRemotingCryptoHelper cryptoHelper;
internal static readonly int MaxItemsInCimClassCache = 100;
2016-07-29 13:02:49 -07:00
internal readonly CimClassDeserializationCache<CimClassSerializationId> cimClassSerializationIdCache = new CimClassDeserializationCache<CimClassSerializationId>();
}
2016-07-29 13:02:49 -07:00
internal class CimClassDeserializationCache<TKey>
{
private readonly Dictionary<TKey, CimClass> _cimClassIdToClass = new Dictionary<TKey, CimClass>();
2016-07-29 13:02:49 -07:00
internal void AddCimClassToCache(TKey key, CimClass cimClass)
{
if (_cimClassIdToClass.Count >= DeserializationContext.MaxItemsInCimClassCache)
{
_cimClassIdToClass.Clear();
}
2016-07-29 13:02:49 -07:00
_cimClassIdToClass.Add(key, cimClass);
2016-07-29 13:02:49 -07:00
/* PRINTF DEBUG
Console.WriteLine("Contents of deserialization cache (after a call to AddCimClassToCache ({0})):", key);
Console.WriteLine(" Count = {0}", this._cimClassIdToClass.Count);
foreach (var t in this._cimClassIdToClass.Keys)
{
Console.WriteLine(" {0}", t);
}
*/
2016-07-29 13:02:49 -07:00
}
2016-07-29 13:02:49 -07:00
internal CimClass GetCimClassFromCache(TKey key)
{
CimClass cimClass;
if (_cimClassIdToClass.TryGetValue(key, out cimClass))
{
/* PRINTF DEBUG
Console.WriteLine("GetCimClassFromCache - class found: {0}", key);
*/
2016-07-29 13:02:49 -07:00
return cimClass;
}
2016-07-29 13:02:49 -07:00
/* PRINTF DEBUG
Console.WriteLine("GetCimClassFromCache - class NOT found: {0}", key);
*/
2016-07-29 13:02:49 -07:00
return null;
}
}
2016-07-29 13:02:49 -07:00
internal class CimClassSerializationCache<TKey>
{
private readonly HashSet<TKey> _cimClassesHeldByDeserializer = new HashSet<TKey>(EqualityComparer<TKey>.Default);
2016-07-29 13:02:49 -07:00
internal bool DoesDeserializerAlreadyHaveCimClass(TKey key)
{
return _cimClassesHeldByDeserializer.Contains(key);
}
2016-07-29 13:02:49 -07:00
internal void AddClassToCache(TKey key)
{
Dbg.Assert(!_cimClassesHeldByDeserializer.Contains(key), "This method should not be called for classes already in the cache");
2016-07-29 13:02:49 -07:00
if (_cimClassesHeldByDeserializer.Count >= DeserializationContext.MaxItemsInCimClassCache)
{
_cimClassesHeldByDeserializer.Clear();
}
2016-07-29 13:02:49 -07:00
_cimClassesHeldByDeserializer.Add(key);
/* PRINTF DEBUG
Console.WriteLine("Contents of serialization cache (after adding {0}):", key);
Console.WriteLine(" Count = {0}", this._cimClassesHeldByDeserializer.Count);
foreach (var t in _cimClassesHeldByDeserializer)
{
Console.WriteLine(" {0}", t);
}
*/
2016-07-29 13:02:49 -07:00
}
}
2016-07-29 13:02:49 -07:00
internal class CimClassSerializationId : Tuple<string, string, string, int>
{
public CimClassSerializationId(string className, string namespaceName, string computerName, int hashCode)
: base(className, namespaceName, computerName, hashCode)
{
}
2016-07-29 13:02:49 -07:00
public string ClassName { get { return this.Item1; } }
2016-07-29 13:02:49 -07:00
public string NamespaceName { get { return this.Item2; } }
2016-07-29 13:02:49 -07:00
public string ComputerName { get { return this.Item3; } }
2016-07-29 13:02:49 -07:00
public int ClassHashCode { get { return this.Item4; } }
}
2016-07-29 13:02:49 -07:00
/// <summary>
/// This class provides functionality for deserializing a PSObject.
2016-07-29 13:02:49 -07:00
/// </summary>
internal class Deserializer
{
#region constructor
2016-07-29 13:02:49 -07:00
private readonly XmlReader _reader;
private readonly InternalDeserializer _deserializer;
private readonly DeserializationContext _context;
2016-07-29 13:02:49 -07:00
/// <summary>
/// Creates a Deserializer using default deserialization context.
2016-07-29 13:02:49 -07:00
/// </summary>
/// <param name="reader">Reader to be used for deserialization.</param>
2016-07-29 13:02:49 -07:00
/// <exception cref="XmlException">
/// Thrown when the xml is in an incorrect format
/// </exception>
internal Deserializer(XmlReader reader)
: this(reader, new DeserializationContext())
{
}
2016-07-29 13:02:49 -07:00
/// <summary>
/// Creates a Deserializer using specified serialization context.
2016-07-29 13:02:49 -07:00
/// </summary>
/// <param name="reader">Reader to be used for deserialization.</param>
/// <param name="context">Serialization context.</param>
2016-07-29 13:02:49 -07:00
/// <exception cref="XmlException">
/// Thrown when the xml is in an incorrect format
/// </exception>
internal Deserializer(XmlReader reader, DeserializationContext context)
{
if (reader == null)
2016-07-29 13:02:49 -07:00
{
2020-05-19 19:57:52 +01:00
throw PSTraceSource.NewArgumentNullException(nameof(reader));
2016-07-29 13:02:49 -07:00
}
2016-07-29 13:02:49 -07:00
_reader = reader;
_context = context;
_deserializer = new InternalDeserializer(_reader, _context);
2016-07-29 13:02:49 -07:00
try
{
Start();
}
catch (XmlException exception)
{
ReportExceptionForETW(exception);
throw;
}
}
2016-07-29 13:02:49 -07:00
#endregion constructor
2016-07-29 13:02:49 -07:00
#region public method / properties
2016-07-29 13:02:49 -07:00
private static void ReportExceptionForETW(XmlException exception)
{
PSEtwLog.LogAnalyticError(
PSEventId.Serializer_XmlExceptionWhenDeserializing, PSOpcode.Exception, PSTask.Serialization,
PSKeyword.Serializer | PSKeyword.UseAlwaysAnalytic,
exception.LineNumber, exception.LinePosition, exception.ToString());
}
2016-07-29 13:02:49 -07:00
private bool _done = false;
2016-07-29 13:02:49 -07:00
/// <summary>
/// Used by Remoting infrastructure. This TypeTable instance
/// will be used by Deserializer if ExecutionContext is not
/// available (to get the ExecutionContext's TypeTable)
2016-07-29 13:02:49 -07:00
/// </summary>
internal TypeTable TypeTable
{
get { return _deserializer.TypeTable; }
2016-07-29 13:02:49 -07:00
set { _deserializer.TypeTable = value; }
}
2016-07-29 13:02:49 -07:00
/// <summary>
2017-01-16 13:31:14 -08:00
/// Read the root element tag and set the cursor to start tag of
2016-07-29 13:02:49 -07:00
/// first object.
/// </summary>
private void Start()
{
Dbg.Assert(_reader.ReadState == ReadState.Initial, "When deserialization starts we should have XmlReader.ReadState == Initial");
Dbg.Assert(_reader.NodeType == XmlNodeType.None, "When deserialization starts we should have XmlReader.NodeType == None");
_reader.Read();
// If version is not provided, we assume it is the default
2016-07-29 13:02:49 -07:00
string version = InternalSerializer.DefaultVersion;
if ((_context.options & DeserializationOptions.NoRootElement) == DeserializationOptions.NoRootElement)
2016-07-29 13:02:49 -07:00
{
_done = _reader.EOF;
}
else
{
// Make sure the reader is positioned on the root ( <Objs> ) element (not on XmlDeclaration for example)
_reader.MoveToContent();
Dbg.Assert(_reader.EOF || (_reader.NodeType == XmlNodeType.Element), "When deserialization starts reading we should have XmlReader.NodeType == Element");
// Read version attribute and validate it.
2016-07-29 13:02:49 -07:00
string versionAttribute = _reader.GetAttribute(SerializationStrings.VersionAttribute);
if (versionAttribute != null)
{
version = versionAttribute;
}
// If the root element tag is empty, there are no objects to read.
2016-07-29 13:02:49 -07:00
if (!_deserializer.ReadStartElementAndHandleEmpty(SerializationStrings.RootElementTag))
{
_done = true;
}
}
2016-07-29 13:02:49 -07:00
_deserializer.ValidateVersion(version);
}
2016-07-29 13:02:49 -07:00
internal bool Done()
{
if (!_done)
2016-07-29 13:02:49 -07:00
{
if ((_context.options & DeserializationOptions.NoRootElement) == DeserializationOptions.NoRootElement)
2016-07-29 13:02:49 -07:00
{
_done = _reader.EOF;
}
else
{
if (_reader.NodeType == XmlNodeType.EndElement)
{
try
{
_reader.ReadEndElement();
}
catch (XmlException exception)
{
ReportExceptionForETW(exception);
throw;
}
2016-07-29 13:02:49 -07:00
_done = true;
}
}
}
2016-07-29 13:02:49 -07:00
return _done;
}
2016-07-29 13:02:49 -07:00
internal void Stop()
{
_deserializer.Stop();
}
2016-07-29 13:02:49 -07:00
/// <summary>
/// Deserializes next object.
/// </summary>
/// <exception cref="XmlException">
/// Thrown when the xml is in an incorrect format
/// </exception>
internal object Deserialize()
{
string ignore;
return Deserialize(out ignore);
}
/// <summary>
/// Deserializes next object.
/// </summary>
/// <param name="streamName">Stream the object belongs to (i.e. "Error", "Output", etc.).</param>
/// <exception cref="XmlException">
/// Thrown when the xml is in an incorrect format
/// </exception>
internal object Deserialize(out string streamName)
{
if (Done())
{
throw PSTraceSource.NewInvalidOperationException(Serialization.ReadCalledAfterDone);
}
try
{
return _deserializer.ReadOneObject(out streamName);
}
catch (XmlException exception)
{
ReportExceptionForETW(exception);
throw;
}
}
2016-07-29 13:02:49 -07:00
#endregion public methods
2016-07-29 13:02:49 -07:00
#region Helper methods for dealing with "Deserialized." prefix
2016-07-29 13:02:49 -07:00
/// <summary>
/// Adds "Deserialized." prefix to passed in argument if not already present.
2016-07-29 13:02:49 -07:00
/// </summary>
/// <param name="type"></param>
internal static void AddDeserializationPrefix(ref string type)
{
Dbg.Assert(type != null, "caller should validate the parameter");
if (!type.StartsWith(Deserializer.DeserializationTypeNamePrefix, StringComparison.OrdinalIgnoreCase))
{
type = type.Insert(0, Deserializer.DeserializationTypeNamePrefix);
}
}
2016-07-29 13:02:49 -07:00
/// <summary>
/// Checks if an object <paramref name="o"/> is either a live or deserialized instance of class <paramref name="type"/> or one of its subclasses.
/// </summary>
/// <param name="o"></param>
/// <param name="type"></param>
2020-11-24 04:35:46 +00:00
/// <returns><see langword="true"/> if <paramref name="o"/> is either a live or deserialized instance of class <paramref name="type"/> or one of its subclasses; <see langword="false"/> otherwise.</returns>
2016-07-29 13:02:49 -07:00
internal static bool IsInstanceOfType(object o, Type type)
{
if (type == null)
2016-07-29 13:02:49 -07:00
{
2020-05-19 19:57:52 +01:00
throw PSTraceSource.NewArgumentNullException(nameof(type));
2016-07-29 13:02:49 -07:00
}
if (o == null)
2016-07-29 13:02:49 -07:00
{
return false;
}
2016-07-29 13:02:49 -07:00
return type.IsInstanceOfType(PSObject.Base(o)) || IsDeserializedInstanceOfType(o, type);
}
2016-07-29 13:02:49 -07:00
/// <summary>
/// Checks if an object <paramref name="o"/> is a deserialized instance of class <paramref name="type"/> or one of its subclasses.
/// </summary>
/// <param name="o"></param>
/// <param name="type"></param>
2020-11-24 04:35:46 +00:00
/// <returns><see langword="true"/> if <paramref name="o"/> is a deserialized instance of class <paramref name="type"/> or one of its subclasses; <see langword="false"/> otherwise.</returns>
2016-07-29 13:02:49 -07:00
internal static bool IsDeserializedInstanceOfType(object o, Type type)
{
if (type == null)
2016-07-29 13:02:49 -07:00
{
2020-05-19 19:57:52 +01:00
throw PSTraceSource.NewArgumentNullException(nameof(type));
2016-07-29 13:02:49 -07:00
}
if (o == null)
2016-07-29 13:02:49 -07:00
{
return false;
}
2016-07-29 13:02:49 -07:00
PSObject pso = o as PSObject;
if (pso != null)
{
IEnumerable<string> typeNames = pso.InternalTypeNames;
if (typeNames != null)
{
foreach (string typeName in typeNames)
{
if (typeName.Length == Deserializer.DeserializationTypeNamePrefix.Length + type.FullName.Length &&
typeName.StartsWith(Deserializer.DeserializationTypeNamePrefix, StringComparison.OrdinalIgnoreCase) &&
typeName.EndsWith(type.FullName, StringComparison.OrdinalIgnoreCase))
{
return true;
}
}
}
}
2016-07-29 13:02:49 -07:00
// not the right type
return false;
}
2016-07-29 13:02:49 -07:00
internal static string MaskDeserializationPrefix(string typeName)
{
if (typeName == null)
2016-07-29 13:02:49 -07:00
{
return null;
}
2016-07-29 13:02:49 -07:00
if (typeName.StartsWith(Deserializer.DeserializationTypeNamePrefix, StringComparison.OrdinalIgnoreCase))
{
typeName = typeName.Substring(Deserializer.DeserializationTypeNamePrefix.Length);
}
2016-07-29 13:02:49 -07:00
return typeName;
}
2016-07-29 13:02:49 -07:00
/// <summary>
/// Gets a new collection of typenames without "Deserialization." prefix
2017-01-16 13:31:14 -08:00
/// in the typename. This will allow to map type info/format info of the orignal type
2016-07-29 13:02:49 -07:00
/// for deserialized objects.
/// </summary>
/// <param name="typeNames"></param>
/// <returns>
/// Null if no type with "Deserialized." prefix is found.
/// Otherwise <paramref name="typeNames"/> with the prefix removed if any.
/// </returns>
internal static Collection<string> MaskDeserializationPrefix(Collection<string> typeNames)
{
Dbg.Assert(typeNames != null, "typeNames cannot be null");
2016-07-29 13:02:49 -07:00
bool atleastOneDeserializedTypeFound = false;
2016-07-29 13:02:49 -07:00
Collection<string> typesWithoutPrefix = new Collection<string>();
foreach (string type in typeNames)
{
if (type.StartsWith(Deserializer.DeserializationTypeNamePrefix,
StringComparison.OrdinalIgnoreCase))
{
atleastOneDeserializedTypeFound = true;
// remove *only* the prefix
typesWithoutPrefix.Add(type.Substring(Deserializer.DeserializationTypeNamePrefix.Length));
}
else
{
typesWithoutPrefix.Add(type);
}
}
2016-07-29 13:02:49 -07:00
if (atleastOneDeserializedTypeFound)
{
return typesWithoutPrefix;
}
2016-07-29 13:02:49 -07:00
return null;
}
2016-07-29 13:02:49 -07:00
/// <summary>
/// Used to prefix a typename for deserialization.
/// </summary>
private const string DeserializationTypeNamePrefix = "Deserialized.";
2016-07-29 13:02:49 -07:00
#endregion
}
2016-07-29 13:02:49 -07:00
/// <summary>
/// Types of known type container supported by monad.
2016-07-29 13:02:49 -07:00
/// </summary>
internal enum ContainerType
{
Dictionary,
Queue,
Stack,
List,
Enumerable,
None
}
2016-07-29 13:02:49 -07:00
/// <summary>
/// This internal helper class provides methods for serializing mshObject.
/// </summary>
internal class InternalSerializer
{
#region constructor
2016-07-29 13:02:49 -07:00
internal const string DefaultVersion = "1.1.0.1";
2016-07-29 13:02:49 -07:00
/// <summary>
/// Xml writer to be used.
2016-07-29 13:02:49 -07:00
/// </summary>
private readonly XmlWriter _writer;
2016-07-29 13:02:49 -07:00
/// <summary>
/// Serialization context.
2016-07-29 13:02:49 -07:00
/// </summary>
private readonly SerializationContext _context;
2016-07-29 13:02:49 -07:00
/// Used by Remoting infrastructure. This TypeTable instance
/// will be used by Serializer if ExecutionContext is not
/// available (to get the ExecutionContext's TypeTable)
2016-07-29 13:02:49 -07:00
private TypeTable _typeTable;
2016-07-29 13:02:49 -07:00
/// <summary>
/// Depth below top level - used to prevent infinitely deep serialization
/// (without this protection it would be possible i.e. with SerializationDepth and recursion)
/// </summary>
private int _depthBelowTopLevel;
2016-07-29 13:02:49 -07:00
private const int MaxDepthBelowTopLevel = 50;
2016-07-29 13:02:49 -07:00
private readonly ReferenceIdHandlerForSerializer<object> _objectRefIdHandler;
private readonly ReferenceIdHandlerForSerializer<ConsolidatedString> _typeRefIdHandler;
2016-07-29 13:02:49 -07:00
internal InternalSerializer(XmlWriter writer, SerializationContext context)
{
Dbg.Assert(writer != null, "caller should validate the parameter");
Dbg.Assert(context != null, "caller should validate the parameter");
_writer = writer;
_context = context;
2016-07-29 13:02:49 -07:00
IDictionary<object, ulong> objectRefIdDictionary = null;
if ((_context.options & SerializationOptions.NoObjectRefIds) == 0)
{
objectRefIdDictionary = new WeakReferenceDictionary<UInt64>();
}
2016-07-29 13:02:49 -07:00
_objectRefIdHandler = new ReferenceIdHandlerForSerializer<object>(objectRefIdDictionary);
2016-07-29 13:02:49 -07:00
_typeRefIdHandler = new ReferenceIdHandlerForSerializer<ConsolidatedString>(
new Dictionary<ConsolidatedString, UInt64>(ConsolidatedString.EqualityComparer));
}
2016-07-29 13:02:49 -07:00
#endregion
2016-07-29 13:02:49 -07:00
/// <summary>
/// Used by Remoting infrastructure. This TypeTable instance
/// will be used by Serializer if ExecutionContext is not
/// available (to get the ExecutionContext's TypeTable)
2016-07-29 13:02:49 -07:00
/// </summary>
internal TypeTable TypeTable
{
get { return _typeTable; }
2016-07-29 13:02:49 -07:00
set { _typeTable = value; }
}
2016-07-29 13:02:49 -07:00
/// <summary>
/// Writes the start of root element.
2016-07-29 13:02:49 -07:00
/// </summary>
internal void Start()
{
if ((_context.options & SerializationOptions.NoRootElement) != SerializationOptions.NoRootElement)
2016-07-29 13:02:49 -07:00
{
this.WriteStartElement(SerializationStrings.RootElementTag);
this.WriteAttribute(SerializationStrings.VersionAttribute, InternalSerializer.DefaultVersion);
}
}
2016-07-29 13:02:49 -07:00
/// <summary>
/// Writes the end of root element.
2016-07-29 13:02:49 -07:00
/// </summary>
internal void End()
{
if ((_context.options & SerializationOptions.NoRootElement) != SerializationOptions.NoRootElement)
2016-07-29 13:02:49 -07:00
{
_writer.WriteEndElement();
}
2016-07-29 13:02:49 -07:00
_writer.Flush();
}
2016-07-29 13:02:49 -07:00
private bool _isStopping = false;
2016-07-29 13:02:49 -07:00
/// <summary>
/// Called from a separate thread will stop the serialization process.
2016-07-29 13:02:49 -07:00
/// </summary>
internal void Stop()
{
_isStopping = true;
}
private void CheckIfStopping()
{
2016-07-29 13:02:49 -07:00
if (_isStopping)
{
throw PSTraceSource.NewInvalidOperationException(Serialization.Stopping);
}
}
2016-07-29 13:02:49 -07:00
internal static bool IsPrimitiveKnownType(Type input)
{
// Check if source is of primitive known type
2016-07-29 13:02:49 -07:00
TypeSerializationInfo pktInfo = KnownTypes.GetTypeSerializationInfo(input);
return (pktInfo != null);
}
2016-07-29 13:02:49 -07:00
/// <summary>
/// This writes one object.
/// </summary>
/// <param name="source">
/// source to be serialized.
/// </param>
/// <param name="streamName">
/// Stream to which source belongs
/// </param>
internal void WriteOneTopLevelObject
(
object source,
string streamName
)
{
Dbg.Assert(_depthBelowTopLevel == 0, "InternalSerializer.depthBelowTopLevel should be 0 at top-level");
WriteOneObject(source, streamName, null, _context.depth);
}
2016-07-29 13:02:49 -07:00
private void WriteOneObject
(
object source,
string streamName,
string property,
int depth
)
{
Dbg.Assert(depth >= 0, "depth should always be greater or equal to zero");
this.CheckIfStopping();
if (source == null)
2016-07-29 13:02:49 -07:00
{
WriteNull(streamName, property);
return;
}
2016-07-29 13:02:49 -07:00
try
{
_depthBelowTopLevel++;
Dbg.Assert(_depthBelowTopLevel <= MaxDepthBelowTopLevel, "depthBelowTopLevel should be <= MaxDepthBelowTopLevel");
if (HandleMaxDepth(source, streamName, property))
{
return;
}
2016-07-29 13:02:49 -07:00
depth = GetDepthOfSerialization(source, depth);
2016-07-29 13:02:49 -07:00
if (HandlePrimitiveKnownTypeByConvertingToPSObject(source, streamName, property, depth))
{
return;
}
// Object is not of primitive known type. Check if this has
// already been serialized.
2016-07-29 13:02:49 -07:00
string refId = _objectRefIdHandler.GetRefId(source);
if (refId != null)
{
WritePSObjectReference(streamName, property, refId);
return;
}
2016-07-29 13:02:49 -07:00
if (HandlePrimitiveKnownTypePSObject(source, streamName, property, depth))
{
return;
}
// Note: We do not use containers in depth calculation. i.e even if the
// current depth is zero, we serialize the container. All contained items will
// get serialized with depth zero.
2016-07-29 13:02:49 -07:00
if (HandleKnownContainerTypes(source, streamName, property, depth))
{
return;
}
2016-07-29 13:02:49 -07:00
PSObject mshSource = PSObject.AsPSObject(source);
// If depth is zero, complex type should be serialized as string.
2016-07-29 13:02:49 -07:00
if (depth == 0 || SerializeAsString(mshSource))
{
HandlePSObjectAsString(mshSource, streamName, property, depth);
return;
}
2016-07-29 13:02:49 -07:00
HandleComplexTypePSObject(source, streamName, property, depth);
return;
}
finally
{
_depthBelowTopLevel--;
Dbg.Assert(_depthBelowTopLevel >= 0, "depthBelowTopLevel should be >= 0");
}
}
2016-07-29 13:02:49 -07:00
private bool HandleMaxDepth(object source, string streamName, string property)
{
if (_depthBelowTopLevel == MaxDepthBelowTopLevel)
{
// assert commented out because of clashes with Wei's tests
// Dbg.Assert(false, "We should never reach MaxDepthBelowTopLevel with non-malicious input");
2016-07-29 13:02:49 -07:00
PSEtwLog.LogAnalyticError(PSEventId.Serializer_MaxDepthWhenSerializing, PSOpcode.Exception,
PSTask.Serialization, PSKeyword.Serializer, source.GetType().AssemblyQualifiedName, property, _depthBelowTopLevel);
2016-07-29 13:02:49 -07:00
string content = Serialization.DeserializationTooDeep;
HandlePrimitiveKnownType(content, streamName, property);
return true;
}
else
{
return false;
}
}
2016-07-29 13:02:49 -07:00
/// <summary>
/// Serializes Primitive Known Types.
/// </summary>
/// <returns>
/// true if source is handled, else false.
/// </returns>
private bool HandlePrimitiveKnownType
(
object source,
string streamName,
string property
)
{
Dbg.Assert(source != null, "caller should validate the parameter");
// Check if source is of primitive known type
2016-07-29 13:02:49 -07:00
TypeSerializationInfo pktInfo = KnownTypes.GetTypeSerializationInfo(source.GetType());
if (pktInfo != null)
{
WriteOnePrimitiveKnownType(this, streamName, property, source, pktInfo);
return true;
}
2016-07-29 13:02:49 -07:00
return false;
}
/// <summary>
/// Handles primitive known type by first converting it to a PSObject.In W8, extended
/// property data is stored external to PSObject. By converting to PSObject, we will
2016-07-29 13:02:49 -07:00
/// be able to retrieve and serialize the extended properties. This is tracked by
/// Win8: 414042.
2016-07-29 13:02:49 -07:00
/// </summary>
/// <param name="source"></param>
/// <param name="streamName"></param>
/// <param name="property"></param>
/// <param name="depth"></param>
/// <returns></returns>
private bool HandlePrimitiveKnownTypeByConvertingToPSObject(
object source,
string streamName,
string property,
int depth
)
{
Dbg.Assert(source != null, "caller should validate the parameter");
// Check if source is of primitive known type
2016-07-29 13:02:49 -07:00
TypeSerializationInfo pktInfo = KnownTypes.GetTypeSerializationInfo(source.GetType());
if (pktInfo != null)
{
PSObject pktInfoPSObject = PSObject.AsPSObject(source);
return HandlePrimitiveKnownTypePSObject(pktInfoPSObject, streamName, property, depth);
}
2016-07-29 13:02:49 -07:00
return false;
}
2016-07-29 13:02:49 -07:00
/// <summary>
/// </summary>
/// <param name="source"></param>
/// <param name="streamName"></param>
/// <param name="property"></param>
/// <returns></returns>
private bool HandleSecureString(object source, string streamName, string property)
{
Dbg.Assert(source != null, "caller should validate the parameter");
2016-07-29 13:02:49 -07:00
SecureString secureString = null;
secureString = source as SecureString;
PSObject moSource;
2016-07-29 13:02:49 -07:00
if (secureString != null)
{
moSource = PSObject.AsPSObject(secureString);
}
else
{
moSource = source as PSObject;
}
if (moSource != null && !moSource.ImmediateBaseObjectIsEmpty)
2016-07-29 13:02:49 -07:00
{
// check if source is of type secure string
secureString = moSource.ImmediateBaseObject as SecureString;
if (secureString != null)
2016-07-29 13:02:49 -07:00
{
// the principle used in serialization is that serialization
// never throws, and if something can't be serialized nothing
// is written. So we write the elements only if encryption succeeds.
// However, in the case for non-Windows where secure string encryption
// is not yet supported, a PSCryptoException will be thrown.
string encryptedString;
if (_context.cryptoHelper != null)
2016-07-29 13:02:49 -07:00
{
encryptedString = _context.cryptoHelper.EncryptSecureString(secureString);
2016-07-29 13:02:49 -07:00
}
else
2016-07-29 13:02:49 -07:00
{
encryptedString = Microsoft.PowerShell.SecureStringHelper.Protect(secureString);
2016-07-29 13:02:49 -07:00
}
if (property != null)
{
WriteStartElement(SerializationStrings.SecureStringTag);
WriteNameAttribute(property);
}
else
{
WriteStartElement(SerializationStrings.SecureStringTag);
}
if (streamName != null)
{
WriteAttribute(SerializationStrings.StreamNameAttribute, streamName);
}
// Note: We do not use WriteRaw for serializing secure string. WriteString
// does necessary escaping which may be needed for certain
// characters.
_writer.WriteString(encryptedString);
_writer.WriteEndElement();
return true;
}
2016-07-29 13:02:49 -07:00
}
2016-07-29 13:02:49 -07:00
return false;
}
2016-07-29 13:02:49 -07:00
/// <summary>
/// Serializes PSObject whose base objects are of primitive known type.
2016-07-29 13:02:49 -07:00
/// </summary>
/// <param name="source"></param>
/// <param name="streamName"></param>
/// <param name="property"></param>
/// <param name="depth"></param>
/// <returns>
/// true if source is handled, else false.
/// </returns>
private bool HandlePrimitiveKnownTypePSObject
(
object source,
string streamName,
string property,
int depth
)
{
Dbg.Assert(source != null, "caller should validate the parameter");
2016-07-29 13:02:49 -07:00
bool sourceHandled = false;
PSObject moSource = source as PSObject;
if (moSource != null && !moSource.ImmediateBaseObjectIsEmpty)
2016-07-29 13:02:49 -07:00
{
// Check if baseObject is primitive known type
2016-07-29 13:02:49 -07:00
object baseObject = moSource.ImmediateBaseObject;
TypeSerializationInfo pktInfo = KnownTypes.GetTypeSerializationInfo(baseObject.GetType());
if (pktInfo != null)
{
WritePrimitiveTypePSObject(moSource, baseObject, pktInfo, streamName, property, depth);
sourceHandled = true;
}
}
2016-07-29 13:02:49 -07:00
return sourceHandled;
}
2016-07-29 13:02:49 -07:00
private bool HandleKnownContainerTypes
(
object source,
string streamName,
string property,
int depth
)
{
Dbg.Assert(source != null, "caller should validate the parameter");
2016-07-29 13:02:49 -07:00
ContainerType ct = ContainerType.None;
PSObject mshSource = source as PSObject;
IEnumerable enumerable = null;
IDictionary dictionary = null;
// If passed in object is PSObject with no baseobject, return false.
if (mshSource != null && mshSource.ImmediateBaseObjectIsEmpty)
2016-07-29 13:02:49 -07:00
{
return false;
}
// Check if source (or baseobject in mshSource) is known container type
2016-07-29 13:02:49 -07:00
SerializationUtilities.GetKnownContainerTypeInfo(mshSource != null ? mshSource.ImmediateBaseObject : source, out ct,
out dictionary, out enumerable);
2016-07-29 13:02:49 -07:00
if (ct == ContainerType.None)
return false;
2016-07-29 13:02:49 -07:00
string refId = _objectRefIdHandler.SetRefId(source);
WriteStartOfPSObject(
mshSource ?? PSObject.AsPSObject(source),
streamName,
property,
refId,
true, // always write TypeNames information for known container types
null); // never write ToString information for known container types
2016-07-29 13:02:49 -07:00
switch (ct)
{
case ContainerType.Dictionary:
WriteDictionary(dictionary, SerializationStrings.DictionaryTag, depth);
break;
case ContainerType.Stack:
WriteEnumerable(enumerable, SerializationStrings.StackTag, depth);
break;
case ContainerType.Queue:
WriteEnumerable(enumerable, SerializationStrings.QueueTag, depth);
break;
case ContainerType.List:
WriteEnumerable(enumerable, SerializationStrings.ListTag, depth);
break;
case ContainerType.Enumerable:
WriteEnumerable(enumerable, SerializationStrings.CollectionTag, depth);
break;
default:
Dbg.Assert(false, "All containers should be handled in the switch");
break;
}
2016-07-29 13:02:49 -07:00
if (depth != 0)
{
2017-01-16 13:31:14 -08:00
// An object which is original enumerable becomes an PSObject with ArrayList on deserialization.
// So on roundtrip it will show up as List.
2017-01-16 13:31:14 -08:00
// 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))
2016-07-29 13:02:49 -07:00
{
PSObject sourceAsPSObject = PSObject.AsPSObject(source);
PSMemberInfoInternalCollection<PSPropertyInfo> specificPropertiesToSerialize = SerializationUtilities.GetSpecificPropertiesToSerialize(sourceAsPSObject, AllPropertiesCollection, _typeTable);
2016-07-29 13:02:49 -07:00
WritePSObjectProperties(sourceAsPSObject, depth, specificPropertiesToSerialize);
SerializeExtendedProperties(sourceAsPSObject, depth, specificPropertiesToSerialize);
}
// always serialize instance properties if there are any
else if (mshSource != null)
{
SerializeInstanceProperties(mshSource, depth);
}
}
2016-07-29 13:02:49 -07:00
_writer.WriteEndElement();
2016-07-29 13:02:49 -07:00
return true;
}
2016-07-29 13:02:49 -07:00
#region Write PSObject
2016-07-29 13:02:49 -07:00
/// <summary>
/// Writes PSObject Reference Element.
2016-07-29 13:02:49 -07:00
/// </summary>
private void WritePSObjectReference
(
string streamName,
string property,
string refId
)
{
Dbg.Assert(!string.IsNullOrEmpty(refId), "caller should validate the parameter");
2016-07-29 13:02:49 -07:00
WriteStartElement(SerializationStrings.ReferenceTag);
if (streamName != null)
{
WriteAttribute(SerializationStrings.StreamNameAttribute, streamName);
}
2016-07-29 13:02:49 -07:00
if (property != null)
{
WriteNameAttribute(property);
}
2016-07-29 13:02:49 -07:00
WriteAttribute(SerializationStrings.ReferenceIdAttribute, refId);
_writer.WriteEndElement();
}
private static bool PSObjectHasModifiedTypesCollection(PSObject pso)
2016-07-29 13:02:49 -07:00
{
ConsolidatedString currentTypes = pso.InternalTypeNames;
Collection<string> originalTypes = pso.InternalAdapter.BaseGetTypeNameHierarchy(pso.ImmediateBaseObject);
2016-07-29 13:02:49 -07:00
if (currentTypes.Count != originalTypes.Count)
{
return true;
}
2016-07-29 13:02:49 -07:00
IEnumerator<string> currentEnumerator = currentTypes.GetEnumerator();
IEnumerator<string> originalEnumerator = originalTypes.GetEnumerator();
while (currentEnumerator.MoveNext() && originalEnumerator.MoveNext())
{
if (!currentEnumerator.Current.Equals(originalEnumerator.Current, StringComparison.OrdinalIgnoreCase))
{
return true;
}
}
2016-07-29 13:02:49 -07:00
return false;
}
2016-07-29 13:02:49 -07:00
/// <summary>
/// Serializes an PSObject whose baseobject is of primitive type.
/// </summary>
/// <param name="source">
/// source from which notes are written
/// </param>
/// <param name="primitive">
/// primitive object which is written as base object. In most cases it
/// is same source.ImmediateBaseObject. When PSObject is serialized as string,
/// it can be different. <see cref="HandlePSObjectAsString"/> for more info.
/// </param>
/// <param name="pktInfo">
2017-01-16 13:31:14 -08:00
/// TypeSerializationInfo for the primitive.
2016-07-29 13:02:49 -07:00
/// </param>
/// <param name="streamName"></param>
/// <param name="property"></param>
/// <param name="depth"></param>
private void WritePrimitiveTypePSObject
(
PSObject source,
object primitive,
TypeSerializationInfo pktInfo,
string streamName,
string property,
int depth
)
{
Dbg.Assert(source != null, "Caller should validate source != null");
2016-07-29 13:02:49 -07:00
string toStringValue = SerializationUtilities.GetToStringForPrimitiveObject(source);
bool hasModifiedTypesCollection = PSObjectHasModifiedTypesCollection(source);
bool hasNotes = PSObjectHasNotes(source);
bool hasModifiedToString = (toStringValue != null);
2016-07-29 13:02:49 -07:00
if (hasNotes || hasModifiedTypesCollection || hasModifiedToString)
{
WritePrimitiveTypePSObjectWithNotes(
source,
primitive,
hasModifiedTypesCollection,
toStringValue,
pktInfo,
streamName,
property,
depth);
return;
}
else
{
if (primitive != null)
{
WriteOnePrimitiveKnownType(this, streamName, property, primitive, pktInfo);
return;
}
else
{
WriteNull(streamName, property);
return;
}
}
}
2016-07-29 13:02:49 -07:00
/// <summary>
/// Serializes an PSObject whose baseobject is of primitive type
/// and which has notes.
/// </summary>
/// <param name="source">
/// source from which notes are written
/// </param>
/// <param name="primitive">
/// primitive object which is written as base object. In most cases it
/// is same source.ImmediateBaseObject. When PSObject is serialized as string,
/// it can be different. <see cref="HandlePSObjectAsString"/> for more info.
/// </param>
/// <param name="hasModifiedTypesCollection"></param>
/// <param name="toStringValue"></param>
/// <param name="pktInfo">
2017-01-16 13:31:14 -08:00
/// TypeSerializationInfo for the primitive.
2016-07-29 13:02:49 -07:00
/// </param>
/// <param name="streamName"></param>
/// <param name="property"></param>
/// <param name="depth"></param>
private void WritePrimitiveTypePSObjectWithNotes
(
PSObject source,
object primitive,
bool hasModifiedTypesCollection,
string toStringValue,
TypeSerializationInfo pktInfo,
string streamName,
string property,
int depth
)
{
Dbg.Assert(source != null, "caller should validate the parameter");
Dbg.Assert(pktInfo != null, "Caller should validate pktInfo != null");
2016-07-29 13:02:49 -07:00
string refId = _objectRefIdHandler.SetRefId(source);
WriteStartOfPSObject(
source,
streamName,
property,
refId,
hasModifiedTypesCollection, // preserve TypeNames information if different from the primitive type
toStringValue); // preserve ToString information only if got it from deserialization or overridden by PSObject
// (example where preservation of TypeNames and ToString is needed: enums serialized as ints, help string with custom type names (HelpInfoShort))
2016-07-29 13:02:49 -07:00
if (pktInfo != null)
{
WriteOnePrimitiveKnownType(this, streamName, null, primitive, pktInfo);
}
2016-07-29 13:02:49 -07:00
// serialize only instance properties - members from type table are
// always going to be available for known primitive types
SerializeInstanceProperties(source, depth);
2016-07-29 13:02:49 -07:00
_writer.WriteEndElement();
}
2016-07-29 13:02:49 -07:00
private void HandleComplexTypePSObject
(
object source,
string streamName,
string property,
int depth
)
{
Dbg.Assert(source != null, "caller should validate the parameter");
PSObject mshSource = PSObject.AsPSObject(source);
2016-07-29 13:02:49 -07:00
// Figure out what kind of object we are dealing with
bool isErrorRecord = false;
bool isInformationalRecord = false;
bool isEnum = false;
bool isPSObject = false;
bool isCimInstance = false;
if (!mshSource.ImmediateBaseObjectIsEmpty)
2016-07-29 13:02:49 -07:00
{
do // false loop
{
CimInstance cimInstance = mshSource.ImmediateBaseObject as CimInstance;
if (cimInstance != null)
{
isCimInstance = true;
break;
}
2016-07-29 13:02:49 -07:00
ErrorRecord errorRecord = mshSource.ImmediateBaseObject as ErrorRecord;
if (errorRecord != null)
{
errorRecord.ToPSObjectForRemoting(mshSource);
isErrorRecord = true;
break;
}
2016-07-29 13:02:49 -07:00
InformationalRecord informationalRecord = mshSource.ImmediateBaseObject as InformationalRecord;
if (informationalRecord != null)
{
informationalRecord.ToPSObjectForRemoting(mshSource);
isInformationalRecord = true;
break;
}
2016-07-29 13:02:49 -07:00
isEnum = mshSource.ImmediateBaseObject is Enum;
isPSObject = mshSource.ImmediateBaseObject is PSObject;
} while (false);
}
2016-07-29 13:02:49 -07:00
bool writeToString = true;
if (mshSource.ToStringFromDeserialization == null) // continue to write ToString from deserialized objects, but...
2016-07-29 13:02:49 -07:00
{
if (mshSource.ImmediateBaseObjectIsEmpty) // ... don't write ToString for property bags
2016-07-29 13:02:49 -07:00
{
writeToString = false;
}
}
2016-07-29 13:02:49 -07:00
string refId = _objectRefIdHandler.SetRefId(source);
WriteStartOfPSObject(
mshSource,
streamName,
property,
refId,
true, // always write TypeNames for complex objects
writeToString ? SerializationUtilities.GetToString(mshSource) : null);
PSMemberInfoInternalCollection<PSPropertyInfo> specificPropertiesToSerialize = SerializationUtilities.GetSpecificPropertiesToSerialize(mshSource, AllPropertiesCollection, _typeTable);
2016-07-29 13:02:49 -07:00
if (isEnum)
{
object baseObject = mshSource.ImmediateBaseObject;
WriteOneObject(System.Convert.ChangeType(baseObject, Enum.GetUnderlyingType(baseObject.GetType()), System.Globalization.CultureInfo.InvariantCulture), null, null, depth);
}
else if (isPSObject)
{
WriteOneObject(mshSource.ImmediateBaseObject, null, null, depth);
}
else if (isErrorRecord || isInformationalRecord)
{
// nothing to do
}
else
{
WritePSObjectProperties(mshSource, depth, specificPropertiesToSerialize);
}
2016-07-29 13:02:49 -07:00
if (isCimInstance)
{
CimInstance cimInstance = mshSource.ImmediateBaseObject as CimInstance;
PrepareCimInstanceForSerialization(mshSource, cimInstance);
}
2016-07-29 13:02:49 -07:00
SerializeExtendedProperties(mshSource, depth, specificPropertiesToSerialize);
2016-07-29 13:02:49 -07:00
_writer.WriteEndElement();
}
private static readonly Lazy<CimSerializer> s_cimSerializer = new Lazy<CimSerializer>(CimSerializer.Create);
2016-07-29 13:02:49 -07:00
private void PrepareCimInstanceForSerialization(PSObject psObject, CimInstance cimInstance)
{
Queue<CimClassSerializationId> serializedClasses = new Queue<CimClassSerializationId>();
2017-01-16 13:31:14 -08:00
//
2016-07-29 13:02:49 -07:00
// CREATE SERIALIZED FORM OF THE CLASS METADATA
//
ArrayList psoClasses = new ArrayList();
2016-07-29 13:02:49 -07:00
for (CimClass cimClass = cimInstance.CimClass; cimClass != null; cimClass = cimClass.CimSuperClass)
{
PSObject psoClass = new PSObject();
psoClass.TypeNames.Clear();
psoClasses.Add(psoClass);
psoClass.Properties.Add(new PSNoteProperty(InternalDeserializer.CimClassNameProperty, cimClass.CimSystemProperties.ClassName));
psoClass.Properties.Add(new PSNoteProperty(InternalDeserializer.CimNamespaceProperty, cimClass.CimSystemProperties.Namespace));
psoClass.Properties.Add(new PSNoteProperty(InternalDeserializer.CimServerNameProperty, cimClass.CimSystemProperties.ServerName));
psoClass.Properties.Add(new PSNoteProperty(InternalDeserializer.CimHashCodeProperty, cimClass.GetHashCode()));
CimClassSerializationId cimClassSerializationId = new CimClassSerializationId(
cimClass.CimSystemProperties.ClassName,
cimClass.CimSystemProperties.Namespace,
cimClass.CimSystemProperties.ServerName,
cimClass.GetHashCode());
if (_context.cimClassSerializationIdCache.DoesDeserializerAlreadyHaveCimClass(cimClassSerializationId))
{
break;
}
serializedClasses.Enqueue(cimClassSerializationId);
byte[] miXmlBytes = s_cimSerializer.Value.Serialize(cimClass, ClassSerializationOptions.None);
string miXmlString = Encoding.Unicode.GetString(miXmlBytes, 0, miXmlBytes.Length);
psoClass.Properties.Add(new PSNoteProperty(InternalDeserializer.CimMiXmlProperty, miXmlString));
}
2016-07-29 13:02:49 -07:00
psoClasses.Reverse();
//
// UPDATE CLASSDECL CACHE
//
foreach (CimClassSerializationId serializedClassId in serializedClasses)
{
_context.cimClassSerializationIdCache.AddClassToCache(serializedClassId);
}
//
// ATTACH CLASS METADATA TO THE OBJECT BEING SERIALIZED
//
PSPropertyInfo classMetadataProperty = psObject.Properties[InternalDeserializer.CimClassMetadataProperty];
if (classMetadataProperty != null)
{
classMetadataProperty.Value = psoClasses;
}
else
{
PSNoteProperty classMetadataNote = new PSNoteProperty(
InternalDeserializer.CimClassMetadataProperty,
psoClasses);
classMetadataNote.IsHidden = true;
psObject.Properties.Add(classMetadataNote);
}
// ATTACH INSTANCE METADATA TO THE OBJECT BEING SERIALIZED
List<string> namesOfModifiedProperties = cimInstance
.CimInstanceProperties
.Where(p => p.IsValueModified)
.Select(p => p.Name)
.ToList();
if (namesOfModifiedProperties.Count != 0)
{
PSObject instanceMetadata = new PSObject();
PSPropertyInfo instanceMetadataProperty = psObject.Properties[InternalDeserializer.CimInstanceMetadataProperty];
if (instanceMetadataProperty != null)
{
instanceMetadataProperty.Value = instanceMetadata;
}
else
{
PSNoteProperty instanceMetadataNote = new PSNoteProperty(InternalDeserializer.CimInstanceMetadataProperty, instanceMetadata);
instanceMetadataNote.IsHidden = true;
psObject.Properties.Add(instanceMetadataNote);
}
instanceMetadata.InternalTypeNames = ConsolidatedString.Empty;
instanceMetadata.Properties.Add(
new PSNoteProperty(
InternalDeserializer.CimModifiedProperties,
string.Join(" ", namesOfModifiedProperties)));
}
}
/// <summary>
/// Writes start element, attributes and typeNames for PSObject.
/// </summary>
/// <param name="mshObject"></param>
/// <param name="streamName"></param>
/// <param name="property"></param>
/// <param name="refId"></param>
/// <param name="writeTypeNames">If true, TypeName information is written, else not.</param>
/// <param name="toStringValue">If not null then ToString information is written.</param>
2016-07-29 13:02:49 -07:00
private void WriteStartOfPSObject
(
PSObject mshObject,
string streamName,
string property,
string refId,
bool writeTypeNames,
string toStringValue
)
{
Dbg.Assert(mshObject != null, "caller should validate the parameter");
// Write PSObject start element.
2016-07-29 13:02:49 -07:00
WriteStartElement(SerializationStrings.PSObjectTag);
if (streamName != null)
{
WriteAttribute(SerializationStrings.StreamNameAttribute, streamName);
}
if (property != null)
{
WriteNameAttribute(property);
}
if (refId != null)
{
WriteAttribute(SerializationStrings.ReferenceIdAttribute, refId);
}
if (writeTypeNames)
{
// Write TypeNames
2016-07-29 13:02:49 -07:00
ConsolidatedString typeNames = mshObject.InternalTypeNames;
if (typeNames.Count > 0)
{
string typeNameHierarchyReferenceId = _typeRefIdHandler.GetRefId(typeNames);
if (typeNameHierarchyReferenceId == null)
2016-07-29 13:02:49 -07:00
{
WriteStartElement(SerializationStrings.TypeNamesTag);
// Create a new refId and write it as attribute
2016-07-29 13:02:49 -07:00
string tnRefId = _typeRefIdHandler.SetRefId(typeNames);
Dbg.Assert(tnRefId != null, "SetRefId should always succeed for strings");
WriteAttribute(SerializationStrings.ReferenceIdAttribute, tnRefId);
foreach (string type in typeNames)
{
WriteEncodedElementString(SerializationStrings.TypeNamesItemTag, type);
}
2016-07-29 13:02:49 -07:00
_writer.WriteEndElement();
}
else
{
WriteStartElement(SerializationStrings.TypeNamesReferenceTag);
WriteAttribute(SerializationStrings.ReferenceIdAttribute, typeNameHierarchyReferenceId);
_writer.WriteEndElement();
}
}
}
if (toStringValue != null)
{
WriteEncodedElementString(SerializationStrings.ToStringElementTag, toStringValue);
}
}
#region membersets
/// <summary>
2017-01-16 13:31:14 -08:00
/// Returns true if PSObject has notes.
2016-07-29 13:02:49 -07:00
/// </summary>
/// <param name="source"></param>
/// <returns>
/// </returns>
private static bool PSObjectHasNotes(PSObject source)
{
Dbg.Assert(source != null, "Caller should validate the parameter");
if (source.InstanceMembers != null && source.InstanceMembers.Count > 0)
{
return true;
}
2016-07-29 13:02:49 -07:00
return false;
}
private bool? _canUseDefaultRunspaceInThreadSafeManner;
private bool CanUseDefaultRunspaceInThreadSafeManner
{
get
{
if (!_canUseDefaultRunspaceInThreadSafeManner.HasValue)
{
_canUseDefaultRunspaceInThreadSafeManner = Runspace.CanUseDefaultRunspace;
}
return _canUseDefaultRunspaceInThreadSafeManner.Value;
}
}
/// <summary>
2017-01-16 13:31:14 -08:00
/// Serialize member set. This method serializes without writing
2016-07-29 13:02:49 -07:00
/// enclosing tags and attributes.
/// </summary>
/// <param name="me">
/// enumerable containing members
/// </param>
/// <param name="depth"></param>
/// <param name="writeEnclosingMemberSetElementTag">
/// if this is true, write an enclosing "<memberset></memberset>" tag.
/// </param>
/// <returns></returns>
private void WriteMemberInfoCollection
(
IEnumerable<PSMemberInfo> me,
int depth,
bool writeEnclosingMemberSetElementTag
)
{
Dbg.Assert(me != null, "caller should validate the parameter");
bool enclosingTagWritten = false;
foreach (PSMemberInfo info in me)
{
if (!info.ShouldSerialize)
{
continue;
}
2016-07-29 13:02:49 -07:00
int depthOfMember = info.IsInstance ? depth : depth - 1;
if (info.MemberType == (info.MemberType & PSMemberTypes.Properties))
{
bool gotValue;
object value = SerializationUtilities.GetPropertyValueInThreadSafeManner((PSPropertyInfo)info, this.CanUseDefaultRunspaceInThreadSafeManner, out gotValue);
if (gotValue)
{
if (writeEnclosingMemberSetElementTag && !enclosingTagWritten)
{
enclosingTagWritten = true;
WriteStartElement(SerializationStrings.MemberSet);
}
WriteOneObject(value, null, info.Name, depthOfMember);
}
}
else if (info.MemberType == PSMemberTypes.MemberSet)
{
if (writeEnclosingMemberSetElementTag && !enclosingTagWritten)
{
enclosingTagWritten = true;
WriteStartElement(SerializationStrings.MemberSet);
}
2016-07-29 13:02:49 -07:00
WriteMemberSet((PSMemberSet)info, depthOfMember);
}
}
2016-07-29 13:02:49 -07:00
if (enclosingTagWritten)
{
_writer.WriteEndElement();
}
}
/// <summary>
/// Serializes MemberSet.
/// </summary>
private void WriteMemberSet
(
PSMemberSet set,
int depth
)
{
Dbg.Assert(set != null, "Caller should validate the parameter");
if (!set.ShouldSerialize)
{
return;
}
2016-07-29 13:02:49 -07:00
WriteStartElement(SerializationStrings.MemberSet);
WriteNameAttribute(set.Name);
WriteMemberInfoCollection(set.Members, depth, false);
_writer.WriteEndElement();
}
#endregion membersets
#region properties
/// <summary>
/// Serializes properties of PSObject.
2016-07-29 13:02:49 -07:00
/// </summary>
private void WritePSObjectProperties
(
PSObject source,
int depth,
IEnumerable<PSPropertyInfo> specificPropertiesToSerialize
)
{
Dbg.Assert(source != null, "caller should validate the information");
// Depth available for each property is one less
2016-07-29 13:02:49 -07:00
--depth;
Dbg.Assert(depth >= 0, "depth should be greater or equal to zero");
if (specificPropertiesToSerialize != null)
{
SerializeProperties(specificPropertiesToSerialize, SerializationStrings.AdapterProperties, depth);
}
else
{
if (source.ShouldSerializeAdapter())
{
IEnumerable<PSPropertyInfo> adapterCollection = null;
adapterCollection = source.GetAdaptedProperties();
if (adapterCollection != null)
{
SerializeProperties(adapterCollection, SerializationStrings.AdapterProperties, depth);
}
}
}
}
private void SerializeInstanceProperties
(
PSObject source,
int depth
)
{
// Serialize instanceMembers
2016-07-29 13:02:49 -07:00
Dbg.Assert(source != null, "caller should validate the information");
PSMemberInfoCollection<PSMemberInfo> instanceMembers = source.InstanceMembers;
if (instanceMembers != null)
{
WriteMemberInfoCollection(instanceMembers, depth, true);
}
}
private Collection<CollectionEntry<PSMemberInfo>> _extendedMembersCollection;
2016-07-29 13:02:49 -07:00
private Collection<CollectionEntry<PSMemberInfo>> ExtendedMembersCollection
{
2019-04-24 19:27:22 +05:00
get
{
return _extendedMembersCollection ??=
PSObject.GetMemberCollection(PSMemberViewTypes.Extended, _typeTable);
2016-07-29 13:02:49 -07:00
}
}
private Collection<CollectionEntry<PSPropertyInfo>> _allPropertiesCollection;
2016-07-29 13:02:49 -07:00
private Collection<CollectionEntry<PSPropertyInfo>> AllPropertiesCollection
{
2019-04-24 19:27:22 +05:00
get
{
return _allPropertiesCollection ??= PSObject.GetPropertyCollection(PSMemberViewTypes.All, _typeTable);
2016-07-29 13:02:49 -07:00
}
}
private void SerializeExtendedProperties
(
PSObject source,
int depth,
IEnumerable<PSPropertyInfo> specificPropertiesToSerialize
)
{
Dbg.Assert(source != null, "caller should validate the information");
IEnumerable<PSMemberInfo> extendedMembersEnumerable = null;
if (specificPropertiesToSerialize == null)
2016-07-29 13:02:49 -07:00
{
// Get only extended members including hidden members from the psobect source.
PSMemberInfoIntegratingCollection<PSMemberInfo> membersToSearch =
new PSMemberInfoIntegratingCollection<PSMemberInfo>(source, ExtendedMembersCollection);
extendedMembersEnumerable = membersToSearch.Match(
"*",
PSMemberTypes.Properties | PSMemberTypes.PropertySet | PSMemberTypes.MemberSet,
MshMemberMatchOptions.IncludeHidden | MshMemberMatchOptions.OnlySerializable);
}
else
{
List<PSMemberInfo> extendedMembersList = new List<PSMemberInfo>(source.InstanceMembers);
extendedMembersEnumerable = extendedMembersList;
foreach (PSMemberInfo member in specificPropertiesToSerialize)
{
if (member.IsInstance)
{
continue;
}
if (member is PSProperty)
{
continue;
}
extendedMembersList.Add(member);
}
}
if (extendedMembersEnumerable != null)
{
WriteMemberInfoCollection(extendedMembersEnumerable, depth, true);
}
}
/// <summary>
/// Serializes properties from collection.
2016-07-29 13:02:49 -07:00
/// </summary>
/// <param name="propertyCollection">
/// Collection of properties to serialize
/// </param>
/// <param name="name">
/// Name for enclosing element tag
/// </param>
/// <param name="depth">
2017-01-16 13:31:14 -08:00
/// depth to which each property should be
2016-07-29 13:02:49 -07:00
/// serialized
/// </param>
private void SerializeProperties
(
IEnumerable<PSPropertyInfo> propertyCollection,
string name,
int depth
)
{
Dbg.Assert(propertyCollection != null, "caller should validate the parameter");
bool startElementWritten = false;
foreach (PSMemberInfo info in propertyCollection)
{
if (!(info is PSProperty prop))
2016-07-29 13:02:49 -07:00
{
continue;
}
if (!startElementWritten)
{
WriteStartElement(name);
startElementWritten = true;
}
bool success;
object value = SerializationUtilities.GetPropertyValueInThreadSafeManner(prop, this.CanUseDefaultRunspaceInThreadSafeManner, out success);
if (success)
{
WriteOneObject(value, null, prop.Name, depth);
}
}
if (startElementWritten)
{
_writer.WriteEndElement();
}
}
#endregion base properties
#endregion WritePSObject
#region enumerable and dictionary
/// <summary>
/// Serializes IEnumerable.
2016-07-29 13:02:49 -07:00
/// </summary>
/// <param name="enumerable">
/// enumerable which is serialized
/// </param>
/// <param name="tag">
/// </param>
/// <param name="depth"></param>
private void WriteEnumerable
(
IEnumerable enumerable,
string tag,
int depth
)
{
Dbg.Assert(enumerable != null, "caller should validate the parameter");
Dbg.Assert(!string.IsNullOrEmpty(tag), "caller should validate the parameter");
// Start element
2016-07-29 13:02:49 -07:00
WriteStartElement(tag);
IEnumerator enumerator = null;
try
{
enumerator = enumerable.GetEnumerator();
try
{
enumerator.Reset();
}
catch (System.NotSupportedException)
{
// ignore exceptions thrown when the enumerator doesn't support Reset() method as in win8:948569
2016-07-29 13:02:49 -07:00
}
}
catch (Exception exception)
{
// Catch-all OK. This is a third-party call-out.
PSEtwLog.LogAnalyticWarning(
PSEventId.Serializer_EnumerationFailed, PSOpcode.Exception, PSTask.Serialization,
PSKeyword.Serializer | PSKeyword.UseAlwaysAnalytic,
enumerable.GetType().AssemblyQualifiedName,
exception.ToString());
enumerator = null;
}
// AD has incorrect implementation of IEnumerable where they returned null
// for GetEnumerator instead of empty enumerator
2016-07-29 13:02:49 -07:00
if (enumerator != null)
{
while (true)
{
object item = null;
try
{
if (!enumerator.MoveNext())
{
break;
}
else
{
item = enumerator.Current;
}
}
catch (Exception exception)
{
// Catch-all OK. This is a third-party call-out.
PSEtwLog.LogAnalyticWarning(
PSEventId.Serializer_EnumerationFailed, PSOpcode.Exception, PSTask.Serialization,
PSKeyword.Serializer | PSKeyword.UseAlwaysAnalytic,
enumerable.GetType().AssemblyQualifiedName,
exception.ToString());
break;
}
2016-07-29 13:02:49 -07:00
WriteOneObject(item, null, null, depth);
}
}
// End element
2016-07-29 13:02:49 -07:00
_writer.WriteEndElement();
}
/// <summary>
/// Serializes IDictionary.
2016-07-29 13:02:49 -07:00
/// </summary>
/// <param name="dictionary">Dictionary which is serialized.</param>
2016-07-29 13:02:49 -07:00
/// <param name="tag"></param>
/// <param name="depth"></param>
private void WriteDictionary
(
IDictionary dictionary,
string tag,
int depth
)
{
Dbg.Assert(dictionary != null, "caller should validate the parameter");
Dbg.Assert(!string.IsNullOrEmpty(tag), "caller should validate the parameter");
// Start element
2016-07-29 13:02:49 -07:00
WriteStartElement(tag);
IDictionaryEnumerator dictionaryEnum = null;
try
{
dictionaryEnum = dictionary.GetEnumerator();
}
catch (Exception exception) // ignore non-severe exceptions
{
// Catch-all OK. This is a third-party call-out.
PSEtwLog.LogAnalyticWarning(
PSEventId.Serializer_EnumerationFailed, PSOpcode.Exception, PSTask.Serialization,
PSKeyword.Serializer | PSKeyword.UseAlwaysAnalytic,
dictionary.GetType().AssemblyQualifiedName,
exception.ToString());
}
if (dictionaryEnum != null)
{
while (true)
{
object key = null;
object value = null;
try
{
if (!dictionaryEnum.MoveNext())
{
break;
}
else
{
key = dictionaryEnum.Key;
value = dictionaryEnum.Value;
}
}
catch (Exception exception)
{
// Catch-all OK. This is a third-party call-out.
PSEtwLog.LogAnalyticWarning(
PSEventId.Serializer_EnumerationFailed, PSOpcode.Exception, PSTask.Serialization,
PSKeyword.Serializer | PSKeyword.UseAlwaysAnalytic,
dictionary.GetType().AssemblyQualifiedName,
exception.ToString());
break;
}
Dbg.Assert(key != null, "Dictionary keys should never be null");
if (key == null) break;
2016-07-29 13:02:49 -07:00
WriteStartElement(SerializationStrings.DictionaryEntryTag);
WriteOneObject(key, null, SerializationStrings.DictionaryKey, depth);
WriteOneObject(value, null, SerializationStrings.DictionaryValue, depth);
_writer.WriteEndElement();
}
}
// End element
2016-07-29 13:02:49 -07:00
_writer.WriteEndElement();
}
#endregion enumerable and dictionary
#region serialize as string
private void HandlePSObjectAsString(
PSObject source,
string streamName,
string property,
int depth)
{
Dbg.Assert(source != null, "caller should validate the information");
string value = GetSerializationString(source);
TypeSerializationInfo pktInfo = null;
if (value != null)
{
pktInfo = KnownTypes.GetTypeSerializationInfo(value.GetType());
}
WritePrimitiveTypePSObject(source, value, pktInfo, streamName, property, depth);
}
/// <summary>
/// Gets the string from PSObject using the information from
2017-01-16 13:31:14 -08:00
/// types.ps1xml.
/// This string is used for serializing the PSObject at depth 0
/// or when pso.SerializationMethod == SerializationMethod.String.
/// </summary>
/// <param name="source">
/// PSObject to be converted to string
/// </param>
/// <returns>
/// string value to use for serializing this PSObject.
/// </returns>
private string GetSerializationString(PSObject source)
{
Dbg.Assert(source != null, "caller should have validated the information");
PSPropertyInfo serializationProperty = null;
try
{
serializationProperty = source.GetStringSerializationSource(_typeTable);
}
catch (ExtendedTypeSystemException e)
{
PSEtwLog.LogAnalyticWarning(
PSEventId.Serializer_ToStringFailed, PSOpcode.Exception, PSTask.Serialization,
PSKeyword.Serializer | PSKeyword.UseAlwaysAnalytic,
source.GetType().AssemblyQualifiedName,
e.InnerException != null ? e.InnerException.ToString() : e.ToString());
}
string result = null;
if (serializationProperty != null)
{
bool success;
object val = SerializationUtilities.GetPropertyValueInThreadSafeManner(serializationProperty, this.CanUseDefaultRunspaceInThreadSafeManner, out success);
if (success && (val != null))
{
result = SerializationUtilities.GetToString(val);
}
}
else
{
result = SerializationUtilities.GetToString(source);
}
return result;
}
/// <summary>
/// Reads the information the PSObject
/// and returns true if this object should be serialized as
/// string.
/// </summary>
/// <param name="source">PSObject to be serialized.</param>
/// <returns>True if the object needs to be serialized as a string.</returns>
private bool SerializeAsString(PSObject source)
{
SerializationMethod method = source.GetSerializationMethod(_typeTable);
if (method == SerializationMethod.String)
{
PSEtwLog.LogAnalyticVerbose(
PSEventId.Serializer_ModeOverride, PSOpcode.SerializationSettings, PSTask.Serialization,
PSKeyword.Serializer | PSKeyword.UseAlwaysAnalytic,
source.InternalTypeNames.Key,
(UInt32)(SerializationMethod.String));
return true;
}
else
{
return false;
}
}
2016-07-29 13:02:49 -07:00
#endregion serialize as string
2016-07-29 13:02:49 -07:00
/// <summary>
/// Compute the serialization depth for an PSObject instance subtree.
2016-07-29 13:02:49 -07:00
/// </summary>
/// <param name="source">PSObject whose serialization depth has to be computed.</param>
/// <param name="depth">Current depth.</param>
2016-07-29 13:02:49 -07:00
/// <returns></returns>
private int GetDepthOfSerialization(object source, int depth)
{
Dbg.Assert(source != null, "Caller should verify source != null");
2016-07-29 13:02:49 -07:00
PSObject pso = PSObject.AsPSObject(source);
if (pso == null)
2016-07-29 13:02:49 -07:00
{
return depth;
}
2016-07-29 13:02:49 -07:00
if (pso.BaseObject is CimInstance)
{
return 1;
}
2016-07-29 13:02:49 -07:00
if (pso.BaseObject is PSCredential)
{
return 1;
}
2016-07-29 13:02:49 -07:00
if (pso.BaseObject is PSSenderInfo)
{
return 4;
}
2016-07-29 13:02:49 -07:00
if (pso.BaseObject is SwitchParameter)
{
return 1;
}
if ((_context.options & SerializationOptions.UseDepthFromTypes) != 0)
2016-07-29 13:02:49 -07:00
{
// get the depth from the PSObject
// NOTE: we assume that the depth out of the PSObject is > 0
// else we consider it not set in types.ps1xml
int typesPs1xmlDepth = pso.GetSerializationDepth(_typeTable);
if (typesPs1xmlDepth > 0)
{
if (typesPs1xmlDepth != depth)
{
PSEtwLog.LogAnalyticVerbose(
PSEventId.Serializer_DepthOverride, PSOpcode.SerializationSettings, PSTask.Serialization,
PSKeyword.Serializer | PSKeyword.UseAlwaysAnalytic,
pso.InternalTypeNames.Key, depth, typesPs1xmlDepth, _depthBelowTopLevel);
2016-07-29 13:02:49 -07:00
return typesPs1xmlDepth;
}
}
}
if ((_context.options & SerializationOptions.PreserveSerializationSettingOfOriginal) != 0)
2016-07-29 13:02:49 -07:00
{
if ((pso.IsDeserialized) && (depth <= 0))
2016-07-29 13:02:49 -07:00
{
return 1;
}
}
2016-07-29 13:02:49 -07:00
return depth;
}
2016-07-29 13:02:49 -07:00
/// <summary>
/// Writes null.
2016-07-29 13:02:49 -07:00
/// </summary>
/// <param name="streamName"></param>
/// <param name="property"></param>
private void WriteNull(string streamName, string property)
{
WriteStartElement(SerializationStrings.NilTag);
2016-07-29 13:02:49 -07:00
if (streamName != null)
{
WriteAttribute(SerializationStrings.StreamNameAttribute, streamName);
}
2016-07-29 13:02:49 -07:00
if (property != null)
{
WriteNameAttribute(property);
}
2016-07-29 13:02:49 -07:00
_writer.WriteEndElement();
}
2016-07-29 13:02:49 -07:00
#region known type serialization
2016-07-29 13:02:49 -07:00
/// <summary>
/// Writes raw string as item or property in Monad namespace.
2016-07-29 13:02:49 -07:00
/// </summary>
/// <param name="serializer">The serializer to which the object is serialized.</param>
/// <param name="streamName">Name of the stream to write. Do not write if null.</param>
/// <param name="property">Name of property. Pass null for item.</param>
/// <param name="raw">String to write.</param>
/// <param name="entry">Serialization information.</param>
2016-07-29 13:02:49 -07:00
private static void WriteRawString
(
InternalSerializer serializer,
string streamName,
string property,
string raw,
TypeSerializationInfo entry
)
{
Dbg.Assert(serializer != null, "caller should have validated the information");
Dbg.Assert(raw != null, "caller should have validated the information");
Dbg.Assert(entry != null, "caller should have validated the information");
2016-07-29 13:02:49 -07:00
if (property != null)
{
serializer.WriteStartElement(entry.PropertyTag);
serializer.WriteNameAttribute(property);
}
else
{
serializer.WriteStartElement(entry.ItemTag);
}
2016-07-29 13:02:49 -07:00
if (streamName != null)
{
serializer.WriteAttribute(SerializationStrings.StreamNameAttribute, streamName);
}
2016-07-29 13:02:49 -07:00
serializer._writer.WriteRaw(raw);
serializer._writer.WriteEndElement();
}
2016-07-29 13:02:49 -07:00
/// <summary>
/// Writes an item or property in Monad namespace.
2016-07-29 13:02:49 -07:00
/// </summary>
/// <param name="serializer">The serializer to which the object is serialized.</param>
/// <param name="streamName"></param>
/// <param name="property">Name of property. Pass null for item.</param>
/// <param name="source">Object to be written.</param>
/// <param name="entry">Serialization information about source.</param>
2016-07-29 13:02:49 -07:00
private static void WriteOnePrimitiveKnownType
(
InternalSerializer serializer,
string streamName,
string property,
object source,
TypeSerializationInfo entry
)
{
Dbg.Assert(serializer != null, "caller should have validated the information");
Dbg.Assert(source != null, "caller should have validated the information");
Dbg.Assert(entry != null, "caller should have validated the information");
if (entry.Serializer == null)
2016-07-29 13:02:49 -07:00
{
// we are not using GetToString, because we assume that
// ToString() for primitive types never throws
string value = Convert.ToString(source, CultureInfo.InvariantCulture);
Dbg.Assert(value != null, "ToString shouldn't return null for primitive types");
WriteRawString(serializer, streamName, property, value, entry);
}
else
{
entry.Serializer(serializer, streamName, property, source, entry);
}
}
2016-07-29 13:02:49 -07:00
/// <summary>
/// Writes DateTime as item or property.
2016-07-29 13:02:49 -07:00
/// </summary>
/// <param name="serializer">The serializer to which the object is serialized.</param>
/// <param name="streamName"></param>
/// <param name="property">Name of property. pass null for item.</param>
/// <param name="source">DateTime to write.</param>
/// <param name="entry">Serialization information about source.</param>
2016-07-29 13:02:49 -07:00
internal static void WriteDateTime(InternalSerializer serializer, string streamName, string property, object source, TypeSerializationInfo entry)
{
Dbg.Assert(serializer != null, "caller should have validated the information");
Dbg.Assert(source != null, "caller should have validated the information");
Dbg.Assert(entry != null, "caller should have validated the information");
WriteRawString(serializer, streamName, property, XmlConvert.ToString((DateTime)source, XmlDateTimeSerializationMode.RoundtripKind), entry);
2016-07-29 13:02:49 -07:00
}
2016-07-29 13:02:49 -07:00
/// <summary>
/// Writes Version.
2016-07-29 13:02:49 -07:00
/// </summary>
/// <param name="serializer">The serializer to which the object is serialized.</param>
/// <param name="streamName"></param>
/// <param name="property">Name of property. pass null for item.</param>
/// <param name="source">Version to write.</param>
/// <param name="entry">Serialization information about source.</param>
2016-07-29 13:02:49 -07:00
internal static void WriteVersion(InternalSerializer serializer, string streamName, string property, object source, TypeSerializationInfo entry)
{
Dbg.Assert(serializer != null, "caller should have validated the information");
Dbg.Assert(source != null, "caller should have validated the information");
Dbg.Assert(source is Version, "Caller should verify that typeof(source) is Version");
Dbg.Assert(entry != null, "caller should have validated the information");
2016-07-29 13:02:49 -07:00
WriteRawString(serializer, streamName, property, Convert.ToString(source, CultureInfo.InvariantCulture), entry);
}
/// <summary>
/// Writes SemanticVersion.
/// </summary>
/// <param name="serializer">The serializer to which the object is serialized.</param>
/// <param name="streamName"></param>
/// <param name="property">Name of property. pass null for item.</param>
/// <param name="source">Version to write.</param>
/// <param name="entry">Serialization information about source.</param>
internal static void WriteSemanticVersion(InternalSerializer serializer, string streamName, string property, object source, TypeSerializationInfo entry)
{
Dbg.Assert(serializer != null, "caller should have validated the information");
Dbg.Assert(source != null, "caller should have validated the information");
Dbg.Assert(source is SemanticVersion, "Caller should verify that typeof(source) is Version");
Dbg.Assert(entry != null, "caller should have validated the information");
WriteRawString(serializer, streamName, property, Convert.ToString(source, CultureInfo.InvariantCulture), entry);
}
2016-07-29 13:02:49 -07:00
/// <summary>
/// Serialize scriptblock as item or property.
2016-07-29 13:02:49 -07:00
/// </summary>
/// <param name="serializer">The serializer to which the object is serialized.</param>
/// <param name="streamName"></param>
/// <param name="property">Name of property. pass null for item.</param>
/// <param name="source">Scriptblock to write.</param>
/// <param name="entry">Serialization information about source.</param>
2016-07-29 13:02:49 -07:00
internal static void WriteScriptBlock(InternalSerializer serializer, string streamName, string property, object source, TypeSerializationInfo entry)
{
Dbg.Assert(serializer != null, "caller should have validated the information");
Dbg.Assert(source != null, "caller should have validated the information");
Dbg.Assert(source is ScriptBlock, "Caller should verify that typeof(source) is ScriptBlock");
Dbg.Assert(entry != null, "caller should have validated the information");
2016-07-29 13:02:49 -07:00
WriteEncodedString(serializer, streamName, property, Convert.ToString(source, CultureInfo.InvariantCulture), entry);
}
2016-07-29 13:02:49 -07:00
/// <summary>
/// Serialize URI as item or property.
2016-07-29 13:02:49 -07:00
/// </summary>
/// <param name="serializer">The serializer to which the object is serialized.</param>
/// <param name="streamName"></param>
/// <param name="property">Name of property. pass null for item.</param>
/// <param name="source">URI to write.</param>
/// <param name="entry">Serialization information about source.</param>
2016-07-29 13:02:49 -07:00
internal static void WriteUri(InternalSerializer serializer, string streamName, string property, object source, TypeSerializationInfo entry)
{
Dbg.Assert(serializer != null, "caller should have validated the information");
Dbg.Assert(source != null, "caller should have validated the information");
Dbg.Assert(source is Uri, "Caller should verify that typeof(source) is Uri");
Dbg.Assert(entry != null, "caller should have validated the information");
2016-07-29 13:02:49 -07:00
WriteEncodedString(serializer, streamName, property, Convert.ToString(source, CultureInfo.InvariantCulture), entry);
}
2016-07-29 13:02:49 -07:00
/// <summary>
/// Serialize string as item or property.
2016-07-29 13:02:49 -07:00
/// </summary>
/// <param name="serializer">The serializer to which the object is serialized.</param>
/// <param name="streamName"></param>
/// <param name="property">Name of property. pass null for item.</param>
/// <param name="source">String to write.</param>
/// <param name="entry">Serialization information about source.</param>
2016-07-29 13:02:49 -07:00
internal static void WriteEncodedString(InternalSerializer serializer, string streamName, string property, object source, TypeSerializationInfo entry)
{
Dbg.Assert(serializer != null, "caller should have validated the information");
Dbg.Assert(source != null, "caller should have validated the information");
Dbg.Assert(source is string, "caller should have validated the information");
Dbg.Assert(entry != null, "caller should have validated the information");
2016-07-29 13:02:49 -07:00
if (property != null)
{
serializer.WriteStartElement(entry.PropertyTag);
serializer.WriteNameAttribute(property);
}
else
{
serializer.WriteStartElement(entry.ItemTag);
}
2016-07-29 13:02:49 -07:00
if (streamName != null)
{
serializer.WriteAttribute(SerializationStrings.StreamNameAttribute, streamName);
}
// Note: We do not use WriteRaw for serializing string. WriteString
// does necessary escaping which may be needed for certain
// characters.
2016-07-29 13:02:49 -07:00
Dbg.Assert(source is string, "Caller should verify that typeof(source) is String");
string s = (string)source;
string encoded = EncodeString(s);
serializer._writer.WriteString(encoded);
2016-07-29 13:02:49 -07:00
serializer._writer.WriteEndElement();
}
2016-07-29 13:02:49 -07:00
/// <summary>
/// Writes Double as item or property.
2016-07-29 13:02:49 -07:00
/// </summary>
/// <param name="serializer">The serializer to which the object is serialized.</param>
/// <param name="streamName"></param>
/// <param name="property">Name of property. pass null for item.</param>
/// <param name="source">Double to write.</param>
/// <param name="entry">Serialization information about source.</param>
2016-07-29 13:02:49 -07:00
internal static void WriteDouble(InternalSerializer serializer, string streamName, string property, object source, TypeSerializationInfo entry)
{
Dbg.Assert(serializer != null, "caller should have validated the information");
Dbg.Assert(source != null, "caller should have validated the information");
Dbg.Assert(entry != null, "caller should have validated the information");
WriteRawString(serializer, streamName, property, XmlConvert.ToString((double)source), entry);
2016-07-29 13:02:49 -07:00
}
2016-07-29 13:02:49 -07:00
/// <summary>
/// Writes Char as item or property.
2016-07-29 13:02:49 -07:00
/// </summary>
/// <param name="serializer">The serializer to which the object is serialized.</param>
/// <param name="streamName"></param>
/// <param name="property">Name of property. pass null for item.</param>
/// <param name="source">Char to write.</param>
/// <param name="entry">Serialization information about source.</param>
2016-07-29 13:02:49 -07:00
internal static void WriteChar(InternalSerializer serializer, string streamName, string property, object source, TypeSerializationInfo entry)
{
Dbg.Assert(serializer != null, "caller should have validated the information");
Dbg.Assert(source != null, "caller should have validated the information");
Dbg.Assert(entry != null, "caller should have validated the information");
// Char is defined as unsigned short in schema
WriteRawString(serializer, streamName, property, XmlConvert.ToString((UInt16)(char)source), entry);
2016-07-29 13:02:49 -07:00
}
2016-07-29 13:02:49 -07:00
/// <summary>
/// Writes Boolean as item or property.
2016-07-29 13:02:49 -07:00
/// </summary>
/// <param name="serializer">The serializer to which the object is serialized.</param>
/// <param name="streamName"></param>
/// <param name="property">Name of property. pass null for item.</param>
/// <param name="source">Boolean to write.</param>
/// <param name="entry">Serialization information about source.</param>
2016-07-29 13:02:49 -07:00
internal static void WriteBoolean(InternalSerializer serializer, string streamName, string property, object source, TypeSerializationInfo entry)
{
Dbg.Assert(serializer != null, "caller should have validated the information");
Dbg.Assert(source != null, "caller should have validated the information");
Dbg.Assert(entry != null, "caller should have validated the information");
WriteRawString(serializer, streamName, property, XmlConvert.ToString((bool)source), entry);
2016-07-29 13:02:49 -07:00
}
2016-07-29 13:02:49 -07:00
/// <summary>
/// Writes Single as item or property.
2016-07-29 13:02:49 -07:00
/// </summary>
/// <param name="serializer">The serializer to which the object is serialized.</param>
/// <param name="streamName"></param>
/// <param name="property">Name of property. pass null for item.</param>
/// <param name="source">Single to write.</param>
/// <param name="entry">Serialization information about source.</param>
2016-07-29 13:02:49 -07:00
internal static void WriteSingle(InternalSerializer serializer, string streamName, string property, object source, TypeSerializationInfo entry)
{
Dbg.Assert(serializer != null, "caller should have validated the information");
Dbg.Assert(source != null, "caller should have validated the information");
Dbg.Assert(entry != null, "caller should have validated the information");
2016-07-29 13:02:49 -07:00
WriteRawString(serializer, streamName, property, XmlConvert.ToString((Single)source), entry);
}
2016-07-29 13:02:49 -07:00
/// <summary>
/// Writes TimeSpan as item or property.
2016-07-29 13:02:49 -07:00
/// </summary>
/// <param name="serializer">The serializer to which the object is serialized.</param>
/// <param name="streamName"></param>
/// <param name="property">Name of property. pass null for item.</param>
/// <param name="source">DateTime to write.</param>
/// <param name="entry">Serialization information about source.</param>
2016-07-29 13:02:49 -07:00
internal static void WriteTimeSpan(InternalSerializer serializer, string streamName, string property, object source, TypeSerializationInfo entry)
{
Dbg.Assert(serializer != null, "caller should have validated the information");
Dbg.Assert(source != null, "caller should have validated the information");
Dbg.Assert(entry != null, "caller should have validated the information");
2016-07-29 13:02:49 -07:00
WriteRawString(serializer, streamName, property, XmlConvert.ToString((TimeSpan)source), entry);
}
2016-07-29 13:02:49 -07:00
/// <summary>
/// Writes Single as item or property.
2016-07-29 13:02:49 -07:00
/// </summary>
/// <param name="serializer">The serializer to which the object is serialized.</param>
/// <param name="streamName"></param>
/// <param name="property">Name of property. pass null for item.</param>
/// <param name="source">Bytearray to write.</param>
/// <param name="entry">Serialization information about source.</param>
2016-07-29 13:02:49 -07:00
internal static void WriteByteArray(InternalSerializer serializer, string streamName, string property, object source, TypeSerializationInfo entry)
{
Dbg.Assert(serializer != null, "caller should have validated the information");
Dbg.Assert(source != null, "caller should have validated the information");
Dbg.Assert(entry != null, "caller should have validated the information");
2018-12-24 11:23:18 +05:00
byte[] bytes = (byte[])source;
2016-07-29 13:02:49 -07:00
if (property != null)
{
serializer.WriteStartElement(entry.PropertyTag);
serializer.WriteNameAttribute(property);
}
else
{
serializer.WriteStartElement(entry.ItemTag);
}
2016-07-29 13:02:49 -07:00
if (streamName != null)
{
serializer.WriteAttribute(SerializationStrings.StreamNameAttribute, streamName);
}
2016-07-29 13:02:49 -07:00
serializer._writer.WriteBase64(bytes, 0, bytes.Length);
serializer._writer.WriteEndElement();
}
2016-07-29 13:02:49 -07:00
internal static void WriteXmlDocument(InternalSerializer serializer, string streamName, string property, object source, TypeSerializationInfo entry)
{
Dbg.Assert(serializer != null, "caller should have validated the information");
Dbg.Assert(source != null, "caller should have validated the information");
Dbg.Assert(entry != null, "caller should have validated the information");
2016-07-29 13:02:49 -07:00
string xml = ((XmlDocument)source).OuterXml;
WriteEncodedString(serializer, streamName, property, xml, entry);
}
2016-07-29 13:02:49 -07:00
internal static void WriteProgressRecord(InternalSerializer serializer, string streamName, string property, object source, TypeSerializationInfo entry)
{
Dbg.Assert(serializer != null, "caller should have validated the information");
Dbg.Assert(source != null, "caller should have validated the information");
Dbg.Assert(entry != null, "caller should have validated the information");
2016-07-29 13:02:49 -07:00
ProgressRecord rec = (ProgressRecord)source;
serializer.WriteStartElement(entry.PropertyTag);
if (property != null)
{
serializer.WriteNameAttribute(property);
}
2016-07-29 13:02:49 -07:00
if (streamName != null)
{
serializer.WriteAttribute(SerializationStrings.StreamNameAttribute, streamName);
}
2016-07-29 13:02:49 -07:00
serializer.WriteEncodedElementString(SerializationStrings.ProgressRecordActivity, rec.Activity);
serializer.WriteEncodedElementString(SerializationStrings.ProgressRecordActivityId, rec.ActivityId.ToString(CultureInfo.InvariantCulture));
serializer.WriteOneObject(rec.CurrentOperation, null, null, 1);
serializer.WriteEncodedElementString(SerializationStrings.ProgressRecordParentActivityId, rec.ParentActivityId.ToString(CultureInfo.InvariantCulture));
serializer.WriteEncodedElementString(SerializationStrings.ProgressRecordPercentComplete, rec.PercentComplete.ToString(CultureInfo.InvariantCulture));
serializer.WriteEncodedElementString(SerializationStrings.ProgressRecordType, rec.RecordType.ToString());
serializer.WriteEncodedElementString(SerializationStrings.ProgressRecordSecondsRemaining, rec.SecondsRemaining.ToString(CultureInfo.InvariantCulture));
serializer.WriteEncodedElementString(SerializationStrings.ProgressRecordStatusDescription, rec.StatusDescription);
2016-07-29 13:02:49 -07:00
serializer._writer.WriteEndElement();
}
2016-07-29 13:02:49 -07:00
internal static void WriteSecureString(InternalSerializer serializer, string streamName, string property, object source, TypeSerializationInfo entry)
{
Dbg.Assert(serializer != null, "caller should have validated the information");
Dbg.Assert(source != null, "caller should have validated the information");
Dbg.Assert(entry != null, "caller should have validated the information");
2016-07-29 13:02:49 -07:00
serializer.HandleSecureString(source, streamName, property);
}
2016-07-29 13:02:49 -07:00
#endregion known type serialization
2016-07-29 13:02:49 -07:00
#region misc
2016-07-29 13:02:49 -07:00
/// <summary>
/// Writes start element in Monad namespace.
2016-07-29 13:02:49 -07:00
/// </summary>
/// <param name="elementTag">Tag of element.</param>
2016-07-29 13:02:49 -07:00
private void WriteStartElement(string elementTag)
{
Dbg.Assert(!string.IsNullOrEmpty(elementTag), "Caller should validate the parameter");
if ((_context.options & SerializationOptions.NoNamespace) == SerializationOptions.NoNamespace)
2016-07-29 13:02:49 -07:00
{
_writer.WriteStartElement(elementTag);
}
else
{
_writer.WriteStartElement(elementTag, SerializationStrings.MonadNamespace);
}
}
2016-07-29 13:02:49 -07:00
/// <summary>
/// Writes attribute in monad namespace.
2016-07-29 13:02:49 -07:00
/// </summary>
/// <param name="name">Name of attribute.</param>
/// <param name="value">Value of attribute.</param>
2016-07-29 13:02:49 -07:00
private void WriteAttribute(string name, string value)
{
Dbg.Assert(!string.IsNullOrEmpty(name), "Caller should validate the parameter");
Dbg.Assert(value != null, "Caller should validate the parameter");
_writer.WriteAttributeString(name, value);
}
2016-07-29 13:02:49 -07:00
private void WriteNameAttribute(string value)
{
Dbg.Assert(!string.IsNullOrEmpty(value), "Caller should validate the parameter");
WriteAttribute(
SerializationStrings.NameAttribute,
EncodeString(value));
}
2016-07-29 13:02:49 -07:00
/// <summary>
/// Encodes the string to escape characters which would make XmlWriter.WriteString throw an exception.
/// </summary>
/// <param name="s">String to encode.</param>
/// <returns>Encoded string.</returns>
2016-07-29 13:02:49 -07:00
/// <remarks>
/// Output from this method can be reverted using XmlConvert.DecodeName method
/// (or InternalDeserializer.DecodeString).
/// This method has been introduced to produce shorter output than XmlConvert.EncodeName
/// (which escapes everything that can't be part of an xml name - whitespace, punctuation).
2017-01-16 13:31:14 -08:00
///
2016-07-29 13:02:49 -07:00
/// This method has been split into 2 parts to optimize its performance:
2017-01-16 13:31:14 -08:00
/// 1) part1 (this method) checks if there are any encodable characters and
2016-07-29 13:02:49 -07:00
/// if there aren't it simply (and efficiently) returns the original string
2017-01-16 13:31:14 -08:00
/// 2) part2 (EncodeString(string, int)) picks up when part1 detects the first encodable
2016-07-29 13:02:49 -07:00
/// character. It avoids looking at the characters already verified by part1
2017-01-16 13:31:14 -08:00
/// and copies those already verified characters and then starts encoding
2016-07-29 13:02:49 -07:00
/// the rest of the string.
/// </remarks>
internal static string EncodeString(string s)
{
Dbg.Assert(s != null, "Caller should validate the parameter");
2016-07-29 13:02:49 -07:00
int slen = s.Length;
for (int i = 0; i < slen; ++i)
{
char c = s[i];
// A control character is in ranges 0x00-0x1F or 0x7F-0x9F
// The escape character is 0x5F ('_') if followed by an 'x'
// A surrogate character is in range 0xD800-0xDFFF
if (c <= 0x1F
|| (c >= 0x7F && c <= 0x9F)
|| (c >= 0xD800 && c <= 0xDFFF)
|| (c == 0x5F && (i + 1 < slen) &&
((s[i + 1] == 'x') || (s[i + 1] == 'X'))
))
{
return EncodeString(s, i);
}
}
// No encodable characters were found above - simply return the original string
return s;
}
2016-07-29 13:02:49 -07:00
private static readonly char[] s_hexlookup = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' };
2016-07-29 13:02:49 -07:00
/// <summary>
/// This is the real workhorse that encodes strings.
/// See <see cref="EncodeString(string)"/> for more information.
2016-07-29 13:02:49 -07:00
/// </summary>
/// <param name="s">String to encode.</param>
/// <param name="indexOfFirstEncodableCharacter">IndexOfFirstEncodableCharacter.</param>
/// <returns>Encoded string.</returns>
2016-07-29 13:02:49 -07:00
private static string EncodeString(string s, int indexOfFirstEncodableCharacter)
{
Dbg.Assert(s != null, "Caller should validate the 's' parameter");
Dbg.Assert(indexOfFirstEncodableCharacter >= 0, "Caller should verify validity of indexOfFirstEncodableCharacter");
Dbg.Assert(indexOfFirstEncodableCharacter < s.Length, "Caller should verify validity of indexOfFirstEncodableCharacter");
2016-07-29 13:02:49 -07:00
int slen = s.Length;
char[] result = new char[indexOfFirstEncodableCharacter + (slen - indexOfFirstEncodableCharacter) * 7];
2016-07-29 13:02:49 -07:00
s.CopyTo(0, result, 0, indexOfFirstEncodableCharacter);
int rlen = indexOfFirstEncodableCharacter;
2016-07-29 13:02:49 -07:00
for (int i = indexOfFirstEncodableCharacter; i < slen; ++i)
{
char c = s[i];
// A control character is in ranges 0x00-0x1F or 0x7F-0x9F
// The escape character is 0x5F ('_') if followed by an 'x'
// A surrogate character is in range 0xD800-0xDFFF
if (c > 0x1F
&& (c < 0x7F || c > 0x9F)
&& (c < 0xD800 || c > 0xDFFF)
&& (c != 0x5F || ((i + 1 >= slen) ||
((s[i + 1] != 'x') && (s[i + 1] != 'X'))
)))
{
result[rlen++] = c;
}
else if (c == 0x5F)
{
// Special case the escape character, encode _
result[rlen + 0] = '_';
result[rlen + 1] = 'x';
result[rlen + 2] = '0';
result[rlen + 3] = '0';
result[rlen + 4] = '5';
result[rlen + 5] = 'F';
result[rlen + 6] = '_';
rlen += 7;
}
else
{
// It is a control character or a unicode surrogate
result[rlen + 0] = '_';
result[rlen + 1] = 'x';
2016-07-29 13:02:49 -07:00
result[rlen + 2 + 3] = s_hexlookup[c & 0x0F];
c >>= 4;
result[rlen + 2 + 2] = s_hexlookup[c & 0x0F];
c >>= 4;
result[rlen + 2 + 1] = s_hexlookup[c & 0x0F];
c >>= 4;
result[rlen + 2 + 0] = s_hexlookup[c & 0x0F];
2016-07-29 13:02:49 -07:00
result[rlen + 6] = '_';
2016-07-29 13:02:49 -07:00
rlen += 7;
}
}
return new string(result, 0, rlen);
2016-07-29 13:02:49 -07:00
}
2016-07-29 13:02:49 -07:00
/// <summary>
/// Writes element string in monad namespace.
2016-07-29 13:02:49 -07:00
/// </summary>
/// <param name="name"></param>
/// <param name="value"></param>
private void WriteEncodedElementString(string name, string value)
{
Dbg.Assert(!string.IsNullOrEmpty(name), "Caller should validate the parameter");
Dbg.Assert(value != null, "Caller should validate the parameter");
this.CheckIfStopping();
2016-07-29 13:02:49 -07:00
value = EncodeString(value);
if ((_context.options & SerializationOptions.NoNamespace) == SerializationOptions.NoNamespace)
2016-07-29 13:02:49 -07:00
{
_writer.WriteElementString(name, value);
}
else
{
_writer.WriteElementString(name, SerializationStrings.MonadNamespace, value);
}
}
2016-07-29 13:02:49 -07:00
#endregion misc
}
2016-07-29 13:02:49 -07:00
/// <summary>
/// This internal class provides methods for de-serializing mshObject.
/// </summary>
internal class InternalDeserializer
{
#region constructor
2016-07-29 13:02:49 -07:00
/// <summary>
/// XmlReader from which object is deserialized.
2016-07-29 13:02:49 -07:00
/// </summary>
private readonly XmlReader _reader;
2016-07-29 13:02:49 -07:00
/// <summary>
/// Deserialization context.
2016-07-29 13:02:49 -07:00
/// </summary>
private readonly DeserializationContext _context;
2016-07-29 13:02:49 -07:00
/// Used by Remoting infrastructure. This TypeTable instance
/// will be used by Serializer if ExecutionContext is not
/// available (to get the ExecutionContext's TypeTable)
2016-07-29 13:02:49 -07:00
private TypeTable _typeTable;
2016-07-29 13:02:49 -07:00
/// <summary>
/// If true, unknowntags are allowed inside PSObject.
2016-07-29 13:02:49 -07:00
/// </summary>
private bool UnknownTagsAllowed
{
get
{
Dbg.Assert(_version.Major <= 1, "Deserializer assumes clixml version is <= 1.1");
// If minor version is greater than 1, it means that there can be
// some unknown tags in xml. Deserialization should ignore such element.
2016-07-29 13:02:49 -07:00
return (_version.Minor > 1);
}
}
[SuppressMessage(
"Performance",
"CA1822:Mark members as static",
Justification = "Accesses instance members in preprocessor branch.")]
2016-07-29 13:02:49 -07:00
private bool DuplicateRefIdsAllowed
{
get
{
#if DEBUG
2016-07-29 13:02:49 -07:00
Dbg.Assert(_version.Major <= 1, "Deserializer assumes clixml version is <= 1.1");
Version boundaryVersion = new Version(1, 1, 0, 1);
return (_version < boundaryVersion);
#else
2016-07-29 13:02:49 -07:00
return true; // handle v1 stuff gracefully
#endif
2016-07-29 13:02:49 -07:00
}
}
2016-07-29 13:02:49 -07:00
/// <summary>
/// Depth below top level - used to prevent stack overflow during deserialization.
2016-07-29 13:02:49 -07:00
/// </summary>
private int _depthBelowTopLevel;
2016-07-29 13:02:49 -07:00
/// <summary>
/// Version declared by the clixml being read.
2016-07-29 13:02:49 -07:00
/// </summary>
private Version _version;
2016-07-29 13:02:49 -07:00
private const int MaxDepthBelowTopLevel = 50;
2016-07-29 13:02:49 -07:00
private readonly ReferenceIdHandlerForDeserializer<object> _objectRefIdHandler;
private readonly ReferenceIdHandlerForDeserializer<ConsolidatedString> _typeRefIdHandler;
2016-07-29 13:02:49 -07:00
/// <summary>
/// </summary>
/// <param name="reader"></param>
/// <param name="context"></param>
internal InternalDeserializer(XmlReader reader, DeserializationContext context)
{
Dbg.Assert(reader != null, "caller should validate the parameter");
2016-07-29 13:02:49 -07:00
_reader = reader;
_context = context;
2016-07-29 13:02:49 -07:00
_objectRefIdHandler = new ReferenceIdHandlerForDeserializer<object>();
_typeRefIdHandler = new ReferenceIdHandlerForDeserializer<ConsolidatedString>();
}
2016-07-29 13:02:49 -07:00
#endregion constructor
2017-07-14 09:52:24 -07:00
#region Known CIMTypes
private static readonly Lazy<HashSet<Type>> s_knownCimArrayTypes = new Lazy<HashSet<Type>>(
2017-07-14 09:52:24 -07:00
() =>
new HashSet<Type>
{
typeof(bool),
2017-07-14 09:52:24 -07:00
typeof(byte),
typeof(char),
typeof(DateTime),
typeof(decimal),
typeof(double),
2017-07-14 09:52:24 -07:00
typeof(Int16),
typeof(Int32),
typeof(Int64),
typeof(sbyte),
2017-07-14 09:52:24 -07:00
typeof(Single),
typeof(string),
2017-07-14 09:52:24 -07:00
typeof(TimeSpan),
typeof(UInt16),
typeof(UInt32),
typeof(UInt64),
typeof(object),
typeof(CimInstance)
}
);
#endregion
2016-07-29 13:02:49 -07:00
#region deserialization
/// <summary>
/// Used by Remoting infrastructure. This TypeTable instance
/// will be used by Deserializer if ExecutionContext is not
/// available (to get the ExecutionContext's TypeTable)
2016-07-29 13:02:49 -07:00
/// </summary>
internal TypeTable TypeTable
{
get { return _typeTable; }
2016-07-29 13:02:49 -07:00
set { _typeTable = value; }
}
2016-07-29 13:02:49 -07:00
/// <summary>
/// Validates the version for correctness. Also validates that deserializer
/// can deserialize this version.
/// </summary>
/// <param name="version">
/// version in string format
/// </param>
internal void ValidateVersion(string version)
{
Dbg.Assert(version != null, "Caller should validate the parameter");
2016-07-29 13:02:49 -07:00
_version = null;
Exception exceptionToRethrow = null;
try
{
_version = new Version(version);
}
catch (ArgumentException e)
{
exceptionToRethrow = e;
}
catch (FormatException e)
{
exceptionToRethrow = e;
}
2016-07-29 13:02:49 -07:00
if (exceptionToRethrow != null)
{
throw NewXmlException(Serialization.InvalidVersion, exceptionToRethrow);
}
// Versioning Note:Future version of serialization can add new known types.
// This version will ignore those known types, if they are base object.
// It is expected that future version will still put information in base
// and adapter properties which this serializer can read and use.
// For example, assume the version 2 serialization engine supports a new known
// type IPAddress. The version 1 deserializer doesn't know IPAddress as known
// type and it must retrieve it as an PSObject. The version 2 serializer
// can serialize this as follows:
// <PSObject Version=1.2 Was=Deserialized.IPAddress >
2016-07-29 13:02:49 -07:00
// <TypeNames>...</TypeNames>
// <BaseObject>
// <IPAddress>120.23.35.53</IPAddress>
2016-07-29 13:02:49 -07:00
// </BaseObject>
// <Properties>
// <string name=Address>120.23.34.53</string>
// <string name=class>A</string>
// </Properties>
// </PSObject>
// In above example, V1 serializer will ignore <IPAddress> element and read
2016-07-29 13:02:49 -07:00
// properties from <Properties>
// V2 serializer can read <IPAddress> tag and ignore properties.
// Read serialization note doc for information.
// Now validate the major version number is 1
2016-07-29 13:02:49 -07:00
if (_version.Major != 1)
{
throw NewXmlException(Serialization.UnexpectedVersion, null, _version.Major);
}
}
2016-07-29 13:02:49 -07:00
private object ReadOneDeserializedObject(out string streamName, out bool isKnownPrimitiveType)
{
if (_reader.NodeType != XmlNodeType.Element)
{
throw NewXmlException(Serialization.InvalidNodeType, null,
2020-05-19 19:57:52 +01:00
_reader.NodeType.ToString(), nameof(XmlNodeType.Element));
2016-07-29 13:02:49 -07:00
}
2016-07-29 13:02:49 -07:00
s_trace.WriteLine("Processing start node {0}", _reader.LocalName);
2016-07-29 13:02:49 -07:00
streamName = _reader.GetAttribute(SerializationStrings.StreamNameAttribute);
isKnownPrimitiveType = false;
// handle nil node
2016-07-29 13:02:49 -07:00
if (IsNextElement(SerializationStrings.NilTag))
{
Skip();
return null;
}
// Handle reference to previous deserialized object.
2016-07-29 13:02:49 -07:00
if (IsNextElement(SerializationStrings.ReferenceTag))
{
string refId = _reader.GetAttribute(SerializationStrings.ReferenceIdAttribute);
if (refId == null)
2016-07-29 13:02:49 -07:00
{
throw NewXmlException(Serialization.AttributeExpected, null, SerializationStrings.ReferenceIdAttribute);
}
2016-07-29 13:02:49 -07:00
object duplicate = _objectRefIdHandler.GetReferencedObject(refId);
if (duplicate == null)
2016-07-29 13:02:49 -07:00
{
throw NewXmlException(Serialization.InvalidReferenceId, null, refId);
}
2016-07-29 13:02:49 -07:00
Skip();
return duplicate;
}
// Handle primitive known types
2016-07-29 13:02:49 -07:00
TypeSerializationInfo pktInfo = KnownTypes.GetTypeSerializationInfoFromItemTag(_reader.LocalName);
if (pktInfo != null)
{
s_trace.WriteLine("Primitive Knowntype Element {0}", pktInfo.ItemTag);
isKnownPrimitiveType = true;
return ReadPrimaryKnownType(pktInfo);
}
// Handle PSObject
2016-07-29 13:02:49 -07:00
if (IsNextElement(SerializationStrings.PSObjectTag))
{
s_trace.WriteLine("PSObject Element");
return ReadPSObject();
}
// If we are here, we have an unknown node. Unknown nodes may
// be allowed inside PSObject. We do not allow them at top level.
2016-07-29 13:02:49 -07:00
s_trace.TraceError("Invalid element {0} tag found", _reader.LocalName);
throw NewXmlException(Serialization.InvalidElementTag, null, _reader.LocalName);
}
2016-07-29 13:02:49 -07:00
private bool _isStopping = false;
2016-07-29 13:02:49 -07:00
/// <summary>
/// Called from a separate thread will stop the serialization process.
2016-07-29 13:02:49 -07:00
/// </summary>
internal void Stop()
{
_isStopping = true;
}
private void CheckIfStopping()
{
2016-07-29 13:02:49 -07:00
if (_isStopping)
{
throw PSTraceSource.NewInvalidOperationException(Serialization.Stopping);
}
}
2016-07-29 13:02:49 -07:00
internal const string CimInstanceMetadataProperty = "__InstanceMetadata";
internal const string CimModifiedProperties = "Modified";
internal const string CimClassMetadataProperty = "__ClassMetadata";
internal const string CimClassNameProperty = "ClassName";
internal const string CimNamespaceProperty = "Namespace";
internal const string CimServerNameProperty = "ServerName";
internal const string CimHashCodeProperty = "Hash";
internal const string CimMiXmlProperty = "MiXml";
private static bool RehydrateCimInstanceProperty(
2016-07-29 13:02:49 -07:00
CimInstance cimInstance,
PSPropertyInfo deserializedProperty,
HashSet<string> namesOfModifiedProperties)
{
Dbg.Assert(cimInstance != null, "Caller should make sure cimInstance != null");
Dbg.Assert(deserializedProperty != null, "Caller should make sure deserializedProperty != null");
2016-07-29 13:02:49 -07:00
if (deserializedProperty.Name.Equals(RemotingConstants.ComputerNameNoteProperty, StringComparison.OrdinalIgnoreCase))
{
string psComputerNameValue = deserializedProperty.Value as string;
if (psComputerNameValue != null)
{
cimInstance.SetCimSessionComputerName(psComputerNameValue);
}
2016-07-29 13:02:49 -07:00
return true;
}
2016-07-29 13:02:49 -07:00
CimProperty cimProperty = cimInstance.CimInstanceProperties[deserializedProperty.Name];
if (cimProperty == null)
2016-07-29 13:02:49 -07:00
{
return false;
}
2016-07-29 13:02:49 -07:00
// TODO/FIXME - think if it is possible to do the array handling in a more efficient way
object propertyValue = deserializedProperty.Value;
if (propertyValue != null)
{
PSObject psoPropertyValue = PSObject.AsPSObject(propertyValue);
if (psoPropertyValue.BaseObject is ArrayList)
{
if ((psoPropertyValue.InternalTypeNames == null) || (psoPropertyValue.InternalTypeNames.Count == 0))
2016-07-29 13:02:49 -07:00
{
return false;
}
2016-07-29 13:02:49 -07:00
string originalArrayTypeName = Deserializer.MaskDeserializationPrefix(psoPropertyValue.InternalTypeNames[0]);
if (originalArrayTypeName == null)
2016-07-29 13:02:49 -07:00
{
return false;
}
2016-07-29 13:02:49 -07:00
Type originalArrayType;
if (!LanguagePrimitives.TryConvertTo(originalArrayTypeName, CultureInfo.InvariantCulture, out originalArrayType))
{
return false;
}
2017-07-14 09:52:24 -07:00
if (!originalArrayType.IsArray || !s_knownCimArrayTypes.Value.Contains(originalArrayType.GetElementType()))
2016-07-29 13:02:49 -07:00
{
return false;
}
2016-07-29 13:02:49 -07:00
object newPropertyValue;
if (!LanguagePrimitives.TryConvertTo(propertyValue, originalArrayType, CultureInfo.InvariantCulture, out newPropertyValue))
{
return false;
}
2016-07-29 13:02:49 -07:00
psoPropertyValue = PSObject.AsPSObject(newPropertyValue);
}
2016-07-29 13:02:49 -07:00
propertyValue = psoPropertyValue.BaseObject;
}
2016-07-29 13:02:49 -07:00
try
{
cimProperty.Value = propertyValue;
2016-07-29 13:02:49 -07:00
if (!namesOfModifiedProperties.Contains(deserializedProperty.Name))
{
cimProperty.IsValueModified = false;
}
#if DEBUG
2016-07-29 13:02:49 -07:00
else
{
Dbg.Assert(cimProperty.IsValueModified, "Deserialized CIM properties should by default be marked as 'modified' ");
}
#endif
2016-07-29 13:02:49 -07:00
}
catch (FormatException)
{
return false;
}
catch (InvalidCastException)
{
return false;
}
catch (ArgumentException)
{
return false;
}
catch (CimException)
{
return false;
}
2016-07-29 13:02:49 -07:00
return true;
}
private static readonly Lazy<CimDeserializer> s_cimDeserializer = new Lazy<CimDeserializer>(CimDeserializer.Create);
2016-07-29 13:02:49 -07:00
private CimClass RehydrateCimClass(PSPropertyInfo classMetadataProperty)
{
if ((classMetadataProperty == null) || (classMetadataProperty.Value == null))
2016-07-29 13:02:49 -07:00
{
return null;
}
2016-07-29 13:02:49 -07:00
IEnumerable deserializedClasses = LanguagePrimitives.GetEnumerable(classMetadataProperty.Value);
if (deserializedClasses == null)
2016-07-29 13:02:49 -07:00
{
return null;
}
2016-07-29 13:02:49 -07:00
Stack<KeyValuePair<CimClassSerializationId, CimClass>> cimClassesToAddToCache = new Stack<KeyValuePair<CimClassSerializationId, CimClass>>();
2016-07-29 13:02:49 -07:00
//
// REHYDRATE CLASS METADATA
//
CimClass parentClass = null;
CimClass currentClass = null;
foreach (var deserializedClass in deserializedClasses)
{
parentClass = currentClass;
if (deserializedClass == null)
2016-07-29 13:02:49 -07:00
{
return null;
}
2016-07-29 13:02:49 -07:00
PSObject psoDeserializedClass = PSObject.AsPSObject(deserializedClass);
if (!(psoDeserializedClass.InstanceMembers[InternalDeserializer.CimNamespaceProperty] is PSPropertyInfo namespaceProperty))
2016-07-29 13:02:49 -07:00
{
return null;
}
2016-07-29 13:02:49 -07:00
string cimNamespace = namespaceProperty.Value as string;
if (!(psoDeserializedClass.InstanceMembers[InternalDeserializer.CimClassNameProperty] is PSPropertyInfo classNameProperty))
2016-07-29 13:02:49 -07:00
{
return null;
}
2016-07-29 13:02:49 -07:00
string cimClassName = classNameProperty.Value as string;
if (!(psoDeserializedClass.InstanceMembers[InternalDeserializer.CimServerNameProperty] is PSPropertyInfo computerNameProperty))
2016-07-29 13:02:49 -07:00
{
return null;
}
2016-07-29 13:02:49 -07:00
string computerName = computerNameProperty.Value as string;
if (!(psoDeserializedClass.InstanceMembers[InternalDeserializer.CimHashCodeProperty] is PSPropertyInfo hashCodeProperty))
2016-07-29 13:02:49 -07:00
{
return null;
}
2016-07-29 13:02:49 -07:00
var hashCodeObject = hashCodeProperty.Value;
if (hashCodeObject == null)
2016-07-29 13:02:49 -07:00
{
return null;
}
2016-07-29 13:02:49 -07:00
if (hashCodeObject is PSObject)
{
hashCodeObject = ((PSObject)hashCodeObject).BaseObject;
}
if (hashCodeObject is not int)
2016-07-29 13:02:49 -07:00
{
return null;
}
2016-07-29 13:02:49 -07:00
int hashCode = (int)hashCodeObject;
2016-07-29 13:02:49 -07:00
CimClassSerializationId cimClassSerializationId = new CimClassSerializationId(cimClassName, cimNamespace, computerName, hashCode);
currentClass = _context.cimClassSerializationIdCache.GetCimClassFromCache(cimClassSerializationId);
if (currentClass != null)
{
continue;
}
2016-07-29 13:02:49 -07:00
PSPropertyInfo miXmlProperty = psoDeserializedClass.InstanceMembers[InternalDeserializer.CimMiXmlProperty] as PSPropertyInfo;
if ((miXmlProperty == null) || (miXmlProperty.Value == null))
2016-07-29 13:02:49 -07:00
{
return null;
}
2016-07-29 13:02:49 -07:00
string miXmlString = miXmlProperty.Value.ToString();
byte[] miXmlBytes = Encoding.Unicode.GetBytes(miXmlString);
uint offset = 0;
try
{
currentClass = s_cimDeserializer.Value.DeserializeClass(
miXmlBytes,
ref offset,
parentClass,
computerName: computerName,
namespaceName: cimNamespace);
cimClassesToAddToCache.Push(new KeyValuePair<CimClassSerializationId, CimClass>(cimClassSerializationId, currentClass));
}
catch (CimException)
{
return null;
}
}
2016-07-29 13:02:49 -07:00
//
// UPDATE CLASSDECL DACHE
//
foreach (var cacheEntry in cimClassesToAddToCache)
{
_context.cimClassSerializationIdCache.AddCimClassToCache(cacheEntry.Key, cacheEntry.Value);
}
2016-07-29 13:02:49 -07:00
return currentClass;
}
2016-07-29 13:02:49 -07:00
// NOTE: Win7 change for refid-s that span multiple xml documents: ADMIN: changelist #226414
2016-07-29 13:02:49 -07:00
private PSObject RehydrateCimInstance(PSObject deserializedObject)
{
if (deserializedObject.BaseObject is not PSCustomObject)
2016-07-29 13:02:49 -07:00
{
return deserializedObject;
}
2016-07-29 13:02:49 -07:00
PSPropertyInfo classMetadataProperty = deserializedObject.InstanceMembers[CimClassMetadataProperty] as PSPropertyInfo;
CimClass cimClass = RehydrateCimClass(classMetadataProperty);
if (cimClass == null)
2016-07-29 13:02:49 -07:00
{
return deserializedObject;
}
2016-07-29 13:02:49 -07:00
CimInstance cimInstance;
try
{
cimInstance = new CimInstance(cimClass);
}
catch (CimException)
{
return deserializedObject;
}
2016-07-29 13:02:49 -07:00
PSObject psoCimInstance = PSObject.AsPSObject(cimInstance);
2016-07-29 13:02:49 -07:00
// process __InstanceMetadata
HashSet<string> namesOfModifiedProperties = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
PSPropertyInfo instanceMetadataProperty = deserializedObject.InstanceMembers[CimInstanceMetadataProperty] as PSPropertyInfo;
if ((instanceMetadataProperty != null) && (instanceMetadataProperty.Value != null))
{
PSObject instanceMetadata = PSObject.AsPSObject(instanceMetadataProperty.Value);
2016-07-29 13:02:49 -07:00
PSPropertyInfo modifiedPropertiesProperty = instanceMetadata.InstanceMembers[CimModifiedProperties] as PSPropertyInfo;
if ((modifiedPropertiesProperty != null) && (modifiedPropertiesProperty.Value != null))
{
string modifiedPropertiesString = modifiedPropertiesProperty.Value.ToString();
foreach (string nameOfModifiedProperty in modifiedPropertiesString.Split(Utils.Separators.Space))
{
namesOfModifiedProperties.Add(nameOfModifiedProperty);
}
}
}
2016-07-29 13:02:49 -07:00
// process properties that were originally "adapted" properties
if (deserializedObject.AdaptedMembers != null)
2016-07-29 13:02:49 -07:00
{
foreach (PSMemberInfo deserializedMemberInfo in deserializedObject.AdaptedMembers)
2016-07-29 13:02:49 -07:00
{
if (!(deserializedMemberInfo is PSPropertyInfo deserializedProperty))
2016-07-29 13:02:49 -07:00
{
continue;
}
2016-07-29 13:02:49 -07:00
bool propertyHandledSuccessfully = RehydrateCimInstanceProperty(
cimInstance,
deserializedProperty,
namesOfModifiedProperties);
2016-07-29 13:02:49 -07:00
if (!propertyHandledSuccessfully)
{
return deserializedObject;
}
}
}
2016-07-29 13:02:49 -07:00
// process properties that were originally "extended" properties
foreach (PSMemberInfo deserializedMemberInfo in deserializedObject.InstanceMembers)
{
if (!(deserializedMemberInfo is PSPropertyInfo deserializedProperty))
2016-07-29 13:02:49 -07:00
{
continue;
}
2016-07-29 13:02:49 -07:00
// skip adapted properties
if ((deserializedObject.AdaptedMembers != null) && (deserializedObject.AdaptedMembers[deserializedProperty.Name] != null))
2016-07-29 13:02:49 -07:00
{
continue;
}
2016-07-29 13:02:49 -07:00
// skip metadata introduced by CliXml/CimInstance serialization
if (deserializedProperty.Name.Equals(CimClassMetadataProperty, StringComparison.OrdinalIgnoreCase))
{
continue;
}
2016-07-29 13:02:49 -07:00
// skip properties re-added by the client (i.e. through types.ps1xml)
if (psoCimInstance.Properties[deserializedProperty.Name] != null)
{
continue;
}
2016-07-29 13:02:49 -07:00
PSNoteProperty noteProperty = new PSNoteProperty(deserializedProperty.Name, deserializedProperty.Value);
psoCimInstance.Properties.Add(noteProperty);
}
2016-07-29 13:02:49 -07:00
return psoCimInstance;
}
2016-07-29 13:02:49 -07:00
/// <summary>
/// Reads one object. At this point reader should be positioned
/// at the start tag of object.
/// </summary>
/// <returns>
/// Deserialized Object.
/// </returns>
internal object ReadOneObject(out string streamName)
{
this.CheckIfStopping();
2016-07-29 13:02:49 -07:00
try
{
_depthBelowTopLevel++;
2016-07-29 13:02:49 -07:00
Dbg.Assert(_depthBelowTopLevel <= MaxDepthBelowTopLevel, "depthBelowTopLevel should be <= MaxDepthBelowTopLevel");
if (_depthBelowTopLevel == MaxDepthBelowTopLevel)
{
throw NewXmlException(Serialization.DeserializationTooDeep, null);
}
2016-07-29 13:02:49 -07:00
bool isKnownPrimitiveType;
object result = ReadOneDeserializedObject(out streamName, out isKnownPrimitiveType);
if (result == null)
2016-07-29 13:02:49 -07:00
{
return null;
}
2016-07-29 13:02:49 -07:00
if (!isKnownPrimitiveType)
{
PSObject mshSource = PSObject.AsPSObject(result);
if (Deserializer.IsDeserializedInstanceOfType(mshSource, typeof(CimInstance)))
{
return RehydrateCimInstance(mshSource);
}
2016-07-29 13:02:49 -07:00
// Convert deserialized object to a user-defined type (specified in a types.ps1xml file)
Type targetType = mshSource.GetTargetTypeForDeserialization(_typeTable);
if (targetType != null)
2016-07-29 13:02:49 -07:00
{
Exception rehydrationException = null;
try
{
object rehydratedResult = LanguagePrimitives.ConvertTo(
result, targetType, true /* recurse */, CultureInfo.InvariantCulture, _typeTable);
2016-07-29 13:02:49 -07:00
PSEtwLog.LogAnalyticVerbose(PSEventId.Serializer_RehydrationSuccess,
PSOpcode.Rehydration, PSTask.Serialization, PSKeyword.Serializer,
mshSource.InternalTypeNames.Key, targetType.FullName,
rehydratedResult.GetType().FullName);
2016-07-29 13:02:49 -07:00
return rehydratedResult;
}
catch (InvalidCastException e)
{
rehydrationException = e;
}
catch (ArgumentException e)
{
rehydrationException = e;
}
2016-07-29 13:02:49 -07:00
Dbg.Assert(rehydrationException != null,
"The only way to get here is with rehydrationException != null");
2016-07-29 13:02:49 -07:00
PSEtwLog.LogAnalyticError(PSEventId.Serializer_RehydrationFailure,
PSOpcode.Rehydration, PSTask.Serialization, PSKeyword.Serializer,
mshSource.InternalTypeNames.Key,
targetType.FullName,
rehydrationException.ToString(),
rehydrationException.InnerException == null
2016-07-29 13:02:49 -07:00
? string.Empty
: rehydrationException.InnerException.ToString());
}
}
2016-07-29 13:02:49 -07:00
return result;
}
finally
{
_depthBelowTopLevel--;
Dbg.Assert(_depthBelowTopLevel >= 0, "depthBelowTopLevel should be >= 0");
}
}
2016-07-29 13:02:49 -07:00
private object ReadOneObject()
{
string ignore;
return ReadOneObject(out ignore);
}
// Reads one PSObject
2016-07-29 13:02:49 -07:00
private PSObject ReadPSObject()
{
PSObject dso = ReadAttributeAndCreatePSObject();
// Read start element tag
if (!ReadStartElementAndHandleEmpty(SerializationStrings.PSObjectTag))
2016-07-29 13:02:49 -07:00
{
// Empty element.
2016-07-29 13:02:49 -07:00
return dso;
}
2016-07-29 13:02:49 -07:00
bool overrideTypeInfo = true;
// Process all the child nodes
2016-07-29 13:02:49 -07:00
while (_reader.NodeType == XmlNodeType.Element)
{
if (IsNextElement(SerializationStrings.TypeNamesTag) ||
IsNextElement(SerializationStrings.TypeNamesReferenceTag))
{
ReadTypeNames(dso);
overrideTypeInfo = false;
}
else if (IsNextElement(SerializationStrings.AdapterProperties))
{
ReadProperties(dso);
}
else if (IsNextElement(SerializationStrings.MemberSet))
{
ReadMemberSet(dso.InstanceMembers);
}
else if (IsNextElement(SerializationStrings.ToStringElementTag))
{
dso.ToStringFromDeserialization = ReadDecodedElementString(SerializationStrings.ToStringElementTag);
dso.InstanceMembers.Add(PSObject.DotNetInstanceAdapter.GetDotNetMethod<PSMemberInfo>(dso, "ToString"));
2016-07-29 13:02:49 -07:00
PSGetMemberBinder.SetHasInstanceMember("ToString");
// Fix for Win8:75437
// The TokenText property is used in type conversion and it is not being populated during deserialization
// As a result, parameter binding fails in the following case on a remote session
2017-01-16 13:31:14 -08:00
// register-psssessionconfiguration -Name foo -psversion 3.0
// The value "3.0" is treated as a double and since the TokenText property holds null, the type converter tries to convert
2016-07-29 13:02:49 -07:00
// from System.Double to System.Version using Parse method of System.Version and fails
dso.TokenText = dso.ToStringFromDeserialization;
}
else
{
// Handle BaseObject
2016-07-29 13:02:49 -07:00
object baseObject = null;
ContainerType ct = ContainerType.None;
// Check if tag is PrimaryKnownType.
2016-07-29 13:02:49 -07:00
TypeSerializationInfo pktInfo = KnownTypes.GetTypeSerializationInfoFromItemTag(_reader.LocalName);
if (pktInfo != null)
{
s_trace.WriteLine("Primitive Knowntype Element {0}", pktInfo.ItemTag);
baseObject = ReadPrimaryKnownType(pktInfo);
}
else if (IsKnownContainerTag(out ct))
{
s_trace.WriteLine("Found container node {0}", ct);
baseObject = ReadKnownContainer(ct);
}
else if (IsNextElement(SerializationStrings.PSObjectTag))
{
s_trace.WriteLine("Found PSObject node");
baseObject = ReadOneObject();
}
else
{
// We have an unknown tag
s_trace.WriteLine("Unknown tag {0} encountered", _reader.LocalName);
2016-07-29 13:02:49 -07:00
if (UnknownTagsAllowed)
{
Skip();
}
else
{
throw NewXmlException(Serialization.InvalidElementTag, null, _reader.LocalName);
}
}
2016-07-29 13:02:49 -07:00
if (baseObject != null)
{
dso.SetCoreOnDeserialization(baseObject, overrideTypeInfo);
}
}
}
2016-07-29 13:02:49 -07:00
ReadEndElement();
2016-07-29 13:02:49 -07:00
PSObject immediateBasePso = dso.ImmediateBaseObject as PSObject;
if (immediateBasePso != null)
{
PSObject.CopyDeserializerFields(source: immediateBasePso, target: dso);
}
2016-07-29 13:02:49 -07:00
return dso;
}
2016-07-29 13:02:49 -07:00
/// <summary>
2017-01-16 13:31:14 -08:00
/// This function reads the refId attribute and creates a
/// mshObject for that attribute.
2016-07-29 13:02:49 -07:00
/// </summary>
/// <returns>MshObject which is created for refId.</returns>
2016-07-29 13:02:49 -07:00
private PSObject ReadAttributeAndCreatePSObject()
{
string refId = _reader.GetAttribute(SerializationStrings.ReferenceIdAttribute);
PSObject sh = new PSObject();
// RefId is not mandatory attribute
2016-07-29 13:02:49 -07:00
if (refId != null)
{
s_trace.WriteLine("Read PSObject with refId: {0}", refId);
_objectRefIdHandler.SetRefId(sh, refId, this.DuplicateRefIdsAllowed);
}
2016-07-29 13:02:49 -07:00
return sh;
}
2016-07-29 13:02:49 -07:00
/// <summary>
/// Read type names.
2016-07-29 13:02:49 -07:00
/// </summary>
/// <param name="dso">
/// PSObject to which TypeNames are added
/// </param>
private void ReadTypeNames(PSObject dso)
{
Dbg.Assert(dso != null, "caller should validate the parameter");
Dbg.Assert(_reader.NodeType == XmlNodeType.Element, "NodeType should be Element");
2016-07-29 13:02:49 -07:00
if (IsNextElement(SerializationStrings.TypeNamesTag))
{
Collection<string> typeNames = new Collection<string>();
// Read refId attribute if available
2016-07-29 13:02:49 -07:00
string refId = _reader.GetAttribute(SerializationStrings.ReferenceIdAttribute);
2016-07-29 13:02:49 -07:00
s_trace.WriteLine("Processing TypeNamesTag with refId {0}", refId);
2016-07-29 13:02:49 -07:00
if (ReadStartElementAndHandleEmpty(SerializationStrings.TypeNamesTag))
{
while (_reader.NodeType == XmlNodeType.Element)
{
if (IsNextElement(SerializationStrings.TypeNamesItemTag))
{
string item = ReadDecodedElementString(SerializationStrings.TypeNamesItemTag);
if (!string.IsNullOrEmpty(item))
{
Deserializer.AddDeserializationPrefix(ref item);
typeNames.Add(item);
}
}
else
{
throw NewXmlException(Serialization.InvalidElementTag, null, _reader.LocalName);
}
}
2016-07-29 13:02:49 -07:00
ReadEndElement();
}
2016-07-29 13:02:49 -07:00
dso.InternalTypeNames = new ConsolidatedString(typeNames);
2016-07-29 13:02:49 -07:00
if (refId != null)
{
_typeRefIdHandler.SetRefId(dso.InternalTypeNames, refId, this.DuplicateRefIdsAllowed);
}
}
else if (IsNextElement(SerializationStrings.TypeNamesReferenceTag))
{
string refId = _reader.GetAttribute(SerializationStrings.ReferenceIdAttribute);
s_trace.WriteLine("Processing TypeNamesReferenceTag with refId {0}", refId);
if (refId == null)
2016-07-29 13:02:49 -07:00
{
throw NewXmlException(Serialization.AttributeExpected, null, SerializationStrings.ReferenceIdAttribute);
}
2016-07-29 13:02:49 -07:00
ConsolidatedString typeNames = _typeRefIdHandler.GetReferencedObject(refId);
if (typeNames == null)
2016-07-29 13:02:49 -07:00
{
throw NewXmlException(Serialization.InvalidTypeHierarchyReferenceId, null, refId);
}
2016-07-29 13:02:49 -07:00
// At this point we know that we will clone the ConsolidatedString object, so we might end up
// allocating much more memory than the length of the xml string
// We have to account for that to limit that to remoting quota and protect against OOM.
_context.LogExtraMemoryUsage(
typeNames.Key.Length * sizeof(char) // Key is shared among the cloned and original object
// but the list of strings isn't. The expression to the left
// is roughly the size of memory the list of strings occupies
- 29 // size of <Obj><TNRef RefId="0"/></Obj> in UTF8 encoding
);
dso.InternalTypeNames = new ConsolidatedString(typeNames);
// Skip the node
2016-07-29 13:02:49 -07:00
Skip();
}
else
{
Dbg.Assert(false, "caller should validate that we do no reach here");
}
}
/// <summary>
/// Read properties.
/// </summary>
private void ReadProperties(PSObject dso)
{
Dbg.Assert(dso != null, "caller should validate the parameter");
Dbg.Assert(_reader.NodeType == XmlNodeType.Element, "NodeType should be Element");
// Since we are adding baseobject properties as propertybag,
// mark the object as deserialized.
dso.IsDeserialized = true;
dso.AdaptedMembers = new PSMemberInfoInternalCollection<PSPropertyInfo>();
// Add the GetType method to the instance members, so that it works on deserialized psobjects
dso.InstanceMembers.Add(PSObject.DotNetInstanceAdapter.GetDotNetMethod<PSMemberInfo>(dso, "GetType"));
PSGetMemberBinder.SetHasInstanceMember("GetType");
// Set Clr members to a collection which is empty
dso.ClrMembers = new PSMemberInfoInternalCollection<PSPropertyInfo>();
if (ReadStartElementAndHandleEmpty(SerializationStrings.AdapterProperties))
{
// Read one or more property elements
while (_reader.NodeType == XmlNodeType.Element)
{
string property = ReadNameAttribute();
object value = ReadOneObject();
PSProperty prop = new PSProperty(property, value);
dso.AdaptedMembers.Add(prop);
}
ReadEndElement();
}
}
2016-07-29 13:02:49 -07:00
#region memberset
2016-07-29 13:02:49 -07:00
/// <summary>
2017-01-16 13:31:14 -08:00
/// Read memberset.
2016-07-29 13:02:49 -07:00
/// </summary>
/// <param name="collection">
/// collection to which members are added
/// </param>
private void ReadMemberSet(PSMemberInfoCollection<PSMemberInfo> collection)
{
Dbg.Assert(collection != null, "caller should validate the value");
2016-07-29 13:02:49 -07:00
if (ReadStartElementAndHandleEmpty(SerializationStrings.MemberSet))
{
while (_reader.NodeType == XmlNodeType.Element)
{
if (IsNextElement(SerializationStrings.MemberSet))
{
string name = ReadNameAttribute();
PSMemberSet set = new PSMemberSet(name);
collection.Add(set);
ReadMemberSet(set.Members);
2016-07-29 13:02:49 -07:00
PSGetMemberBinder.SetHasInstanceMember(name);
}
else
{
PSNoteProperty note = ReadNoteProperty();
collection.Add(note);
2016-07-29 13:02:49 -07:00
PSGetMemberBinder.SetHasInstanceMember(note.Name);
}
}
2016-07-29 13:02:49 -07:00
ReadEndElement();
}
}
2016-07-29 13:02:49 -07:00
/// <summary>
/// Read note.
2016-07-29 13:02:49 -07:00
/// </summary>
/// <returns></returns>
private PSNoteProperty ReadNoteProperty()
{
string name = ReadNameAttribute();
object value = ReadOneObject();
PSNoteProperty note = new PSNoteProperty(name, value);
return note;
}
2016-07-29 13:02:49 -07:00
#endregion memberset
2016-07-29 13:02:49 -07:00
#region known container
2016-07-29 13:02:49 -07:00
private bool IsKnownContainerTag(out ContainerType ct)
{
Dbg.Assert(_reader.NodeType == XmlNodeType.Element, "Expected node type is element");
2016-07-29 13:02:49 -07:00
if (IsNextElement(SerializationStrings.DictionaryTag))
{
ct = ContainerType.Dictionary;
}
else if (IsNextElement(SerializationStrings.QueueTag))
{
ct = ContainerType.Queue;
}
else if (IsNextElement(SerializationStrings.StackTag))
{
ct = ContainerType.Stack;
}
else if (IsNextElement(SerializationStrings.ListTag))
{
ct = ContainerType.List;
}
else if (IsNextElement(SerializationStrings.CollectionTag))
{
ct = ContainerType.Enumerable;
}
else
{
ct = ContainerType.None;
}
2016-07-29 13:02:49 -07:00
return ct != ContainerType.None;
}
2016-07-29 13:02:49 -07:00
private object ReadKnownContainer(ContainerType ct)
{
switch (ct)
{
case ContainerType.Dictionary:
return ReadDictionary(ct);
2016-07-29 13:02:49 -07:00
case ContainerType.Enumerable:
case ContainerType.List:
case ContainerType.Queue:
case ContainerType.Stack:
return ReadListContainer(ct);
2016-07-29 13:02:49 -07:00
default:
Dbg.Assert(false, "Unrecognized ContainerType enum");
return null;
}
}
2016-07-29 13:02:49 -07:00
/// <summary>
/// Read List Containers.
2016-07-29 13:02:49 -07:00
/// </summary>
/// <returns></returns>
private object ReadListContainer(ContainerType ct)
{
Dbg.Assert(ct == ContainerType.Enumerable ||
ct == ContainerType.List ||
ct == ContainerType.Queue ||
ct == ContainerType.Stack, "ct should be queue, stack, enumerable or list");
ArrayList list = new ArrayList();
2016-07-29 13:02:49 -07:00
if (ReadStartElementAndHandleEmpty(_reader.LocalName))
{
while (_reader.NodeType == XmlNodeType.Element)
{
list.Add(ReadOneObject());
}
2016-07-29 13:02:49 -07:00
ReadEndElement();
}
2016-07-29 13:02:49 -07:00
if (ct == ContainerType.Stack)
{
list.Reverse();
return new Stack(list);
}
else if (ct == ContainerType.Queue)
{
return new Queue(list);
2016-07-29 13:02:49 -07:00
}
2016-07-29 13:02:49 -07:00
return list;
}
2016-07-29 13:02:49 -07:00
/// <summary>
/// Deserialize Dictionary.
2016-07-29 13:02:49 -07:00
/// </summary>
/// <returns></returns>
private object ReadDictionary(ContainerType ct)
{
Dbg.Assert(ct == ContainerType.Dictionary, "Unrecognized ContainerType enum");
2016-07-29 13:02:49 -07:00
// We assume the hash table is a PowerShell hash table and hence uses
// a case insensitive string comparer. If we discover a key collision,
// we'll revert back to the default comparer.
Hashtable table = new Hashtable(StringComparer.CurrentCultureIgnoreCase);
int keyClashFoundIteration = 0;
if (ReadStartElementAndHandleEmpty(SerializationStrings.DictionaryTag))
{
while (_reader.NodeType == XmlNodeType.Element)
{
ReadStartElement(SerializationStrings.DictionaryEntryTag);
// Read Key
2016-07-29 13:02:49 -07:00
if (_reader.NodeType != XmlNodeType.Element)
{
throw NewXmlException(Serialization.DictionaryKeyNotSpecified, null);
}
2016-07-29 13:02:49 -07:00
string name = ReadNameAttribute();
2020-06-25 07:30:40 +01:00
if (!string.Equals(name, SerializationStrings.DictionaryKey, StringComparison.OrdinalIgnoreCase))
2016-07-29 13:02:49 -07:00
{
throw NewXmlException(Serialization.InvalidDictionaryKeyName, null);
}
2016-07-29 13:02:49 -07:00
object key = ReadOneObject();
if (key == null)
2016-07-29 13:02:49 -07:00
{
throw NewXmlException(Serialization.NullAsDictionaryKey, null);
}
// Read Value
2016-07-29 13:02:49 -07:00
if (_reader.NodeType != XmlNodeType.Element)
{
throw NewXmlException(Serialization.DictionaryValueNotSpecified, null);
}
2016-07-29 13:02:49 -07:00
name = ReadNameAttribute();
2020-06-25 07:30:40 +01:00
if (!string.Equals(name, SerializationStrings.DictionaryValue, StringComparison.OrdinalIgnoreCase))
2016-07-29 13:02:49 -07:00
{
throw NewXmlException(Serialization.InvalidDictionaryValueName, null);
}
2016-07-29 13:02:49 -07:00
object value = ReadOneObject();
2016-07-29 13:02:49 -07:00
// On the first collision, copy the hash table to one that uses the default comparer.
if (table.ContainsKey(key) && (keyClashFoundIteration == 0))
{
keyClashFoundIteration++;
Hashtable newHashTable = new Hashtable();
foreach (DictionaryEntry entry in table)
{
newHashTable.Add(entry.Key, entry.Value);
}
2016-07-29 13:02:49 -07:00
table = newHashTable;
}
2016-07-29 13:02:49 -07:00
// win8: 389060. If there are still collisions even with case-sensitive default comparer,
// use an IEqualityComparer that does object ref equality.
if (table.ContainsKey(key) && (keyClashFoundIteration == 1))
{
keyClashFoundIteration++;
IEqualityComparer equalityComparer = new ReferenceEqualityComparer();
Hashtable newHashTable = new Hashtable(equalityComparer);
foreach (DictionaryEntry entry in table)
{
newHashTable.Add(entry.Key, entry.Value);
}
2016-07-29 13:02:49 -07:00
table = newHashTable;
}
2016-07-29 13:02:49 -07:00
try
{
// Add entry to hashtable
2016-07-29 13:02:49 -07:00
table.Add(key, value);
}
catch (ArgumentException e)
{
throw this.NewXmlException(Serialization.InvalidPrimitiveType, e, typeof(Hashtable));
}
2016-07-29 13:02:49 -07:00
ReadEndElement();
}
2016-07-29 13:02:49 -07:00
ReadEndElement();
}
2016-07-29 13:02:49 -07:00
return table;
}
2016-07-29 13:02:49 -07:00
#endregion known containers
2016-07-29 13:02:49 -07:00
#endregion deserialization
2016-07-29 13:02:49 -07:00
#region Getting XmlReaderSettings
internal static XmlReaderSettings XmlReaderSettingsForCliXml { get; } = GetXmlReaderSettingsForCliXml();
private static XmlReaderSettings GetXmlReaderSettingsForCliXml()
2016-07-29 13:02:49 -07:00
{
XmlReaderSettings xrs = new XmlReaderSettings();
2016-07-29 13:02:49 -07:00
xrs.CheckCharacters = false;
xrs.CloseInput = false;
// The XML data needs to be in conformance to the rules for a well-formed XML 1.0 document.
2016-07-29 13:02:49 -07:00
xrs.ConformanceLevel = ConformanceLevel.Document;
xrs.IgnoreComments = true;
xrs.IgnoreProcessingInstructions = true;
xrs.IgnoreWhitespace = false;
xrs.MaxCharactersFromEntities = 1024;
xrs.XmlResolver = null;
xrs.DtdProcessing = DtdProcessing.Prohibit;
2016-07-29 13:02:49 -07:00
xrs.Schemas = null;
xrs.ValidationFlags = System.Xml.Schema.XmlSchemaValidationFlags.None;
xrs.ValidationType = ValidationType.None;
return xrs;
}
internal static XmlReaderSettings XmlReaderSettingsForUntrustedXmlDocument { get; } = GetXmlReaderSettingsForUntrustedXmlDocument();
private static XmlReaderSettings GetXmlReaderSettingsForUntrustedXmlDocument()
2016-07-29 13:02:49 -07:00
{
XmlReaderSettings settings = new XmlReaderSettings();
2016-07-29 13:02:49 -07:00
settings.CheckCharacters = false;
settings.ConformanceLevel = ConformanceLevel.Auto;
settings.IgnoreComments = true;
settings.IgnoreProcessingInstructions = true;
settings.IgnoreWhitespace = true;
settings.MaxCharactersFromEntities = 1024;
settings.MaxCharactersInDocument = 512 * 1024 * 1024; // 512M characters = 1GB
settings.XmlResolver = null;
settings.DtdProcessing = DtdProcessing.Parse; // Allowing DTD parsing with limits of MaxCharactersFromEntities/MaxCharactersInDocument
2016-07-29 13:02:49 -07:00
settings.ValidationFlags = System.Xml.Schema.XmlSchemaValidationFlags.None;
settings.ValidationType = ValidationType.None;
return settings;
}
2016-07-29 13:02:49 -07:00
#endregion
2016-07-29 13:02:49 -07:00
#region known type deserialization
2016-07-29 13:02:49 -07:00
internal static object DeserializeBoolean(InternalDeserializer deserializer)
{
Dbg.Assert(deserializer != null, "Caller should validate the parameter");
try
{
return XmlConvert.ToBoolean(deserializer._reader.ReadElementContentAsString());
2016-07-29 13:02:49 -07:00
}
catch (FormatException e)
{
throw deserializer.NewXmlException(Serialization.InvalidPrimitiveType, e, typeof(bool).FullName);
}
}
2016-07-29 13:02:49 -07:00
internal static object DeserializeByte(InternalDeserializer deserializer)
{
Dbg.Assert(deserializer != null, "Caller should validate the parameter");
Exception recognizedException = null;
try
{
return XmlConvert.ToByte(deserializer._reader.ReadElementContentAsString());
2016-07-29 13:02:49 -07:00
}
catch (FormatException e)
{
recognizedException = e;
}
catch (OverflowException e)
{
recognizedException = e;
}
2016-07-29 13:02:49 -07:00
throw deserializer.NewXmlException(Serialization.InvalidPrimitiveType, recognizedException, typeof(byte).FullName);
}
2016-07-29 13:02:49 -07:00
internal static object DeserializeChar(InternalDeserializer deserializer)
{
Dbg.Assert(deserializer != null, "Caller should validate the parameter");
Exception recognizedException = null;
try
{
return (char)XmlConvert.ToUInt16(deserializer._reader.ReadElementContentAsString());
2016-07-29 13:02:49 -07:00
}
catch (FormatException e)
{
recognizedException = e;
}
catch (OverflowException e)
{
recognizedException = e;
}
2016-07-29 13:02:49 -07:00
throw deserializer.NewXmlException(Serialization.InvalidPrimitiveType, recognizedException, typeof(char).FullName);
}
internal static object DeserializeDateTime(InternalDeserializer deserializer)
{
Dbg.Assert(deserializer != null, "Caller should validate the parameter");
try
{
return XmlConvert.ToDateTime(deserializer._reader.ReadElementContentAsString(), XmlDateTimeSerializationMode.RoundtripKind);
}
catch (FormatException e)
{
throw deserializer.NewXmlException(Serialization.InvalidPrimitiveType, e, typeof(DateTime).FullName);
}
}
2016-07-29 13:02:49 -07:00
internal static object DeserializeDecimal(InternalDeserializer deserializer)
{
Dbg.Assert(deserializer != null, "Caller should validate the parameter");
Exception recognizedException = null;
try
{
return XmlConvert.ToDecimal(deserializer._reader.ReadElementContentAsString());
2016-07-29 13:02:49 -07:00
}
catch (FormatException e)
{
recognizedException = e;
}
catch (OverflowException e)
{
recognizedException = e;
}
2016-07-29 13:02:49 -07:00
throw deserializer.NewXmlException(Serialization.InvalidPrimitiveType, recognizedException, typeof(decimal).FullName);
}
2016-07-29 13:02:49 -07:00
internal static object DeserializeDouble(InternalDeserializer deserializer)
{
Dbg.Assert(deserializer != null, "Caller should validate the parameter");
Exception recognizedException = null;
try
{
return XmlConvert.ToDouble(deserializer._reader.ReadElementContentAsString());
2016-07-29 13:02:49 -07:00
}
catch (FormatException e)
{
recognizedException = e;
}
catch (OverflowException e)
{
recognizedException = e;
}
2016-07-29 13:02:49 -07:00
throw deserializer.NewXmlException(Serialization.InvalidPrimitiveType, recognizedException, typeof(double).FullName);
}
2016-07-29 13:02:49 -07:00
internal static object DeserializeGuid(InternalDeserializer deserializer)
{
Dbg.Assert(deserializer != null, "Caller should validate the parameter");
Exception recognizedException = null;
try
{
return XmlConvert.ToGuid(deserializer._reader.ReadElementContentAsString());
2016-07-29 13:02:49 -07:00
}
// MSDN for XmlConvert.ToGuid doesn't list any exceptions, but
// Reflector shows that this just calls to new Guid(string)
// which MSDN documents can throw Format/OverflowException
catch (FormatException e)
{
recognizedException = e;
}
catch (OverflowException e)
{
recognizedException = e;
}
2016-07-29 13:02:49 -07:00
throw deserializer.NewXmlException(Serialization.InvalidPrimitiveType, recognizedException, typeof(Guid).FullName);
}
2016-07-29 13:02:49 -07:00
internal static object DeserializeVersion(InternalDeserializer deserializer)
{
Dbg.Assert(deserializer != null, "Caller should validate the parameter");
Exception recognizedException = null;
try
{
return new Version(deserializer._reader.ReadElementContentAsString());
2016-07-29 13:02:49 -07:00
}
catch (ArgumentException e)
{
recognizedException = e;
}
catch (FormatException e)
{
recognizedException = e;
}
catch (OverflowException e)
{
recognizedException = e;
}
2016-07-29 13:02:49 -07:00
throw deserializer.NewXmlException(Serialization.InvalidPrimitiveType, recognizedException, typeof(Version).FullName);
}
internal static object DeserializeSemanticVersion(InternalDeserializer deserializer)
{
Dbg.Assert(deserializer != null, "Caller should validate the parameter");
Exception recognizedException = null;
try
{
return new SemanticVersion(deserializer._reader.ReadElementContentAsString());
}
catch (ArgumentException e)
{
recognizedException = e;
}
catch (FormatException e)
{
recognizedException = e;
}
catch (OverflowException e)
{
recognizedException = e;
}
throw deserializer.NewXmlException(Serialization.InvalidPrimitiveType, recognizedException, typeof(Version).FullName);
}
2016-07-29 13:02:49 -07:00
internal static object DeserializeInt16(InternalDeserializer deserializer)
{
Dbg.Assert(deserializer != null, "Caller should validate the parameter");
Exception recognizedException = null;
try
{
return XmlConvert.ToInt16(deserializer._reader.ReadElementContentAsString());
2016-07-29 13:02:49 -07:00
}
catch (FormatException e)
{
recognizedException = e;
}
catch (OverflowException e)
{
recognizedException = e;
}
2016-07-29 13:02:49 -07:00
throw deserializer.NewXmlException(Serialization.InvalidPrimitiveType, recognizedException, typeof(Int16).FullName);
}
2016-07-29 13:02:49 -07:00
internal static object DeserializeInt32(InternalDeserializer deserializer)
{
Dbg.Assert(deserializer != null, "Caller should validate the parameter");
Exception recognizedException = null;
try
{
return XmlConvert.ToInt32(deserializer._reader.ReadElementContentAsString());
2016-07-29 13:02:49 -07:00
}
catch (FormatException e)
{
recognizedException = e;
}
catch (OverflowException e)
{
recognizedException = e;
}
2016-07-29 13:02:49 -07:00
throw deserializer.NewXmlException(Serialization.InvalidPrimitiveType, recognizedException, typeof(Int32).FullName);
}
2016-07-29 13:02:49 -07:00
internal static object DeserializeInt64(InternalDeserializer deserializer)
{
Dbg.Assert(deserializer != null, "Caller should validate the parameter");
Exception recognizedException = null;
try
{
return XmlConvert.ToInt64(deserializer._reader.ReadElementContentAsString());
2016-07-29 13:02:49 -07:00
}
catch (FormatException e)
{
recognizedException = e;
}
catch (OverflowException e)
{
recognizedException = e;
}
2016-07-29 13:02:49 -07:00
throw deserializer.NewXmlException(Serialization.InvalidPrimitiveType, recognizedException, typeof(Int64).FullName);
}
2016-07-29 13:02:49 -07:00
internal static object DeserializeSByte(InternalDeserializer deserializer)
{
Dbg.Assert(deserializer != null, "Caller should validate the parameter");
Exception recognizedException = null;
try
{
return XmlConvert.ToSByte(deserializer._reader.ReadElementContentAsString());
2016-07-29 13:02:49 -07:00
}
catch (FormatException e)
{
recognizedException = e;
}
catch (OverflowException e)
{
recognizedException = e;
}
2016-07-29 13:02:49 -07:00
throw deserializer.NewXmlException(Serialization.InvalidPrimitiveType, recognizedException, typeof(sbyte).FullName);
}
2016-07-29 13:02:49 -07:00
internal static object DeserializeSingle(InternalDeserializer deserializer)
{
Dbg.Assert(deserializer != null, "Caller should validate the parameter");
Exception recognizedException = null;
try
{
return XmlConvert.ToSingle(deserializer._reader.ReadElementContentAsString());
2016-07-29 13:02:49 -07:00
}
catch (FormatException e)
{
recognizedException = e;
}
catch (OverflowException e)
{
recognizedException = e;
}
2016-07-29 13:02:49 -07:00
throw deserializer.NewXmlException(Serialization.InvalidPrimitiveType, recognizedException, typeof(float).FullName);
}
2016-07-29 13:02:49 -07:00
internal static object DeserializeScriptBlock(InternalDeserializer deserializer)
{
Dbg.Assert(deserializer != null, "Caller should validate the parameter");
string scriptBlockBody = deserializer.ReadDecodedElementString(SerializationStrings.ScriptBlockTag);
if ((deserializer._context.options & DeserializationOptions.DeserializeScriptBlocks) == DeserializationOptions.DeserializeScriptBlocks)
2016-07-29 13:02:49 -07:00
{
return ScriptBlock.Create(scriptBlockBody);
}
else
{
// Scriptblock is deserialized as string
2016-07-29 13:02:49 -07:00
return scriptBlockBody;
}
}
2016-07-29 13:02:49 -07:00
internal static object DeserializeString(InternalDeserializer deserializer)
{
Dbg.Assert(deserializer != null, "Caller should validate the parameter");
return deserializer.ReadDecodedElementString(SerializationStrings.StringTag);
}
internal static object DeserializeTimeSpan(InternalDeserializer deserializer)
2016-07-29 13:02:49 -07:00
{
Dbg.Assert(deserializer != null, "Caller should validate the parameter");
try
{
return XmlConvert.ToTimeSpan(deserializer._reader.ReadElementContentAsString());
2016-07-29 13:02:49 -07:00
}
catch (FormatException e)
{
throw deserializer.NewXmlException(Serialization.InvalidPrimitiveType, e, typeof(TimeSpan).FullName);
}
}
2016-07-29 13:02:49 -07:00
internal static object DeserializeUInt16(InternalDeserializer deserializer)
{
Dbg.Assert(deserializer != null, "Caller should validate the parameter");
Exception recognizedException = null;
try
{
return XmlConvert.ToUInt16(deserializer._reader.ReadElementContentAsString());
2016-07-29 13:02:49 -07:00
}
catch (FormatException e)
{
recognizedException = e;
}
catch (OverflowException e)
{
recognizedException = e;
}
2016-07-29 13:02:49 -07:00
throw deserializer.NewXmlException(Serialization.InvalidPrimitiveType, recognizedException, typeof(UInt16).FullName);
}
2016-07-29 13:02:49 -07:00
internal static object DeserializeUInt32(InternalDeserializer deserializer)
{
Dbg.Assert(deserializer != null, "Caller should validate the parameter");
Exception recognizedException = null;
try
{
return XmlConvert.ToUInt32(deserializer._reader.ReadElementContentAsString());
2016-07-29 13:02:49 -07:00
}
catch (FormatException e)
{
recognizedException = e;
}
catch (OverflowException e)
{
recognizedException = e;
}
2016-07-29 13:02:49 -07:00
throw deserializer.NewXmlException(Serialization.InvalidPrimitiveType, recognizedException, typeof(UInt32).FullName);
}
2016-07-29 13:02:49 -07:00
internal static object DeserializeUInt64(InternalDeserializer deserializer)
{
Dbg.Assert(deserializer != null, "Caller should validate the parameter");
Exception recognizedException = null;
try
{
return XmlConvert.ToUInt64(deserializer._reader.ReadElementContentAsString());
2016-07-29 13:02:49 -07:00
}
catch (FormatException e)
{
recognizedException = e;
}
catch (OverflowException e)
{
recognizedException = e;
}
2016-07-29 13:02:49 -07:00
throw deserializer.NewXmlException(Serialization.InvalidPrimitiveType, recognizedException, typeof(UInt64).FullName);
}
2016-07-29 13:02:49 -07:00
internal static object DeserializeUri(InternalDeserializer deserializer)
{
Dbg.Assert(deserializer != null, "Caller should validate the parameter");
try
{
string uriString = deserializer.ReadDecodedElementString(SerializationStrings.AnyUriTag);
return new Uri(uriString, UriKind.RelativeOrAbsolute);
}
catch (UriFormatException e)
{
throw deserializer.NewXmlException(Serialization.InvalidPrimitiveType, e, typeof(Uri).FullName);
}
}
2016-07-29 13:02:49 -07:00
internal static object DeserializeByteArray(InternalDeserializer deserializer)
{
Dbg.Assert(deserializer != null, "Caller should validate the parameter");
try
{
return Convert.FromBase64String(deserializer._reader.ReadElementContentAsString());
2016-07-29 13:02:49 -07:00
}
catch (FormatException e)
{
throw deserializer.NewXmlException(Serialization.InvalidPrimitiveType, e, typeof(byte[]).FullName);
}
}
2016-07-29 13:02:49 -07:00
/// <exception cref="System.Xml.XmlException"></exception>
internal static XmlDocument LoadUnsafeXmlDocument(FileInfo xmlPath, bool preserveNonElements, int? maxCharactersInDocument)
{
XmlDocument doc = null;
// same FileStream options as Reflector shows for XmlDocument.Load(path) / XmlDownloadManager.GetStream:
using (Stream stream = new FileStream(xmlPath.FullName, FileMode.Open, FileAccess.Read, FileShare.Read))
{
doc = LoadUnsafeXmlDocument(stream, preserveNonElements, maxCharactersInDocument);
}
2016-07-29 13:02:49 -07:00
return doc;
}
2016-07-29 13:02:49 -07:00
/// <exception cref="System.Xml.XmlException"></exception>
internal static XmlDocument LoadUnsafeXmlDocument(string xmlContents, bool preserveNonElements, int? maxCharactersInDocument)
{
using (TextReader textReader = new StringReader(xmlContents))
{
return LoadUnsafeXmlDocument(textReader, preserveNonElements, maxCharactersInDocument);
}
}
2016-07-29 13:02:49 -07:00
/// <exception cref="System.Xml.XmlException"></exception>
internal static XmlDocument LoadUnsafeXmlDocument(Stream stream, bool preserveNonElements, int? maxCharactersInDocument)
{
using (TextReader textReader = new StreamReader(stream))
{
return LoadUnsafeXmlDocument(textReader, preserveNonElements, maxCharactersInDocument);
}
}
2016-07-29 13:02:49 -07:00
/// <exception cref="System.Xml.XmlException"></exception>
internal static XmlDocument LoadUnsafeXmlDocument(TextReader textReader, bool preserveNonElements, int? maxCharactersInDocument)
{
XmlReaderSettings settings;
if (maxCharactersInDocument.HasValue || preserveNonElements)
{
settings = InternalDeserializer.XmlReaderSettingsForUntrustedXmlDocument.Clone();
if (maxCharactersInDocument.HasValue)
{
settings.MaxCharactersInDocument = maxCharactersInDocument.Value;
}
2016-07-29 13:02:49 -07:00
if (preserveNonElements)
{
settings.IgnoreWhitespace = false;
settings.IgnoreProcessingInstructions = false;
settings.IgnoreComments = false;
}
}
else
{
settings = InternalDeserializer.XmlReaderSettingsForUntrustedXmlDocument;
}
2016-07-29 13:02:49 -07:00
try
{
XmlReader xmlReader = XmlReader.Create(textReader, settings);
XmlDocument xmlDocument = new XmlDocument();
xmlDocument.PreserveWhitespace = preserveNonElements;
xmlDocument.Load(xmlReader);
return xmlDocument;
}
catch (InvalidOperationException invalidOperationException)
{
throw new XmlException(invalidOperationException.Message, invalidOperationException);
}
}
2016-07-29 13:02:49 -07:00
internal static object DeserializeXmlDocument(InternalDeserializer deserializer)
{
Dbg.Assert(deserializer != null, "Caller should validate the parameter");
string docAsString = deserializer.ReadDecodedElementString(SerializationStrings.XmlDocumentTag);
2016-07-29 13:02:49 -07:00
try
{
int? maxCharactersInDocument = null;
if (deserializer._context.MaximumAllowedMemory.HasValue)
{
maxCharactersInDocument = deserializer._context.MaximumAllowedMemory.Value / sizeof(char);
}
2016-07-29 13:02:49 -07:00
XmlDocument doc = InternalDeserializer.LoadUnsafeXmlDocument(
docAsString,
true, /* preserve whitespace, comments, etc. */
maxCharactersInDocument);
2016-07-29 13:02:49 -07:00
deserializer._context.LogExtraMemoryUsage((docAsString.Length - doc.OuterXml.Length) * sizeof(char));
2016-07-29 13:02:49 -07:00
return doc;
}
catch (XmlException e)
{
throw deserializer.NewXmlException(Serialization.InvalidPrimitiveType, e, typeof(XmlDocument).FullName);
}
}
2016-07-29 13:02:49 -07:00
internal static object DeserializeProgressRecord(InternalDeserializer deserializer)
{
Dbg.Assert(deserializer != null, "Caller should validate the parameter");
2016-07-29 13:02:49 -07:00
//
// read deserialized elements of a progress record
//
2016-07-29 13:02:49 -07:00
deserializer.ReadStartElement(SerializationStrings.ProgressRecord);
2016-07-29 13:02:49 -07:00
string activity = null, currentOperation = null, prt = null, statusDescription = null;
int activityId = 0, parentActivityId = 0, percentComplete = 0, secondsRemaining = 0;
2016-07-29 13:02:49 -07:00
Exception recognizedException = null;
try
{
activity = deserializer.ReadDecodedElementString(SerializationStrings.ProgressRecordActivity);
activityId = int.Parse(deserializer.ReadDecodedElementString(SerializationStrings.ProgressRecordActivityId), CultureInfo.InvariantCulture);
2016-07-29 13:02:49 -07:00
object tmp = deserializer.ReadOneObject();
currentOperation = tmp?.ToString();
2016-07-29 13:02:49 -07:00
parentActivityId = int.Parse(deserializer.ReadDecodedElementString(SerializationStrings.ProgressRecordParentActivityId), CultureInfo.InvariantCulture);
percentComplete = int.Parse(deserializer.ReadDecodedElementString(SerializationStrings.ProgressRecordPercentComplete), CultureInfo.InvariantCulture);
prt = deserializer.ReadDecodedElementString(SerializationStrings.ProgressRecordType);
secondsRemaining = int.Parse(deserializer.ReadDecodedElementString(SerializationStrings.ProgressRecordSecondsRemaining), CultureInfo.InvariantCulture);
statusDescription = deserializer.ReadDecodedElementString(SerializationStrings.ProgressRecordStatusDescription);
}
catch (FormatException e)
{
recognizedException = e;
}
catch (OverflowException e)
{
recognizedException = e;
}
2016-07-29 13:02:49 -07:00
if (recognizedException != null)
{
throw deserializer.NewXmlException(Serialization.InvalidPrimitiveType, recognizedException, typeof(UInt64).FullName);
}
2016-07-29 13:02:49 -07:00
deserializer.ReadEndElement();
2016-07-29 13:02:49 -07:00
//
// Build the progress record
//
2016-07-29 13:02:49 -07:00
ProgressRecordType type;
try
{
type = (ProgressRecordType)Enum.Parse(typeof(ProgressRecordType), prt, true);
}
catch (ArgumentException e)
{
throw deserializer.NewXmlException(Serialization.InvalidPrimitiveType, e, typeof(ProgressRecord).FullName);
}
2016-07-29 13:02:49 -07:00
try
{
ProgressRecord record = new ProgressRecord(activityId, activity, statusDescription);
2016-07-29 13:02:49 -07:00
if (!string.IsNullOrEmpty(currentOperation))
{
record.CurrentOperation = currentOperation;
}
2016-07-29 13:02:49 -07:00
record.ParentActivityId = parentActivityId;
record.PercentComplete = percentComplete;
record.RecordType = type;
record.SecondsRemaining = secondsRemaining;
2016-07-29 13:02:49 -07:00
return record;
}
catch (ArgumentException e)
{
throw deserializer.NewXmlException(Serialization.InvalidPrimitiveType, e, typeof(ProgressRecord).FullName);
}
}
2016-07-29 13:02:49 -07:00
internal static object DeserializeSecureString(InternalDeserializer deserializer)
{
Dbg.Assert(deserializer != null, "Caller should validate the parameter");
2016-07-29 13:02:49 -07:00
//
// read deserialized elements of a Secure String
//
return deserializer.ReadSecureString();
}
2016-07-29 13:02:49 -07:00
#endregion known type deserialization
2016-07-29 13:02:49 -07:00
#region misc
2016-07-29 13:02:49 -07:00
/// <summary>
/// Check if LocalName of next element is "tag"
/// </summary>
/// <param name="tag"></param>
/// <returns></returns>
private bool IsNextElement(string tag)
{
Dbg.Assert(!string.IsNullOrEmpty(tag), "Caller should validate the parameter");
return (_reader.LocalName == tag) &&
(((_context.options & DeserializationOptions.NoNamespace) != 0) ||
2016-07-29 13:02:49 -07:00
(_reader.NamespaceURI == SerializationStrings.MonadNamespace));
}
2016-07-29 13:02:49 -07:00
/// <summary>
/// Read start element in monad namespace.
2016-07-29 13:02:49 -07:00
/// </summary>
/// <param name="element">Element tag to read.</param>
/// <returns>True if not an empty element else false.</returns>
2016-07-29 13:02:49 -07:00
internal bool ReadStartElementAndHandleEmpty(string element)
{
Dbg.Assert(!string.IsNullOrEmpty(element), "Caller should validate the parameter");
// IsEmpty is set to true when element is of the form <tag/>
2016-07-29 13:02:49 -07:00
bool isEmpty = _reader.IsEmptyElement;
2016-07-29 13:02:49 -07:00
this.ReadStartElement(element);
// This takes care of the case: <tag></tag> or <tag> </tag>. In
// this case isEmpty is false.
if (!isEmpty && _reader.NodeType == XmlNodeType.EndElement)
2016-07-29 13:02:49 -07:00
{
ReadEndElement();
isEmpty = true;
}
2016-07-29 13:02:49 -07:00
return !isEmpty;
}
2016-07-29 13:02:49 -07:00
private void ReadStartElement(string element)
{
Dbg.Assert(!string.IsNullOrEmpty(element), "Caller should validate the parameter");
if ((_context.options & DeserializationOptions.NoNamespace) == DeserializationOptions.NoNamespace)
2016-07-29 13:02:49 -07:00
{
_reader.ReadStartElement(element);
}
else
{
_reader.ReadStartElement(element, SerializationStrings.MonadNamespace);
}
2016-07-29 13:02:49 -07:00
_reader.MoveToContent();
}
private void ReadEndElement()
{
_reader.ReadEndElement();
_reader.MoveToContent();
}
private string ReadDecodedElementString(string element)
{
Dbg.Assert(!string.IsNullOrEmpty(element), "Caller should validate the parameter");
this.CheckIfStopping();
string temp = null;
if ((_context.options & DeserializationOptions.NoNamespace) == DeserializationOptions.NoNamespace)
2016-07-29 13:02:49 -07:00
{
temp = _reader.ReadElementContentAsString(element, string.Empty);
2016-07-29 13:02:49 -07:00
}
else
{
temp = _reader.ReadElementContentAsString(element, SerializationStrings.MonadNamespace);
}
2016-07-29 13:02:49 -07:00
_reader.MoveToContent();
temp = DecodeString(temp);
return temp;
}
2016-07-29 13:02:49 -07:00
/// <summary>
/// Skips an element and all its child elements.
2016-07-29 13:02:49 -07:00
/// Moves cursor to next content Node.
/// </summary>
private void Skip()
{
_reader.Skip();
_reader.MoveToContent();
}
2016-07-29 13:02:49 -07:00
/// <summary>
/// Reads Primary known type.
2016-07-29 13:02:49 -07:00
/// </summary>
/// <param name="pktInfo"></param>
/// <returns></returns>
private object ReadPrimaryKnownType(TypeSerializationInfo pktInfo)
{
Dbg.Assert(pktInfo != null, "Deserializer should be available");
Dbg.Assert(pktInfo.Deserializer != null, "Deserializer should be available");
object result = pktInfo.Deserializer(this);
_reader.MoveToContent();
return result;
}
2016-07-29 13:02:49 -07:00
private object ReadSecureString()
{
string encryptedString = _reader.ReadElementContentAsString();
2016-07-29 13:02:49 -07:00
try
{
object result;
if (_context.cryptoHelper != null)
{
result = _context.cryptoHelper.DecryptSecureString(encryptedString);
}
else
{
result = Microsoft.PowerShell.SecureStringHelper.Unprotect(encryptedString);
}
2016-07-29 13:02:49 -07:00
_reader.MoveToContent();
return result;
}
catch (PSCryptoException)
{
throw NewXmlException(Serialization.DeserializeSecureStringFailed, null);
}
}
2016-07-29 13:02:49 -07:00
/// <summary>
/// Helper function for building XmlException.
2016-07-29 13:02:49 -07:00
/// </summary>
/// <param name="resourceString">
2017-01-16 13:31:14 -08:00
/// resource String
2016-07-29 13:02:49 -07:00
/// </param>
/// <param name="innerException"></param>
/// <param name="args">
/// params for format string obtained from resourceId
/// </param>
private XmlException NewXmlException
(
string resourceString,
Exception innerException,
params object[] args
)
{
Dbg.Assert(!string.IsNullOrEmpty(resourceString), "Caller should validate the parameter");
2016-07-29 13:02:49 -07:00
string message = StringUtil.Format(resourceString, args);
2016-07-29 13:02:49 -07:00
XmlException ex = null;
IXmlLineInfo xmlLineInfo = _reader as IXmlLineInfo;
2016-07-29 13:02:49 -07:00
if (xmlLineInfo != null)
{
if (xmlLineInfo.HasLineInfo())
{
ex = new XmlException
(
message,
innerException,
xmlLineInfo.LineNumber,
xmlLineInfo.LinePosition
);
}
}
2016-08-02 22:25:13 -07:00
return ex ?? new XmlException(message, innerException);
2016-07-29 13:02:49 -07:00
}
2016-07-29 13:02:49 -07:00
private string ReadNameAttribute()
{
string encodedName = _reader.GetAttribute(SerializationStrings.NameAttribute);
if (encodedName == null)
2016-07-29 13:02:49 -07:00
{
throw NewXmlException(Serialization.AttributeExpected, null, SerializationStrings.NameAttribute);
}
2016-07-29 13:02:49 -07:00
return DecodeString(encodedName);
}
2016-07-29 13:02:49 -07:00
private static string DecodeString(string s)
{
Dbg.Assert(s != null, "Caller should validate the parameter");
return XmlConvert.DecodeName(s);
}
2016-07-29 13:02:49 -07:00
#endregion misc
2016-07-29 13:02:49 -07:00
[TraceSourceAttribute("InternalDeserializer", "InternalDeserializer class")]
private static readonly PSTraceSource s_trace = PSTraceSource.GetTracer("InternalDeserializer", "InternalDeserializer class");
}
2016-07-29 13:02:49 -07:00
/// <summary>
/// Helper class for generating reference id.
/// </summary>
internal class ReferenceIdHandlerForSerializer<T> where T : class
{
/// <summary>
/// Get new reference id.
/// </summary>
/// <returns>New reference id.</returns>
2016-07-29 13:02:49 -07:00
private UInt64 GetNewReferenceId()
{
UInt64 refId = _seed++;
return refId;
}
2016-07-29 13:02:49 -07:00
/// <summary>
/// Seed is incremented by one after each reference generation.
2016-07-29 13:02:49 -07:00
/// </summary>
private UInt64 _seed;
2016-07-29 13:02:49 -07:00
// note:
// any boxed UInt64 takes 16 bytes on the heap
// one-character string (i.e. "7") takes 20 bytes on the heap
private readonly IDictionary<T, UInt64> _object2refId;
2016-07-29 13:02:49 -07:00
internal ReferenceIdHandlerForSerializer(IDictionary<T, UInt64> dictionary)
{
_object2refId = dictionary;
}
2016-07-29 13:02:49 -07:00
/// <summary>
/// Assigns a RefId to the given object.
2016-07-29 13:02:49 -07:00
/// </summary>
/// <param name="t">Object to assign a RefId to.</param>
/// <returns>RefId assigned to the object.</returns>
2016-07-29 13:02:49 -07:00
internal string SetRefId(T t)
{
if (_object2refId != null)
{
Dbg.Assert(!_object2refId.ContainsKey(t), "SetRefId shouldn't be called when the object is already assigned a ref id");
UInt64 refId = GetNewReferenceId();
_object2refId.Add(t, refId);
return refId.ToString(System.Globalization.CultureInfo.InvariantCulture);
}
else
{
return null;
}
}
2016-07-29 13:02:49 -07:00
/// <summary>
2020-11-24 04:35:46 +00:00
/// Gets a RefId already assigned for the given object or <see langword="null"/> if there is no associated ref id.
2016-07-29 13:02:49 -07:00
/// </summary>
/// <param name="t"></param>
/// <returns></returns>
internal string GetRefId(T t)
{
UInt64 refId;
if ((_object2refId != null) && (_object2refId.TryGetValue(t, out refId)))
{
return refId.ToString(System.Globalization.CultureInfo.InvariantCulture);
}
else
{
return null;
}
}
}
2016-07-29 13:02:49 -07:00
internal class ReferenceIdHandlerForDeserializer<T> where T : class
{
private readonly Dictionary<string, T> _refId2object = new Dictionary<string, T>();
2016-07-29 13:02:49 -07:00
internal void SetRefId(T o, string refId, bool duplicateRefIdsAllowed)
{
#if DEBUG
2016-07-29 13:02:49 -07:00
if (!duplicateRefIdsAllowed)
{
Dbg.Assert(!_refId2object.ContainsKey(refId), "You can't change refId association");
}
#endif
2016-07-29 13:02:49 -07:00
_refId2object[refId] = o;
}
2016-07-29 13:02:49 -07:00
internal T GetReferencedObject(string refId)
{
Dbg.Assert(_refId2object.ContainsKey(refId), "Reference id wasn't seen earlier");
T t;
if (_refId2object.TryGetValue(refId, out t))
{
return t;
}
else
{
return null;
}
}
}
2016-07-29 13:02:49 -07:00
/// <summary>
/// A delegate for serializing known type.
2016-07-29 13:02:49 -07:00
/// </summary>
internal delegate void TypeSerializerDelegate(InternalSerializer serializer, string streamName, string property, object source, TypeSerializationInfo entry);
/// <summary>
/// A delegate for deserializing known type.
2016-07-29 13:02:49 -07:00
/// </summary>
internal delegate object TypeDeserializerDelegate(InternalDeserializer deserializer);
2016-07-29 13:02:49 -07:00
/// <summary>
/// This class contains serialization information about a type.
/// </summary>
internal class TypeSerializationInfo
{
/// <summary>
/// Constructor.
2016-07-29 13:02:49 -07:00
/// </summary>
/// <param name="type">Type for which this entry is created.</param>
/// <param name="itemTag">ItemTag for the type.</param>
/// <param name="propertyTag">PropertyTag for the type.</param>
/// <param name="serializer">TypeSerializerDelegate for serializing the type.</param>
/// <param name="deserializer">TypeDeserializerDelegate for deserializing the type.</param>
2016-07-29 13:02:49 -07:00
internal TypeSerializationInfo(Type type, string itemTag, string propertyTag, TypeSerializerDelegate serializer, TypeDeserializerDelegate deserializer)
{
2016-08-02 13:29:09 -07:00
Type = type;
Serializer = serializer;
Deserializer = deserializer;
ItemTag = itemTag;
PropertyTag = propertyTag;
2016-07-29 13:02:49 -07:00
}
2016-07-29 13:02:49 -07:00
#region properties
2016-07-29 13:02:49 -07:00
/// <summary>
/// Get the type for which this TypeSerializationInfo is created.
/// </summary>
2016-08-02 13:29:09 -07:00
internal Type Type { get; }
2016-07-29 13:02:49 -07:00
/// <summary>
/// Get the item tag for this type.
2016-07-29 13:02:49 -07:00
/// </summary>
2016-08-02 13:29:09 -07:00
internal string ItemTag { get; }
2016-07-29 13:02:49 -07:00
/// <summary>
/// Get the Property tag for this type.
2016-07-29 13:02:49 -07:00
/// </summary>
2016-08-02 13:29:09 -07:00
internal string PropertyTag { get; }
2016-07-29 13:02:49 -07:00
/// <summary>
/// Gets the delegate to serialize this type.
2016-07-29 13:02:49 -07:00
/// </summary>
2016-08-02 13:29:09 -07:00
internal TypeSerializerDelegate Serializer { get; }
2016-07-29 13:02:49 -07:00
/// <summary>
/// Gets the delegate to deserialize this type.
2016-07-29 13:02:49 -07:00
/// </summary>
2016-08-02 13:29:09 -07:00
internal TypeDeserializerDelegate Deserializer { get; }
2016-07-29 13:02:49 -07:00
#endregion properties
2016-07-29 13:02:49 -07:00
#region private
2016-08-02 13:29:09 -07:00
2016-07-29 13:02:49 -07:00
#endregion private
}
2016-07-29 13:02:49 -07:00
/// <summary>
/// A class for identifying types which are treated as KnownType by Monad.
2017-01-16 13:31:14 -08:00
/// A KnownType is guranteed to be available on machine on which monad is
2016-07-29 13:02:49 -07:00
/// running.
/// </summary>
internal static class KnownTypes
{
/// <summary>
/// Static constructor.
2016-07-29 13:02:49 -07:00
/// </summary>
static KnownTypes()
{
for (int i = 0; i < s_typeSerializationInfo.Length; i++)
{
s_knownTableKeyType.Add(s_typeSerializationInfo[i].Type.FullName, s_typeSerializationInfo[i]);
s_knownTableKeyItemTag.Add(s_typeSerializationInfo[i].ItemTag, s_typeSerializationInfo[i]);
}
}
2016-07-29 13:02:49 -07:00
/// <summary>
/// Gets the type serialization information about a type.
2016-07-29 13:02:49 -07:00
/// </summary>
/// <param name="type">Type for which information is retrieved.</param>
/// <returns>TypeSerializationInfo for the type, null if it doesn't exist.</returns>
2016-07-29 13:02:49 -07:00
internal static TypeSerializationInfo GetTypeSerializationInfo(Type type)
{
TypeSerializationInfo temp;
if (!s_knownTableKeyType.TryGetValue(type.FullName, out temp) && typeof(XmlDocument).IsAssignableFrom(type))
{
temp = s_xdInfo;
}
2016-07-29 13:02:49 -07:00
return temp;
}
2016-07-29 13:02:49 -07:00
/// <summary>
/// Get TypeSerializationInfo using ItemTag as key.
2016-07-29 13:02:49 -07:00
/// </summary>
/// <param name="itemTag">ItemTag for which TypeSerializationInfo is to be fetched.</param>
/// <returns>TypeSerializationInfo entry, null if no entry exist for the tag.</returns>
2016-07-29 13:02:49 -07:00
internal static TypeSerializationInfo GetTypeSerializationInfoFromItemTag(string itemTag)
{
TypeSerializationInfo temp;
s_knownTableKeyItemTag.TryGetValue(itemTag, out temp);
return temp;
}
2016-07-29 13:02:49 -07:00
#region private_fields
// TypeSerializationInfo for XmlDocument
2016-07-29 13:02:49 -07:00
private static readonly TypeSerializationInfo s_xdInfo =
new TypeSerializationInfo(typeof(XmlDocument),
SerializationStrings.XmlDocumentTag,
SerializationStrings.XmlDocumentTag,
InternalSerializer.WriteXmlDocument,
InternalDeserializer.DeserializeXmlDocument);
2016-07-29 13:02:49 -07:00
/// <summary>
/// Array of known types.
/// </summary>
private static readonly TypeSerializationInfo[] s_typeSerializationInfo = new TypeSerializationInfo[]
{
new TypeSerializationInfo(typeof(bool),
2016-07-29 13:02:49 -07:00
SerializationStrings.BooleanTag,
SerializationStrings.BooleanTag,
InternalSerializer.WriteBoolean,
InternalDeserializer.DeserializeBoolean),
new TypeSerializationInfo(typeof(byte),
2016-07-29 13:02:49 -07:00
SerializationStrings.UnsignedByteTag,
SerializationStrings.UnsignedByteTag,
null,
InternalDeserializer.DeserializeByte),
new TypeSerializationInfo(typeof(char),
2016-07-29 13:02:49 -07:00
SerializationStrings.CharTag,
SerializationStrings.CharTag,
InternalSerializer.WriteChar,
InternalDeserializer.DeserializeChar),
2016-07-29 13:02:49 -07:00
new TypeSerializationInfo(typeof(DateTime),
SerializationStrings.DateTimeTag,
SerializationStrings.DateTimeTag,
InternalSerializer.WriteDateTime,
InternalDeserializer.DeserializeDateTime),
new TypeSerializationInfo(typeof(decimal),
2016-07-29 13:02:49 -07:00
SerializationStrings.DecimalTag,
SerializationStrings.DecimalTag,
null,
InternalDeserializer.DeserializeDecimal),
new TypeSerializationInfo(typeof(double),
2016-07-29 13:02:49 -07:00
SerializationStrings.DoubleTag,
SerializationStrings.DoubleTag,
InternalSerializer.WriteDouble,
InternalDeserializer.DeserializeDouble),
2016-07-29 13:02:49 -07:00
new TypeSerializationInfo(typeof(Guid),
SerializationStrings.GuidTag,
SerializationStrings.GuidTag,
null,
InternalDeserializer.DeserializeGuid),
new TypeSerializationInfo(typeof(Int16),
SerializationStrings.ShortTag,
SerializationStrings.ShortTag,
null,
InternalDeserializer.DeserializeInt16),
2016-07-29 13:02:49 -07:00
new TypeSerializationInfo(typeof(Int32),
SerializationStrings.IntTag,
SerializationStrings.IntTag,
null,
InternalDeserializer.DeserializeInt32),
2016-07-29 13:02:49 -07:00
new TypeSerializationInfo(typeof(Int64),
SerializationStrings.LongTag,
SerializationStrings.LongTag,
null,
InternalDeserializer.DeserializeInt64),
new TypeSerializationInfo(typeof(sbyte),
2016-07-29 13:02:49 -07:00
SerializationStrings.ByteTag,
SerializationStrings.ByteTag,
null,
InternalDeserializer.DeserializeSByte),
2016-07-29 13:02:49 -07:00
new TypeSerializationInfo(typeof(Single),
SerializationStrings.FloatTag,
SerializationStrings.FloatTag,
InternalSerializer.WriteSingle,
InternalDeserializer.DeserializeSingle),
2016-07-29 13:02:49 -07:00
new TypeSerializationInfo(typeof(ScriptBlock),
SerializationStrings.ScriptBlockTag,
SerializationStrings.ScriptBlockTag,
InternalSerializer.WriteScriptBlock,
InternalDeserializer.DeserializeScriptBlock),
new TypeSerializationInfo(typeof(string),
2016-07-29 13:02:49 -07:00
SerializationStrings.StringTag,
SerializationStrings.StringTag,
InternalSerializer.WriteEncodedString,
InternalDeserializer.DeserializeString),
2016-07-29 13:02:49 -07:00
new TypeSerializationInfo(typeof(TimeSpan),
SerializationStrings.DurationTag,
SerializationStrings.DurationTag,
InternalSerializer.WriteTimeSpan,
InternalDeserializer.DeserializeTimeSpan),
2016-07-29 13:02:49 -07:00
new TypeSerializationInfo(typeof(UInt16),
SerializationStrings.UnsignedShortTag,
SerializationStrings.UnsignedShortTag,
null,
InternalDeserializer.DeserializeUInt16),
2016-07-29 13:02:49 -07:00
new TypeSerializationInfo(typeof(UInt32),
SerializationStrings.UnsignedIntTag,
SerializationStrings.UnsignedIntTag,
null,
InternalDeserializer.DeserializeUInt32),
2016-07-29 13:02:49 -07:00
new TypeSerializationInfo(typeof(UInt64),
SerializationStrings.UnsignedLongTag,
SerializationStrings.UnsignedLongTag,
null,
InternalDeserializer.DeserializeUInt64),
2016-07-29 13:02:49 -07:00
new TypeSerializationInfo(typeof(Uri),
SerializationStrings.AnyUriTag,
SerializationStrings.AnyUriTag,
InternalSerializer.WriteUri,
InternalDeserializer.DeserializeUri),
2016-07-29 13:02:49 -07:00
new TypeSerializationInfo(typeof(byte[]),
SerializationStrings.Base64BinaryTag,
SerializationStrings.Base64BinaryTag,
InternalSerializer.WriteByteArray,
InternalDeserializer.DeserializeByteArray),
2016-07-29 13:02:49 -07:00
new TypeSerializationInfo(typeof(System.Version),
SerializationStrings.VersionTag,
SerializationStrings.VersionTag,
InternalSerializer.WriteVersion,
InternalDeserializer.DeserializeVersion),
2016-07-29 13:02:49 -07:00
s_xdInfo,
2016-07-29 13:02:49 -07:00
new TypeSerializationInfo(typeof(ProgressRecord),
SerializationStrings.ProgressRecord,
SerializationStrings.ProgressRecord,
InternalSerializer.WriteProgressRecord,
InternalDeserializer.DeserializeProgressRecord),
2016-07-29 13:02:49 -07:00
new TypeSerializationInfo(typeof(SecureString),
SerializationStrings.SecureStringTag,
SerializationStrings.SecureStringTag,
InternalSerializer.WriteSecureString,
InternalDeserializer.DeserializeSecureString),
};
2016-07-29 13:02:49 -07:00
/// <summary>
2017-01-16 13:31:14 -08:00
/// Hashtable of knowntypes.
2016-07-29 13:02:49 -07:00
/// Key is Type.FullName and value is Type object.
/// </summary>
private static readonly Dictionary<string, TypeSerializationInfo> s_knownTableKeyType = new Dictionary<string, TypeSerializationInfo>();
2016-07-29 13:02:49 -07:00
/// <summary>
/// Hashtable of knowntypes. Key is ItemTag.
2016-07-29 13:02:49 -07:00
/// </summary>
private static readonly Dictionary<string, TypeSerializationInfo> s_knownTableKeyItemTag = new Dictionary<string, TypeSerializationInfo>();
2016-07-29 13:02:49 -07:00
#endregion private_fields
}
2016-07-29 13:02:49 -07:00
/// <summary>
/// This class contains helper routined for serialization/deserialization.
2016-07-29 13:02:49 -07:00
/// </summary>
internal static class SerializationUtilities
{
/// <summary>
/// Extracts the value of a note property from a PSObject; returns null if the property does not exist.
2016-07-29 13:02:49 -07:00
/// </summary>
internal static object GetPropertyValue(PSObject psObject, string propertyName)
{
PSNoteProperty property = (PSNoteProperty)psObject.Properties[propertyName];
if (property == null)
2016-07-29 13:02:49 -07:00
{
return null;
}
2016-07-29 13:02:49 -07:00
return property.Value;
}
2016-07-29 13:02:49 -07:00
/// <summary>
/// Returns the BaseObject of a note property encoded as a PSObject; returns null if the property does not exist.
2016-07-29 13:02:49 -07:00
/// </summary>
internal static object GetPsObjectPropertyBaseObject(PSObject psObject, string propertyName)
{
PSObject propertyPsObject = (PSObject)GetPropertyValue(psObject, propertyName);
if (propertyPsObject == null)
2016-07-29 13:02:49 -07:00
{
return null;
}
2016-07-29 13:02:49 -07:00
return propertyPsObject.BaseObject;
}
/// <summary>
2017-01-16 13:31:14 -08:00
/// Checks if source is known container type and returns appropriate
/// information.
/// </summary>
/// <param name="source"></param>
/// <param name="ct"></param>
/// <param name="dictionary"></param>
/// <param name="enumerable"></param>
internal static void GetKnownContainerTypeInfo(
object source,
out ContainerType ct,
out IDictionary dictionary,
out IEnumerable enumerable)
{
Dbg.Assert(source != null, "caller should validate the parameter");
ct = ContainerType.None;
dictionary = null;
enumerable = null;
dictionary = source as IDictionary;
if (dictionary != null)
{
ct = ContainerType.Dictionary;
}
else if (source is Stack)
{
ct = ContainerType.Stack;
enumerable = LanguagePrimitives.GetEnumerable(source);
Dbg.Assert(enumerable != null, "Stack is enumerable");
}
else if (source is Queue)
{
ct = ContainerType.Queue;
enumerable = LanguagePrimitives.GetEnumerable(source);
Dbg.Assert(enumerable != null, "Queue is enumerable");
}
else if (source is IList)
{
ct = ContainerType.List;
enumerable = LanguagePrimitives.GetEnumerable(source);
Dbg.Assert(enumerable != null, "IList is enumerable");
}
else
{
Type gt = source.GetType();
if (gt.IsGenericType)
{
if (DerivesFromGenericType(gt, typeof(Stack<>)))
{
ct = ContainerType.Stack;
enumerable = LanguagePrimitives.GetEnumerable(source);
Dbg.Assert(enumerable != null, "Stack is enumerable");
}
else if (DerivesFromGenericType(gt, typeof(Queue<>)))
{
ct = ContainerType.Queue;
enumerable = LanguagePrimitives.GetEnumerable(source);
Dbg.Assert(enumerable != null, "Queue is enumerable");
}
else if (DerivesFromGenericType(gt, typeof(List<>)))
{
ct = ContainerType.List;
enumerable = LanguagePrimitives.GetEnumerable(source);
Dbg.Assert(enumerable != null, "Queue is enumerable");
}
}
}
// Check if LanguagePrimitive.GetEnumerable can do some magic to get IEnumerable instance
if (ct == ContainerType.None)
{
try
{
enumerable = LanguagePrimitives.GetEnumerable(source);
if (enumerable != null)
{
ct = ContainerType.Enumerable;
}
}
catch (Exception exception)
{
// Catch-all OK. This is a third-party call-out.
PSEtwLog.LogAnalyticWarning(PSEventId.Serializer_EnumerationFailed, PSOpcode.Exception,
PSTask.Serialization, PSKeyword.Serializer, source.GetType().AssemblyQualifiedName,
exception.ToString());
}
}
// Check if type is IEnumerable
// (LanguagePrimitives.GetEnumerable above should be enough - the check below is to preserve
// backcompatibility in some corner-cases (see bugs in Windows7 - #372562 and #372563))
if (ct == ContainerType.None)
{
enumerable = source as IEnumerable;
if (enumerable != null)
{
// WinBlue: 206515 - There are no elements in the source. The source is of type XmlLinkedNode (which derives from XmlNode which implements IEnumerable).
// So, adding an additional check to see if this contains any elements
IEnumerator enumerator = enumerable.GetEnumerator();
if (enumerator != null && enumerator.MoveNext())
{
ct = ContainerType.Enumerable;
}
}
}
}
/// <summary>
/// Checks if derived is of type baseType or a type derived from baseType.
/// </summary>
/// <param name="derived"></param>
/// <param name="baseType"></param>
/// <returns></returns>
private static bool DerivesFromGenericType(Type derived, Type baseType)
{
Dbg.Assert(derived != null, "caller should validate the parameter");
Dbg.Assert(baseType != null, "caller should validate the parameter");
while (derived != null)
{
if (derived.IsGenericType)
derived = derived.GetGenericTypeDefinition();
if (derived == baseType)
{
return true;
}
derived = derived.BaseType;
}
return false;
}
/// <summary>
/// Gets the "ToString" from PSObject.
/// </summary>
/// <param name="source">
/// PSObject to be converted to string
/// </param>
/// <returns>
/// "ToString" value
/// </returns>
internal static string GetToString(object source)
{
Dbg.Assert(source != null, "caller should have validated the information");
// fall back value
string result = null;
try
{
result = Convert.ToString(source, CultureInfo.InvariantCulture);
}
catch (Exception e)
{
// Catch-all OK. This is a third-party call-out.
PSEtwLog.LogAnalyticWarning(
PSEventId.Serializer_ToStringFailed, PSOpcode.Exception, PSTask.Serialization,
PSKeyword.Serializer | PSKeyword.UseAlwaysAnalytic,
source.GetType().AssemblyQualifiedName,
e.ToString());
}
return result;
}
internal static string GetToStringForPrimitiveObject(PSObject pso)
{
// if object is not wrapped in a PSObject, then nothing modifies the ToString value of the primitive object
if (pso == null)
{
return null;
}
// preserve ToString throughout deserialization/*re*serialization
if (pso.ToStringFromDeserialization != null)
{
return pso.ToStringFromDeserialization;
}
// preserve token text (i.e. double: 0E1517567410; see Windows 7 bug #694057 for more details)
string token = pso.TokenText;
if (token != null)
{
string originalToString = GetToString(pso.BaseObject);
if (originalToString == null || !string.Equals(token, originalToString, StringComparison.Ordinal))
{
return token;
}
}
// no need to write <ToString> element otherwise - the ToString method of a deserialized, live primitive object will return the right value
return null;
}
internal static PSMemberInfoInternalCollection<PSPropertyInfo> GetSpecificPropertiesToSerialize(PSObject source, Collection<CollectionEntry<PSPropertyInfo>> allPropertiesCollection, TypeTable typeTable)
{
if (source == null)
{
return null;
}
if (source.GetSerializationMethod(typeTable) == SerializationMethod.SpecificProperties)
{
PSEtwLog.LogAnalyticVerbose(
PSEventId.Serializer_ModeOverride, PSOpcode.SerializationSettings, PSTask.Serialization,
PSKeyword.Serializer | PSKeyword.UseAlwaysAnalytic,
source.InternalTypeNames.Key,
(UInt32)(SerializationMethod.SpecificProperties));
PSMemberInfoInternalCollection<PSPropertyInfo> specificProperties =
new PSMemberInfoInternalCollection<PSPropertyInfo>();
PSMemberInfoIntegratingCollection<PSPropertyInfo> allProperties =
new PSMemberInfoIntegratingCollection<PSPropertyInfo>(
source,
allPropertiesCollection);
Collection<string> namesOfPropertiesToSerialize = source.GetSpecificPropertiesToSerialize(typeTable);
foreach (string propertyName in namesOfPropertiesToSerialize)
{
PSPropertyInfo property = allProperties[propertyName];
if (property == null)
{
PSEtwLog.LogAnalyticWarning(
PSEventId.Serializer_SpecificPropertyMissing, PSOpcode.Exception, PSTask.Serialization,
PSKeyword.Serializer | PSKeyword.UseAlwaysAnalytic,
source.InternalTypeNames.Key,
propertyName);
}
else
{
specificProperties.Add(property);
}
}
return specificProperties;
}
return null;
}
internal static object GetPropertyValueInThreadSafeManner(PSPropertyInfo property, bool canUseDefaultRunspaceInThreadSafeManner, out bool success)
{
Dbg.Assert(property != null, "Caller should validate the parameter");
if (!property.IsGettable)
{
success = false;
return null;
}
PSAliasProperty alias = property as PSAliasProperty;
if (alias != null)
{
property = alias.ReferencedMember as PSPropertyInfo;
}
PSScriptProperty script = property as PSScriptProperty;
Dbg.Assert(script == null || script.GetterScript != null, "scriptProperty.IsGettable => (scriptProperty.GetterScript != null)");
if ((script != null) && (!canUseDefaultRunspaceInThreadSafeManner))
{
PSEtwLog.LogAnalyticWarning(
PSEventId.Serializer_ScriptPropertyWithoutRunspace, PSOpcode.Exception, PSTask.Serialization,
PSKeyword.Serializer | PSKeyword.UseAlwaysAnalytic,
property.Name,
property.instance == null ? string.Empty : PSObject.GetTypeNames(property.instance).Key,
script.GetterScript.ToString());
success = false;
return null;
}
try
{
object value = property.Value;
success = true;
return value;
}
catch (ExtendedTypeSystemException e)
{
PSEtwLog.LogAnalyticWarning(
PSEventId.Serializer_PropertyGetterFailed, PSOpcode.Exception, PSTask.Serialization,
PSKeyword.Serializer | PSKeyword.UseAlwaysAnalytic,
property.Name,
property.instance == null ? string.Empty : PSObject.GetTypeNames(property.instance).Key,
e.ToString(),
e.InnerException == null ? string.Empty : e.InnerException.ToString());
success = false;
return null;
}
}
2016-07-29 13:02:49 -07:00
}
2016-07-29 13:02:49 -07:00
/// <summary>
/// A dictionary from object to T where
2017-01-16 13:31:14 -08:00
/// 1) keys are objects,
/// 2) keys use reference equality,
/// 3) dictionary keeps only weak references to keys.
2016-07-29 13:02:49 -07:00
/// </summary>
/// <typeparam name="T">type of dictionary values</typeparam>
internal class WeakReferenceDictionary<T> : IDictionary<object, T>
{
private class WeakReferenceEqualityComparer : IEqualityComparer<WeakReference>
{
public bool Equals(WeakReference x, WeakReference y)
{
object tx = x.Target;
if (tx == null)
2016-07-29 13:02:49 -07:00
{
return false; // collected object is not equal to anything (object.ReferenceEquals(null, null) == true)
}
2016-07-29 13:02:49 -07:00
object ty = y.Target;
if (ty == null)
2016-07-29 13:02:49 -07:00
{
return false; // collected object is not equal to anything (object.ReferenceEquals(null, null) == true)
}
2016-07-29 13:02:49 -07:00
return object.ReferenceEquals(tx, ty);
}
2016-07-29 13:02:49 -07:00
public int GetHashCode(WeakReference obj)
{
object t = obj.Target;
if (t == null)
2016-07-29 13:02:49 -07:00
{
// collected object doesn't have a hash code
// return an arbitrary hashcode here and fall back on Equal method for comparison
return RuntimeHelpers.GetHashCode(obj); // RuntimeHelpers.GetHashCode(null) returns 0 - this would cause many hashtable collisions for WeakReferences to dead objects
2016-07-29 13:02:49 -07:00
}
else
{
return RuntimeHelpers.GetHashCode(t);
}
}
}
2016-07-29 13:02:49 -07:00
private readonly IEqualityComparer<WeakReference> _weakEqualityComparer;
private Dictionary<WeakReference, T> _dictionary;
2016-07-29 13:02:49 -07:00
public WeakReferenceDictionary()
{
_weakEqualityComparer = new WeakReferenceEqualityComparer();
_dictionary = new Dictionary<WeakReference, T>(_weakEqualityComparer);
}
#if DEBUG
2016-07-29 13:02:49 -07:00
private const int initialCleanupTriggerSize = 2; // 2 will stress this code more
#else
2016-07-29 13:02:49 -07:00
private const int initialCleanupTriggerSize = 1000;
#endif
2016-07-29 13:02:49 -07:00
private int _cleanupTriggerSize = initialCleanupTriggerSize;
2016-07-29 13:02:49 -07:00
private void CleanUp()
{
if (this.Count > _cleanupTriggerSize)
{
Dictionary<WeakReference, T> alive = new Dictionary<WeakReference, T>(_weakEqualityComparer);
foreach (KeyValuePair<WeakReference, T> weakKeyValuePair in _dictionary)
{
object key = weakKeyValuePair.Key.Target;
if (key != null)
{
alive.Add(weakKeyValuePair.Key, weakKeyValuePair.Value);
}
}
2016-07-29 13:02:49 -07:00
_dictionary = alive;
_cleanupTriggerSize = initialCleanupTriggerSize + this.Count * 2;
}
}
2016-07-29 13:02:49 -07:00
#region IDictionary<object,T> Members
2016-07-29 13:02:49 -07:00
public void Add(object key, T value)
{
_dictionary.Add(new WeakReference(key), value);
this.CleanUp();
}
2016-07-29 13:02:49 -07:00
public bool ContainsKey(object key)
{
return _dictionary.ContainsKey(new WeakReference(key));
}
2016-07-29 13:02:49 -07:00
public ICollection<object> Keys
{
get
{
List<object> keys = new List<object>(_dictionary.Keys.Count);
foreach (WeakReference weakKey in _dictionary.Keys)
{
object key = weakKey.Target;
if (key != null)
{
keys.Add(key);
}
}
2016-07-29 13:02:49 -07:00
return keys;
}
}
2016-07-29 13:02:49 -07:00
public bool Remove(object key)
{
return _dictionary.Remove(new WeakReference(key));
}
2016-07-29 13:02:49 -07:00
public bool TryGetValue(object key, out T value)
{
WeakReference weakKey = new WeakReference(key);
return _dictionary.TryGetValue(weakKey, out value);
}
2016-07-29 13:02:49 -07:00
public ICollection<T> Values
{
get
{
return _dictionary.Values;
}
}
2016-07-29 13:02:49 -07:00
public T this[object key]
{
get
{
return _dictionary[new WeakReference(key)];
}
2016-07-29 13:02:49 -07:00
set
{
_dictionary[new WeakReference(key)] = value;
this.CleanUp();
}
}
2016-07-29 13:02:49 -07:00
#endregion
2016-07-29 13:02:49 -07:00
#region ICollection<KeyValuePair<object,T>> Members
2016-07-29 13:02:49 -07:00
private ICollection<KeyValuePair<WeakReference, T>> WeakCollection
{
get
{
return _dictionary;
}
}
private static KeyValuePair<WeakReference, T> WeakKeyValuePair(KeyValuePair<object, T> publicKeyValuePair)
2016-07-29 13:02:49 -07:00
{
return new KeyValuePair<WeakReference, T>(new WeakReference(publicKeyValuePair.Key), publicKeyValuePair.Value);
}
2016-07-29 13:02:49 -07:00
public void Add(KeyValuePair<object, T> item)
{
this.WeakCollection.Add(WeakKeyValuePair(item));
this.CleanUp();
}
2016-07-29 13:02:49 -07:00
public void Clear()
{
this.WeakCollection.Clear();
}
2016-07-29 13:02:49 -07:00
public bool Contains(KeyValuePair<object, T> item)
{
return this.WeakCollection.Contains(WeakKeyValuePair(item));
}
2016-07-29 13:02:49 -07:00
public void CopyTo(KeyValuePair<object, T>[] array, int arrayIndex)
{
List<KeyValuePair<object, T>> rawList = new List<KeyValuePair<object, T>>(this.WeakCollection.Count);
foreach (KeyValuePair<object, T> keyValuePair in this)
{
rawList.Add(keyValuePair);
}
2016-07-29 13:02:49 -07:00
rawList.CopyTo(array, arrayIndex);
}
2016-07-29 13:02:49 -07:00
public int Count
{
get
{
return this.WeakCollection.Count;
}
}
2016-07-29 13:02:49 -07:00
public bool IsReadOnly
{
get
{
return this.WeakCollection.IsReadOnly;
}
}
2016-07-29 13:02:49 -07:00
public bool Remove(KeyValuePair<object, T> item)
{
return this.WeakCollection.Remove(WeakKeyValuePair(item));
}
2016-07-29 13:02:49 -07:00
#endregion
2016-07-29 13:02:49 -07:00
#region IEnumerable<KeyValuePair<object,T>> Members
2016-07-29 13:02:49 -07:00
public IEnumerator<KeyValuePair<object, T>> GetEnumerator()
{
foreach (KeyValuePair<WeakReference, T> weakKeyValuePair in this.WeakCollection)
{
object key = weakKeyValuePair.Key.Target;
if (key != null)
{
yield return new KeyValuePair<object, T>(key, weakKeyValuePair.Value);
}
}
}
2016-07-29 13:02:49 -07:00
#endregion
2016-07-29 13:02:49 -07:00
#region IEnumerable Members
2016-07-29 13:02:49 -07:00
IEnumerator IEnumerable.GetEnumerator()
{
IEnumerable<KeyValuePair<object, T>> enumerable = this;
IEnumerator<KeyValuePair<object, T>> enumerator = enumerable.GetEnumerator();
return enumerator;
}
2016-07-29 13:02:49 -07:00
#endregion
}
2016-07-29 13:02:49 -07:00
/// <summary>
2017-01-16 13:31:14 -08:00
/// <see cref="PSPrimitiveDictionary"/> is a <see cref="Hashtable"/> that is limited to
/// 1) case-insensitive strings as keys and
/// 2) values that can be serialized and deserialized during PowerShell remoting handshake
2016-07-29 13:02:49 -07:00
/// (in major-version compatible versions of PowerShell remoting)
/// </summary>
[Serializable]
public sealed class PSPrimitiveDictionary : Hashtable
{
#region Constructors
2016-07-29 13:02:49 -07:00
/// <summary>
/// Initializes a new empty instance of the <see cref="PSPrimitiveDictionary"/> class.
2016-07-29 13:02:49 -07:00
/// </summary>
public PSPrimitiveDictionary()
: base(StringComparer.OrdinalIgnoreCase)
{
}
2016-07-29 13:02:49 -07:00
/// <summary>
/// Initializes a new instance of the <see cref="PSPrimitiveDictionary"/> class with contents
/// copied from the <paramref name="other"/> hashtable.
/// </summary>
/// <param name="other">Hashtable to copy into the new instance of <see cref="PSPrimitiveDictionary"/></param>
2016-07-29 13:02:49 -07:00
/// <exception cref="ArgumentException">
/// This constructor will throw if the <paramref name="other"/> hashtable contains keys that are not a strings
/// or values that are not one of primitive types that will work during PowerShell remoting handshake.
/// </exception>
[SuppressMessage("Microsoft.Usage", "CA2214:DoNotCallOverridableMethodsInConstructors", Justification = "The class is sealed")]
public PSPrimitiveDictionary(Hashtable other)
: base(StringComparer.OrdinalIgnoreCase)
{
if (other == null)
2016-07-29 13:02:49 -07:00
{
2020-05-19 19:57:52 +01:00
throw new ArgumentNullException(nameof(other));
2016-07-29 13:02:49 -07:00
}
2016-07-29 13:02:49 -07:00
foreach (DictionaryEntry entry in other)
{
Hashtable valueAsHashtable = PSObject.Base(entry.Value) as Hashtable;
if (valueAsHashtable != null)
{
this.Add(entry.Key, new PSPrimitiveDictionary(valueAsHashtable));
}
else
{
this.Add(entry.Key, entry.Value);
}
}
}
#if !CORECLR // No .NET Serialization In CoreCLR
2016-07-29 13:02:49 -07:00
/// <summary>
/// Support for .NET serialization.
2016-07-29 13:02:49 -07:00
/// </summary>
private PSPrimitiveDictionary(System.Runtime.Serialization.SerializationInfo info, System.Runtime.Serialization.StreamingContext context)
: base(info, context)
{
}
#endif
2016-07-29 13:02:49 -07:00
#endregion
2016-07-29 13:02:49 -07:00
#region Plumbing to make Hashtable reject all non-primitive types
private static string VerifyKey(object key)
2016-07-29 13:02:49 -07:00
{
key = PSObject.Base(key);
string keyAsString = key as string;
if (keyAsString == null)
2016-07-29 13:02:49 -07:00
{
string message = StringUtil.Format(Serialization.PrimitiveHashtableInvalidKey,
key.GetType().FullName);
throw new ArgumentException(message);
}
else
{
return keyAsString;
}
}
2016-07-29 13:02:49 -07:00
private static readonly Type[] s_handshakeFriendlyTypes = new Type[] {
typeof(bool),
typeof(byte),
typeof(char),
2016-07-29 13:02:49 -07:00
typeof(DateTime),
typeof(decimal),
typeof(double),
2016-07-29 13:02:49 -07:00
typeof(Guid),
typeof(Int32),
typeof(Int64),
typeof(sbyte),
2016-07-29 13:02:49 -07:00
typeof(Single),
// typeof(ScriptBlock) - don't want ScriptBlocks, because they are deserialized into strings
typeof(string),
2016-07-29 13:02:49 -07:00
typeof(TimeSpan),
typeof(UInt16),
typeof(UInt32),
typeof(UInt64),
typeof(Uri),
typeof(byte[]),
typeof(Version),
typeof(ProgressRecord),
typeof(XmlDocument),
2016-07-29 13:02:49 -07:00
typeof(PSPrimitiveDictionary)
};
2016-07-29 13:02:49 -07:00
private void VerifyValue(object value)
{
// null is a primitive type
if (value == null)
2016-07-29 13:02:49 -07:00
{
return;
}
2016-07-29 13:02:49 -07:00
value = PSObject.Base(value);
2016-07-29 13:02:49 -07:00
// this list is based on the list inside KnownTypes
// it is copied here to make sure that a list of "handshake friendly types"
// won't change even if we add new primitive types in v.next
2016-07-29 13:02:49 -07:00
Type valueType = value.GetType();
2016-07-29 13:02:49 -07:00
// if "value" is a "primitiveType" then we are good
foreach (Type goodType in s_handshakeFriendlyTypes)
{
if (valueType == goodType)
{
return;
}
}
2016-07-29 13:02:49 -07:00
// if "value" is an array of "primitiveType" items then we are good
// (note: we could have used IEnumerable<> or ICollection<> (covariance/contravariance concerns),
// but it is safer to limit the types to arrays.
// (one concern is that in v.next we might allow overriding SerializationMethod for
// types [i.e. check SerializationMethod *before* HandleKnownContainerTypes)
if ((valueType.IsArray) || valueType == typeof(ArrayList))
2016-07-29 13:02:49 -07:00
{
foreach (object o in (IEnumerable)value)
{
VerifyValue(o);
}
2016-07-29 13:02:49 -07:00
return;
}
2016-07-29 13:02:49 -07:00
string message = StringUtil.Format(Serialization.PrimitiveHashtableInvalidValue,
value.GetType().FullName);
throw new ArgumentException(message);
}
2016-07-29 13:02:49 -07:00
/// <summary>
/// Adds an element with the specified key and value into the Hashtable.
2016-07-29 13:02:49 -07:00
/// </summary>
/// <param name="key">The key of the element to add.</param>
/// <param name="value">The value of the element to add.</param>
2016-07-29 13:02:49 -07:00
/// <exception cref="ArgumentException">
/// This method will throw if the <paramref name="key"/> is not a string and the <paramref name="value"/>
/// is not one of primitive types that will work during PowerShell remoting handshake.
/// Use of strongly-typed overloads of this method is suggested if throwing an exception is not acceptable.
/// </exception>
public override void Add(object key, object value)
{
string keyAsString = VerifyKey(key);
2016-07-29 13:02:49 -07:00
this.VerifyValue(value);
base.Add(keyAsString, value);
}
2016-07-29 13:02:49 -07:00
/// <summary>
2017-01-16 13:31:14 -08:00
/// Gets or sets the value associated with the specified key.
2016-07-29 13:02:49 -07:00
/// </summary>
/// <param name="key">The key whose value to get or set.</param>
2016-07-29 13:02:49 -07:00
/// <returns>The value associated with the specified key.</returns>
/// <remarks>
2020-11-24 04:35:46 +00:00
/// If the specified key is not found, attempting to get it returns <see langword="null"/>
2016-07-29 13:02:49 -07:00
/// and attempting to set it creates a new element using the specified key.
/// </remarks>
/// <exception cref="ArgumentException">
/// The setter will throw if the <paramref name="key"/> is not a string and the value
/// is not one of primitive types that will work during PowerShell remoting handshake.
/// Use of strongly-typed overloads of Add method is suggested if throwing an exception is not acceptable.
/// </exception>
public override object this[object key]
{
get
{
return base[key];
}
2016-07-29 13:02:49 -07:00
set
{
string keyAsString = VerifyKey(key);
2016-07-29 13:02:49 -07:00
this.VerifyValue(value);
base[keyAsString] = value;
}
}
2016-07-29 13:02:49 -07:00
/// <summary>
2017-01-16 13:31:14 -08:00
/// Gets or sets the value associated with the specified key.
2016-07-29 13:02:49 -07:00
/// </summary>
/// <param name="key">The key whose value to get or set.</param>
2016-07-29 13:02:49 -07:00
/// <returns>The value associated with the specified key.</returns>
/// <remarks>
2020-11-24 04:35:46 +00:00
/// If the specified key is not found, attempting to get it returns <see langword="null"/>
2016-07-29 13:02:49 -07:00
/// and attempting to set it creates a new element using the specified key.
/// </remarks>
/// <exception cref="ArgumentException">
/// The setter will throw if the value
/// is not one of primitive types that will work during PowerShell remoting handshake.
/// Use of strongly-typed overloads of Add method is suggested if throwing an exception is not acceptable.
/// </exception>
public object this[string key]
{
get
{
return base[key];
}
2016-07-29 13:02:49 -07:00
set
{
this.VerifyValue(value);
base[key] = value;
}
}
2016-07-29 13:02:49 -07:00
#endregion
2016-07-29 13:02:49 -07:00
#region Helper methods
2016-07-29 13:02:49 -07:00
/// <summary>
/// Creates a new instance by doing a shallow copy of the current instance.
/// </summary>
/// <returns></returns>
public override object Clone()
{
return new PSPrimitiveDictionary(this);
}
2016-07-29 13:02:49 -07:00
/// <summary>
/// Adds an element with the specified key and value into the Hashtable.
2016-07-29 13:02:49 -07:00
/// </summary>
/// <param name="key">The key of the element to add.</param>
/// <param name="value">The value of the element to add.</param>
public void Add(string key, bool value)
2016-07-29 13:02:49 -07:00
{
this.Add((object)key, (object)value);
}
2016-07-29 13:02:49 -07:00
/// <summary>
/// Adds an element with the specified key and value into the Hashtable.
2016-07-29 13:02:49 -07:00
/// </summary>
/// <param name="key">The key of the element to add.</param>
/// <param name="value">The value of the element to add.</param>
public void Add(string key, bool[] value)
2016-07-29 13:02:49 -07:00
{
this.Add((object)key, (object)value);
}
2016-07-29 13:02:49 -07:00
/// <summary>
/// Adds an element with the specified key and value into the Hashtable.
2016-07-29 13:02:49 -07:00
/// </summary>
/// <param name="key">The key of the element to add.</param>
/// <param name="value">The value of the element to add.</param>
public void Add(string key, byte value)
2016-07-29 13:02:49 -07:00
{
this.Add((object)key, (object)value);
}
2016-07-29 13:02:49 -07:00
/// <summary>
/// Adds an element with the specified key and value into the Hashtable.
2016-07-29 13:02:49 -07:00
/// </summary>
/// <param name="key">The key of the element to add.</param>
/// <param name="value">The value of the element to add.</param>
2016-07-29 13:02:49 -07:00
public void Add(string key, byte[] value)
{
this.Add((object)key, (object)value);
}
2016-07-29 13:02:49 -07:00
/// <summary>
/// Adds an element with the specified key and value into the Hashtable.
2016-07-29 13:02:49 -07:00
/// </summary>
/// <param name="key">The key of the element to add.</param>
/// <param name="value">The value of the element to add.</param>
public void Add(string key, char value)
2016-07-29 13:02:49 -07:00
{
this.Add((object)key, (object)value);
}
2016-07-29 13:02:49 -07:00
/// <summary>
/// Adds an element with the specified key and value into the Hashtable.
2016-07-29 13:02:49 -07:00
/// </summary>
/// <param name="key">The key of the element to add.</param>
/// <param name="value">The value of the element to add.</param>
public void Add(string key, char[] value)
2016-07-29 13:02:49 -07:00
{
this.Add((object)key, (object)value);
}
2016-07-29 13:02:49 -07:00
/// <summary>
/// Adds an element with the specified key and value into the Hashtable.
2016-07-29 13:02:49 -07:00
/// </summary>
/// <param name="key">The key of the element to add.</param>
/// <param name="value">The value of the element to add.</param>
2016-07-29 13:02:49 -07:00
public void Add(string key, DateTime value)
{
this.Add((object)key, (object)value);
}
2016-07-29 13:02:49 -07:00
/// <summary>
/// Adds an element with the specified key and value into the Hashtable.
2016-07-29 13:02:49 -07:00
/// </summary>
/// <param name="key">The key of the element to add.</param>
/// <param name="value">The value of the element to add.</param>
2016-07-29 13:02:49 -07:00
public void Add(string key, DateTime[] value)
{
this.Add((object)key, (object)value);
}
2016-07-29 13:02:49 -07:00
/// <summary>
/// Adds an element with the specified key and value into the Hashtable.
2016-07-29 13:02:49 -07:00
/// </summary>
/// <param name="key">The key of the element to add.</param>
/// <param name="value">The value of the element to add.</param>
2016-07-29 13:02:49 -07:00
public void Add(string key, Decimal value)
{
this.Add((object)key, (object)value);
}
2016-07-29 13:02:49 -07:00
/// <summary>
/// Adds an element with the specified key and value into the Hashtable.
2016-07-29 13:02:49 -07:00
/// </summary>
/// <param name="key">The key of the element to add.</param>
/// <param name="value">The value of the element to add.</param>
2016-07-29 13:02:49 -07:00
public void Add(string key, Decimal[] value)
{
this.Add((object)key, (object)value);
}
2016-07-29 13:02:49 -07:00
/// <summary>
/// Adds an element with the specified key and value into the Hashtable.
2016-07-29 13:02:49 -07:00
/// </summary>
/// <param name="key">The key of the element to add.</param>
/// <param name="value">The value of the element to add.</param>
public void Add(string key, double value)
2016-07-29 13:02:49 -07:00
{
this.Add((object)key, (object)value);
}
2016-07-29 13:02:49 -07:00
/// <summary>
/// Adds an element with the specified key and value into the Hashtable.
2016-07-29 13:02:49 -07:00
/// </summary>
/// <param name="key">The key of the element to add.</param>
/// <param name="value">The value of the element to add.</param>
public void Add(string key, double[] value)
2016-07-29 13:02:49 -07:00
{
this.Add((object)key, (object)value);
}
2016-07-29 13:02:49 -07:00
/// <summary>
/// Adds an element with the specified key and value into the Hashtable.
2016-07-29 13:02:49 -07:00
/// </summary>
/// <param name="key">The key of the element to add.</param>
/// <param name="value">The value of the element to add.</param>
2016-07-29 13:02:49 -07:00
public void Add(string key, Guid value)
{
this.Add((object)key, (object)value);
}
2016-07-29 13:02:49 -07:00
/// <summary>
/// Adds an element with the specified key and value into the Hashtable.
2016-07-29 13:02:49 -07:00
/// </summary>
/// <param name="key">The key of the element to add.</param>
/// <param name="value">The value of the element to add.</param>
2016-07-29 13:02:49 -07:00
public void Add(string key, Guid[] value)
{
this.Add((object)key, (object)value);
}
2016-07-29 13:02:49 -07:00
/// <summary>
/// Adds an element with the specified key and value into the Hashtable.
2016-07-29 13:02:49 -07:00
/// </summary>
/// <param name="key">The key of the element to add.</param>
/// <param name="value">The value of the element to add.</param>
2016-07-29 13:02:49 -07:00
public void Add(string key, Int32 value)
{
this.Add((object)key, (object)value);
}
2016-07-29 13:02:49 -07:00
/// <summary>
/// Adds an element with the specified key and value into the Hashtable.
2016-07-29 13:02:49 -07:00
/// </summary>
/// <param name="key">The key of the element to add.</param>
/// <param name="value">The value of the element to add.</param>
2016-07-29 13:02:49 -07:00
public void Add(string key, Int32[] value)
{
this.Add((object)key, (object)value);
}
2016-07-29 13:02:49 -07:00
/// <summary>
/// Adds an element with the specified key and value into the Hashtable.
2016-07-29 13:02:49 -07:00
/// </summary>
/// <param name="key">The key of the element to add.</param>
/// <param name="value">The value of the element to add.</param>
2016-07-29 13:02:49 -07:00
public void Add(string key, Int64 value)
{
this.Add((object)key, (object)value);
}
2016-07-29 13:02:49 -07:00
/// <summary>
/// Adds an element with the specified key and value into the Hashtable.
2016-07-29 13:02:49 -07:00
/// </summary>
/// <param name="key">The key of the element to add.</param>
/// <param name="value">The value of the element to add.</param>
2016-07-29 13:02:49 -07:00
public void Add(string key, Int64[] value)
{
this.Add((object)key, (object)value);
}
2016-07-29 13:02:49 -07:00
/// <summary>
/// Adds an element with the specified key and value into the Hashtable.
2016-07-29 13:02:49 -07:00
/// </summary>
/// <param name="key">The key of the element to add.</param>
/// <param name="value">The value of the element to add.</param>
public void Add(string key, sbyte value)
2016-07-29 13:02:49 -07:00
{
this.Add((object)key, (object)value);
}
2016-07-29 13:02:49 -07:00
/// <summary>
/// Adds an element with the specified key and value into the Hashtable.
2016-07-29 13:02:49 -07:00
/// </summary>
/// <param name="key">The key of the element to add.</param>
/// <param name="value">The value of the element to add.</param>
public void Add(string key, sbyte[] value)
2016-07-29 13:02:49 -07:00
{
this.Add((object)key, (object)value);
}
2016-07-29 13:02:49 -07:00
/// <summary>
/// Adds an element with the specified key and value into the Hashtable.
2016-07-29 13:02:49 -07:00
/// </summary>
/// <param name="key">The key of the element to add.</param>
/// <param name="value">The value of the element to add.</param>
2016-07-29 13:02:49 -07:00
public void Add(string key, Single value)
{
this.Add((object)key, (object)value);
}
2016-07-29 13:02:49 -07:00
/// <summary>
/// Adds an element with the specified key and value into the Hashtable.
2016-07-29 13:02:49 -07:00
/// </summary>
/// <param name="key">The key of the element to add.</param>
/// <param name="value">The value of the element to add.</param>
2016-07-29 13:02:49 -07:00
public void Add(string key, Single[] value)
{
this.Add((object)key, (object)value);
}
2016-07-29 13:02:49 -07:00
/// <summary>
/// Adds an element with the specified key and value into the Hashtable.
2016-07-29 13:02:49 -07:00
/// </summary>
/// <param name="key">The key of the element to add.</param>
/// <param name="value">The value of the element to add.</param>
public void Add(string key, string value)
2016-07-29 13:02:49 -07:00
{
this.Add((object)key, (object)value);
}
2016-07-29 13:02:49 -07:00
/// <summary>
/// Adds an element with the specified key and value into the Hashtable.
2016-07-29 13:02:49 -07:00
/// </summary>
/// <param name="key">The key of the element to add.</param>
/// <param name="value">The value of the element to add.</param>
public void Add(string key, string[] value)
2016-07-29 13:02:49 -07:00
{
this.Add((object)key, (object)value);
}
2016-07-29 13:02:49 -07:00
/// <summary>
/// Adds an element with the specified key and value into the Hashtable.
2016-07-29 13:02:49 -07:00
/// </summary>
/// <param name="key">The key of the element to add.</param>
/// <param name="value">The value of the element to add.</param>
2016-07-29 13:02:49 -07:00
public void Add(string key, TimeSpan value)
{
this.Add((object)key, (object)value);
}
2016-07-29 13:02:49 -07:00
/// <summary>
/// Adds an element with the specified key and value into the Hashtable.
2016-07-29 13:02:49 -07:00
/// </summary>
/// <param name="key">The key of the element to add.</param>
/// <param name="value">The value of the element to add.</param>
2016-07-29 13:02:49 -07:00
public void Add(string key, TimeSpan[] value)
{
this.Add((object)key, (object)value);
}
2016-07-29 13:02:49 -07:00
/// <summary>
/// Adds an element with the specified key and value into the Hashtable.
2016-07-29 13:02:49 -07:00
/// </summary>
/// <param name="key">The key of the element to add.</param>
/// <param name="value">The value of the element to add.</param>
2016-07-29 13:02:49 -07:00
public void Add(string key, UInt16 value)
{
this.Add((object)key, (object)value);
}
2016-07-29 13:02:49 -07:00
/// <summary>
/// Adds an element with the specified key and value into the Hashtable.
2016-07-29 13:02:49 -07:00
/// </summary>
/// <param name="key">The key of the element to add.</param>
/// <param name="value">The value of the element to add.</param>
2016-07-29 13:02:49 -07:00
public void Add(string key, UInt16[] value)
{
this.Add((object)key, (object)value);
}
2016-07-29 13:02:49 -07:00
/// <summary>
/// Adds an element with the specified key and value into the Hashtable.
2016-07-29 13:02:49 -07:00
/// </summary>
/// <param name="key">The key of the element to add.</param>
/// <param name="value">The value of the element to add.</param>
2016-07-29 13:02:49 -07:00
public void Add(string key, UInt32 value)
{
this.Add((object)key, (object)value);
}
2016-07-29 13:02:49 -07:00
/// <summary>
/// Adds an element with the specified key and value into the Hashtable.
2016-07-29 13:02:49 -07:00
/// </summary>
/// <param name="key">The key of the element to add.</param>
/// <param name="value">The value of the element to add.</param>
2016-07-29 13:02:49 -07:00
public void Add(string key, UInt32[] value)
{
this.Add((object)key, (object)value);
}
2016-07-29 13:02:49 -07:00
/// <summary>
/// Adds an element with the specified key and value into the Hashtable.
2016-07-29 13:02:49 -07:00
/// </summary>
/// <param name="key">The key of the element to add.</param>
/// <param name="value">The value of the element to add.</param>
2016-07-29 13:02:49 -07:00
public void Add(string key, UInt64 value)
{
this.Add((object)key, (object)value);
}
2016-07-29 13:02:49 -07:00
/// <summary>
/// Adds an element with the specified key and value into the Hashtable.
2016-07-29 13:02:49 -07:00
/// </summary>
/// <param name="key">The key of the element to add.</param>
/// <param name="value">The value of the element to add.</param>
2016-07-29 13:02:49 -07:00
public void Add(string key, UInt64[] value)
{
this.Add((object)key, (object)value);
}
2016-07-29 13:02:49 -07:00
/// <summary>
/// Adds an element with the specified key and value into the Hashtable.
2016-07-29 13:02:49 -07:00
/// </summary>
/// <param name="key">The key of the element to add.</param>
/// <param name="value">The value of the element to add.</param>
2016-07-29 13:02:49 -07:00
public void Add(string key, Uri value)
{
this.Add((object)key, (object)value);
}
2016-07-29 13:02:49 -07:00
/// <summary>
/// Adds an element with the specified key and value into the Hashtable.
2016-07-29 13:02:49 -07:00
/// </summary>
/// <param name="key">The key of the element to add.</param>
/// <param name="value">The value of the element to add.</param>
2016-07-29 13:02:49 -07:00
public void Add(string key, Uri[] value)
{
this.Add((object)key, (object)value);
}
2016-07-29 13:02:49 -07:00
/// <summary>
/// Adds an element with the specified key and value into the Hashtable.
2016-07-29 13:02:49 -07:00
/// </summary>
/// <param name="key">The key of the element to add.</param>
/// <param name="value">The value of the element to add.</param>
2016-07-29 13:02:49 -07:00
public void Add(string key, Version value)
{
this.Add((object)key, (object)value);
}
2016-07-29 13:02:49 -07:00
/// <summary>
/// Adds an element with the specified key and value into the Hashtable.
2016-07-29 13:02:49 -07:00
/// </summary>
/// <param name="key">The key of the element to add.</param>
/// <param name="value">The value of the element to add.</param>
2016-07-29 13:02:49 -07:00
public void Add(string key, Version[] value)
{
this.Add((object)key, (object)value);
}
2016-07-29 13:02:49 -07:00
/// <summary>
/// Adds an element with the specified key and value into the Hashtable.
2016-07-29 13:02:49 -07:00
/// </summary>
/// <param name="key">The key of the element to add.</param>
/// <param name="value">The value of the element to add.</param>
2016-07-29 13:02:49 -07:00
public void Add(string key, PSPrimitiveDictionary value)
{
this.Add((object)key, (object)value);
}
2016-07-29 13:02:49 -07:00
/// <summary>
/// Adds an element with the specified key and value into the Hashtable.
2016-07-29 13:02:49 -07:00
/// </summary>
/// <param name="key">The key of the element to add.</param>
/// <param name="value">The value of the element to add.</param>
2016-07-29 13:02:49 -07:00
public void Add(string key, PSPrimitiveDictionary[] value)
{
this.Add((object)key, (object)value);
}
2016-07-29 13:02:49 -07:00
#endregion
2016-07-29 13:02:49 -07:00
#region Internal Methods
2016-07-29 13:02:49 -07:00
/// <summary>
2017-01-16 13:31:14 -08:00
/// If originalHash contains PSVersionTable, then just returns the Cloned copy of
2016-07-29 13:02:49 -07:00
/// the original hash. Othewise, creates a clone copy and add PSVersionInfo.GetPSVersionTable
/// to the clone and returns.
/// </summary>
/// <param name="originalHash"></param>
/// <returns></returns>
internal static PSPrimitiveDictionary CloneAndAddPSVersionTable(PSPrimitiveDictionary originalHash)
{
if ((originalHash != null) &&
2016-07-29 13:02:49 -07:00
(originalHash.ContainsKey(PSVersionInfo.PSVersionTableName)))
{
return (PSPrimitiveDictionary)originalHash.Clone();
}
2016-07-29 13:02:49 -07:00
PSPrimitiveDictionary result = originalHash;
if (originalHash != null)
2016-07-29 13:02:49 -07:00
{
result = (PSPrimitiveDictionary)originalHash.Clone();
}
else
{
result = new PSPrimitiveDictionary();
}
PSPrimitiveDictionary versionTable = new PSPrimitiveDictionary(PSVersionInfo.GetPSVersionTableForDownLevel())
{
{"PSSemanticVersion", PSVersionInfo.PSVersion.ToString()}
};
2016-07-29 13:02:49 -07:00
result.Add(PSVersionInfo.PSVersionTableName, versionTable);
2016-07-29 13:02:49 -07:00
return result;
}
2016-07-29 13:02:49 -07:00
/// <summary>
/// Tries to get a value that might be present in a chain of nested PSPrimitiveDictionaries.
/// For example to get $sessionInfo.ApplicationPrivateData.ImplicitRemoting.Hash you could call
/// TryPathGet&lt;string&gt;($sessionInfo.ApplicationPrivateData, out myHash, "ImplicitRemoting", "Hash").
/// </summary>
/// <typeparam name="T">Expected type of the value</typeparam>
/// <param name="data">The root dictionary.</param>
2016-07-29 13:02:49 -07:00
/// <param name="result"></param>
/// <param name="keys">A chain of keys leading from the root dictionary (<paramref name="data"/>) to the value.</param>
2020-11-24 04:35:46 +00:00
/// <returns><see langword="true"/> if the value was found and was of the correct type; <see langword="false"/> otherwise.</returns>
internal static bool TryPathGet<T>(IDictionary data, out T result, params string[] keys)
2016-07-29 13:02:49 -07:00
{
Dbg.Assert(keys != null, "Caller should verify that keys != null");
Dbg.Assert(keys.Length >= 1, "Caller should verify that keys.Length >= 1");
Dbg.Assert(keys[0] != null, "Caller should verify that keys[i] != null");
if (data == null || !data.Contains(keys[0]))
2016-07-29 13:02:49 -07:00
{
result = default(T);
return false;
}
2016-07-29 13:02:49 -07:00
if (keys.Length == 1)
{
return LanguagePrimitives.TryConvertTo<T>(data[keys[0]], out result);
}
else
{
IDictionary subData;
if (LanguagePrimitives.TryConvertTo<IDictionary>(data[keys[0]], out subData)
&& subData != null)
{
string[] subKeys = new string[keys.Length - 1];
Array.Copy(keys, 1, subKeys, 0, subKeys.Length);
return TryPathGet<T>(subData, out result, subKeys);
}
else
{
result = default(T);
return false;
}
}
}
2016-07-29 13:02:49 -07:00
#endregion
}
}
namespace Microsoft.PowerShell
{
2016-07-29 13:02:49 -07:00
using System.Management.Automation;
using System.Security.Principal;
2016-07-29 13:02:49 -07:00
/// <summary>
/// Rehydrating type converter used during deserialization.
/// It takes results of serializing some common types
/// and rehydrates them back from property bags into live objects.
/// </summary>
/// <!--
/// To add a new type for rehydration:
/// - Add a new T RehydrateT(PSObject pso) method below
/// - Add this method to converters dictionary in the static constructor below
/// - If implicit rehydration is required then
/// - Add appropriate types.ps1 xml entries for
/// - SerializationDepth=X
/// - For types depending only on ToString for rehydration set
/// - SerializationMethod=SpecificProperties
/// - PropertySerializationSet=<empty>
/// - TargetTypeForDeserialization=DeserializingTypeConverter
/// - Add a field of that type in unit tests / S.M.A.Test.SerializationTest+RehydratedType
/// (testsrc\admintest\monad\DRT\engine\UnitTests\SerializationTest.cs)
/// -->
public sealed class DeserializingTypeConverter : PSTypeConverter
{
#region Infrastructure
2016-07-29 13:02:49 -07:00
private static readonly Dictionary<Type, Func<PSObject, object>> s_converter;
static DeserializingTypeConverter()
{
2016-07-29 13:02:49 -07:00
s_converter = new Dictionary<Type, Func<PSObject, object>>();
2016-07-29 13:02:49 -07:00
s_converter.Add(typeof(PSPrimitiveDictionary), RehydratePrimitiveHashtable);
2016-07-29 13:02:49 -07:00
s_converter.Add(typeof(SwitchParameter), RehydrateSwitchParameter);
s_converter.Add(typeof(PSListModifier), RehydratePSListModifier);
s_converter.Add(typeof(PSCredential), RehydratePSCredential);
s_converter.Add(typeof(PSSenderInfo), RehydratePSSenderInfo);
s_converter.Add(typeof(CultureInfo), RehydrateCultureInfo);
s_converter.Add(typeof(ParameterSetMetadata), RehydrateParameterSetMetadata);
2016-07-29 13:02:49 -07:00
s_converter.Add(typeof(System.Security.Cryptography.X509Certificates.X509Certificate2), RehydrateX509Certificate2);
s_converter.Add(typeof(System.Security.Cryptography.X509Certificates.X500DistinguishedName), RehydrateX500DistinguishedName);
s_converter.Add(typeof(System.Net.IPAddress), RehydrateIPAddress);
2016-07-29 13:02:49 -07:00
s_converter.Add(typeof(MailAddress), RehydrateMailAddress);
2016-07-29 13:02:49 -07:00
s_converter.Add(typeof(System.Security.AccessControl.DirectorySecurity), RehydrateObjectSecurity<System.Security.AccessControl.DirectorySecurity>);
s_converter.Add(typeof(System.Security.AccessControl.FileSecurity), RehydrateObjectSecurity<System.Security.AccessControl.FileSecurity>);
s_converter.Add(typeof(System.Security.AccessControl.RegistrySecurity), RehydrateObjectSecurity<System.Security.AccessControl.RegistrySecurity>);
2016-07-29 13:02:49 -07:00
s_converter.Add(typeof(ExtendedTypeDefinition), RehydrateExtendedTypeDefinition);
s_converter.Add(typeof(FormatViewDefinition), RehydrateFormatViewDefinition);
s_converter.Add(typeof(PSControl), RehydratePSControl);
s_converter.Add(typeof(PSControlGroupBy), RehydrateGroupBy);
s_converter.Add(typeof(DisplayEntry), RehydrateDisplayEntry);
s_converter.Add(typeof(EntrySelectedBy), RehydrateEntrySelectedBy);
s_converter.Add(typeof(TableControlColumnHeader), RehydrateTableControlColumnHeader);
s_converter.Add(typeof(TableControlRow), RehydrateTableControlRow);
s_converter.Add(typeof(TableControlColumn), RehydrateTableControlColumn);
s_converter.Add(typeof(ListControlEntry), RehydrateListControlEntry);
s_converter.Add(typeof(ListControlEntryItem), RehydrateListControlEntryItem);
s_converter.Add(typeof(WideControlEntryItem), RehydrateWideControlEntryItem);
s_converter.Add(typeof(CustomControlEntry), RehydrateCustomControlEntry);
s_converter.Add(typeof(CustomItemBase), RehydrateCustomItemBase);
2016-07-29 13:02:49 -07:00
s_converter.Add(typeof(CompletionResult), RehydrateCompletionResult);
s_converter.Add(typeof(ModuleSpecification), RehydrateModuleSpecification);
s_converter.Add(typeof(CommandCompletion), RehydrateCommandCompletion);
s_converter.Add(typeof(JobStateInfo), RehydrateJobStateInfo);
s_converter.Add(typeof(JobStateEventArgs), RehydrateJobStateEventArgs);
s_converter.Add(typeof(PSSessionOption), RehydratePSSessionOption);
s_converter.Add(typeof(LineBreakpoint), RehydrateLineBreakpoint);
s_converter.Add(typeof(CommandBreakpoint), RehydrateCommandBreakpoint);
s_converter.Add(typeof(VariableBreakpoint), RehydrateVariableBreakpoint);
s_converter.Add(typeof(BreakpointUpdatedEventArgs), RehydrateBreakpointUpdatedEventArgs);
s_converter.Add(typeof(DebuggerCommand), RehydrateDebuggerCommand);
s_converter.Add(typeof(DebuggerCommandResults), RehydrateDebuggerCommandResults);
s_converter.Add(typeof(DebuggerStopEventArgs), RehydrateDebuggerStopEventArgs);
}
2016-07-29 13:02:49 -07:00
/// <summary>
/// Determines if the converter can convert the <paramref name="sourceValue"/> parameter to the <paramref name="destinationType"/> parameter.
/// </summary>
/// <param name="sourceValue">The value to convert from.</param>
/// <param name="destinationType">The type to convert to.</param>
2016-07-29 13:02:49 -07:00
/// <returns>True if the converter can convert the <paramref name="sourceValue"/> parameter to the <paramref name="destinationType"/> parameter, otherwise false.</returns>
public override bool CanConvertFrom(PSObject sourceValue, Type destinationType)
{
foreach (Type type in s_converter.Keys)
{
if (Deserializer.IsDeserializedInstanceOfType(sourceValue, type))
{
return true;
}
}
2016-07-29 13:02:49 -07:00
return false;
}
2016-07-29 13:02:49 -07:00
/// <summary>
/// Converts the <paramref name="sourceValue"/> parameter to the <paramref name="destinationType"/> parameter using formatProvider and ignoreCase.
2016-07-29 13:02:49 -07:00
/// </summary>
/// <param name="sourceValue">The value to convert from.</param>
/// <param name="destinationType">The type to convert to.</param>
/// <param name="formatProvider">The format provider to use like in IFormattable's ToString.</param>
/// <param name="ignoreCase">True if case should be ignored.</param>
/// <returns>The <paramref name="sourceValue"/> parameter converted to the <paramref name="destinationType"/> parameter using formatProvider and ignoreCase.</returns>
/// <exception cref="InvalidCastException">If no conversion was possible.</exception>
2016-07-29 13:02:49 -07:00
public override object ConvertFrom(PSObject sourceValue, Type destinationType, IFormatProvider formatProvider, bool ignoreCase)
{
if (destinationType == null)
2016-07-29 13:02:49 -07:00
{
2020-05-19 19:57:52 +01:00
throw PSTraceSource.NewArgumentNullException(nameof(destinationType));
2016-07-29 13:02:49 -07:00
}
if (sourceValue == null)
2016-07-29 13:02:49 -07:00
{
throw new PSInvalidCastException(
"InvalidCastWhenRehydratingFromNull",
2020-05-19 19:57:52 +01:00
PSTraceSource.NewArgumentNullException(nameof(sourceValue)),
2016-07-29 13:02:49 -07:00
ExtendedTypeSystem.InvalidCastFromNull,
destinationType.ToString());
}
2016-07-29 13:02:49 -07:00
foreach (KeyValuePair<Type, Func<PSObject, object>> item in s_converter)
{
Type type = item.Key;
Func<PSObject, object> typeConverter = item.Value;
if (Deserializer.IsDeserializedInstanceOfType(sourceValue, type))
{
return ConvertFrom(sourceValue, typeConverter);
}
}
2016-07-29 13:02:49 -07:00
throw new PSInvalidCastException(
"InvalidCastEnumFromTypeNotAString",
null,
ExtendedTypeSystem.InvalidCastException,
sourceValue,
typeof(PSObject),
2016-07-29 13:02:49 -07:00
destinationType);
}
2016-07-29 13:02:49 -07:00
private static object ConvertFrom(PSObject o, Func<PSObject, object> converter)
{
// rehydrate
PSObject dso = o;
object rehydratedObject = converter(dso);
2016-07-29 13:02:49 -07:00
// re-add instance properties
// (dso.InstanceMembers includes both instance and *type table* properties;
// therefore this will also re-add type table properties if they are not present when the deserializer runs;
// this is ok)
bool returnPSObject = false;
PSObject rehydratedPSObject = PSObject.AsPSObject(rehydratedObject);
foreach (PSMemberInfo member in dso.InstanceMembers)
{
if (member.MemberType == (member.MemberType & (PSMemberTypes.Properties | PSMemberTypes.MemberSet | PSMemberTypes.PropertySet)))
{
if (rehydratedPSObject.Members[member.Name] == null)
2016-07-29 13:02:49 -07:00
{
rehydratedPSObject.InstanceMembers.Add(member);
returnPSObject = true;
}
}
}
2016-07-29 13:02:49 -07:00
if (returnPSObject)
{
return rehydratedPSObject;
}
else
{
return rehydratedObject;
}
}
2016-07-29 13:02:49 -07:00
/// <summary>
/// Returns true if the converter can convert the <paramref name="sourceValue"/> parameter to the <paramref name="destinationType"/> parameter.
2016-07-29 13:02:49 -07:00
/// </summary>
/// <param name="sourceValue">The value to convert from.</param>
/// <param name="destinationType">The type to convert to.</param>
2016-07-29 13:02:49 -07:00
/// <returns>True if the converter can convert the <paramref name="sourceValue"/> parameter to the <paramref name="destinationType"/> parameter, otherwise false.</returns>
public override bool CanConvertTo(object sourceValue, Type destinationType)
{
return false;
}
2016-07-29 13:02:49 -07:00
/// <summary>
/// Converts the <paramref name="sourceValue"/> parameter to the <paramref name="destinationType"/> parameter using formatProvider and ignoreCase.
2016-07-29 13:02:49 -07:00
/// </summary>
/// <param name="sourceValue">The value to convert from.</param>
/// <param name="destinationType">The type to convert to.</param>
/// <param name="formatProvider">The format provider to use like in IFormattable's ToString.</param>
/// <param name="ignoreCase">True if case should be ignored.</param>
/// <returns>SourceValue converted to the <paramref name="destinationType"/> parameter using formatProvider and ignoreCase.</returns>
/// <exception cref="InvalidCastException">If no conversion was possible.</exception>
2016-07-29 13:02:49 -07:00
public override object ConvertTo(object sourceValue, Type destinationType, IFormatProvider formatProvider, bool ignoreCase)
{
throw PSTraceSource.NewNotSupportedException();
}
2016-07-29 13:02:49 -07:00
/// <summary>
/// This method is not implemented - an overload taking a PSObject is implemented instead.
2016-07-29 13:02:49 -07:00
/// </summary>
public override bool CanConvertFrom(object sourceValue, Type destinationType)
{
throw new NotImplementedException();
}
2016-07-29 13:02:49 -07:00
/// <summary>
/// This method is not implemented - an overload taking a PSObject is implemented instead.
2016-07-29 13:02:49 -07:00
/// </summary>
public override bool CanConvertTo(PSObject sourceValue, Type destinationType)
{
throw new NotImplementedException();
}
2016-07-29 13:02:49 -07:00
/// <summary>
/// This method is not implemented - an overload taking a PSObject is implemented instead.
2016-07-29 13:02:49 -07:00
/// </summary>
public override object ConvertFrom(object sourceValue, Type destinationType, IFormatProvider formatProvider, bool ignoreCase)
{
throw new NotImplementedException();
}
2016-07-29 13:02:49 -07:00
/// <summary>
/// This method is not implemented - an overload taking a PSObject is implemented instead.
2016-07-29 13:02:49 -07:00
/// </summary>
public override object ConvertTo(PSObject sourceValue, Type destinationType, IFormatProvider formatProvider, bool ignoreCase)
{
throw new NotImplementedException();
}
2016-07-29 13:02:49 -07:00
#endregion
2016-07-29 13:02:49 -07:00
#region Rehydration helpers
2016-07-29 13:02:49 -07:00
[Flags]
internal enum RehydrationFlags
{
NullValueBad = 0,
NullValueOk = 0x1,
NullValueMeansEmptyList = 0x3,
2016-07-29 13:02:49 -07:00
MissingPropertyBad = 0,
MissingPropertyOk = 0x4,
}
2016-07-29 13:02:49 -07:00
/// <summary>
2017-01-16 13:31:14 -08:00
/// Gets value of a property (has to be present, value has to be non-null).
2016-07-29 13:02:49 -07:00
/// Can throw any exception (which is ok - LanguagePrimitives.ConvertTo will catch that).
/// </summary>
/// <typeparam name="T">Expected type of the property</typeparam>
/// <param name="pso">Deserialized object.</param>
/// <param name="propertyName">Property name.</param>
2016-07-29 13:02:49 -07:00
/// <returns></returns>
private static T GetPropertyValue<T>(PSObject pso, string propertyName)
{
return GetPropertyValue<T>(pso, propertyName, RehydrationFlags.NullValueBad | RehydrationFlags.MissingPropertyBad);
}
2016-07-29 13:02:49 -07:00
/// <summary>
/// Gets value of a property. Can throw any exception (which is ok - LanguagePrimitives.ConvertTo will catch that).
/// </summary>
/// <typeparam name="T">Expected type of the property</typeparam>
/// <param name="pso">Deserialized object.</param>
/// <param name="propertyName">Property name.</param>
2016-07-29 13:02:49 -07:00
/// <param name="flags"></param>
/// <returns></returns>
internal static T GetPropertyValue<T>(PSObject pso, string propertyName, RehydrationFlags flags)
{
Dbg.Assert(pso != null, "Caller should verify pso != null");
Dbg.Assert(!string.IsNullOrEmpty(propertyName), "Caller should verify propertyName != null");
2016-07-29 13:02:49 -07:00
PSPropertyInfo property = pso.Properties[propertyName];
if ((property == null) && ((flags & RehydrationFlags.MissingPropertyOk) == RehydrationFlags.MissingPropertyOk))
2016-07-29 13:02:49 -07:00
{
return default(T);
}
else
{
object propertyValue = property.Value;
if ((propertyValue == null) && ((flags & RehydrationFlags.NullValueOk) == RehydrationFlags.NullValueOk))
2016-07-29 13:02:49 -07:00
{
return default(T);
}
else
{
T t = (T)LanguagePrimitives.ConvertTo(propertyValue, typeof(T), CultureInfo.InvariantCulture);
return t;
}
}
}
private static ListType RehydrateList<ListType, ItemType>(PSObject pso, string propertyName, RehydrationFlags flags)
where ListType : IList, new()
{
ArrayList deserializedList = GetPropertyValue<ArrayList>(pso, propertyName, flags);
if (deserializedList == null)
{
if ((flags & RehydrationFlags.NullValueMeansEmptyList) == RehydrationFlags.NullValueMeansEmptyList)
{
return new ListType();
}
else
{
return default(ListType);
}
}
else
{
ListType newList = new ListType();
foreach (object deserializedItem in deserializedList)
{
ItemType item = (ItemType)LanguagePrimitives.ConvertTo(deserializedItem, typeof(ItemType), CultureInfo.InvariantCulture);
newList.Add(item);
}
return newList;
}
}
2016-07-29 13:02:49 -07:00
#endregion
2016-07-29 13:02:49 -07:00
#region Rehydration of miscellaneous types
2016-07-29 13:02:49 -07:00
private static object RehydratePrimitiveHashtable(PSObject pso)
{
Hashtable hashtable = (Hashtable)LanguagePrimitives.ConvertTo(pso, typeof(Hashtable), CultureInfo.InvariantCulture);
return new PSPrimitiveDictionary(hashtable);
}
2016-07-29 13:02:49 -07:00
private static object RehydrateSwitchParameter(PSObject pso)
{
return GetPropertyValue<SwitchParameter>(pso, "IsPresent");
}
2016-07-29 13:02:49 -07:00
private static CultureInfo RehydrateCultureInfo(PSObject pso)
{
string s = pso.ToString();
return new CultureInfo(s);
}
2016-07-29 13:02:49 -07:00
private static PSListModifier RehydratePSListModifier(PSObject pso)
{
Hashtable h = new Hashtable();
2016-07-29 13:02:49 -07:00
PSPropertyInfo addProperty = pso.Properties[PSListModifier.AddKey];
if ((addProperty != null) && (addProperty.Value != null))
{
h.Add(PSListModifier.AddKey, addProperty.Value);
}
2016-07-29 13:02:49 -07:00
PSPropertyInfo removeProperty = pso.Properties[PSListModifier.RemoveKey];
if ((removeProperty != null) && (removeProperty.Value != null))
{
h.Add(PSListModifier.RemoveKey, removeProperty.Value);
}
2016-07-29 13:02:49 -07:00
PSPropertyInfo replaceProperty = pso.Properties[PSListModifier.ReplaceKey];
if ((replaceProperty != null) && (replaceProperty.Value != null))
{
h.Add(PSListModifier.ReplaceKey, replaceProperty.Value);
}
2016-07-29 13:02:49 -07:00
return new PSListModifier(h);
}
2016-07-29 13:02:49 -07:00
private static CompletionResult RehydrateCompletionResult(PSObject pso)
{
string completionText = GetPropertyValue<string>(pso, "CompletionText");
string listItemText = GetPropertyValue<string>(pso, "ListItemText");
string toolTip = GetPropertyValue<string>(pso, "ToolTip");
CompletionResultType resultType = GetPropertyValue<CompletionResultType>(pso, "ResultType");
2016-07-29 13:02:49 -07:00
return new CompletionResult(completionText, listItemText, resultType, toolTip);
}
2016-07-29 13:02:49 -07:00
private static ModuleSpecification RehydrateModuleSpecification(PSObject pso)
{
return new ModuleSpecification
{
Name = GetPropertyValue<string>(pso, "Name"),
Guid = GetPropertyValue<Guid?>(pso, "Guid"),
Version = GetPropertyValue<Version>(pso, "Version"),
MaximumVersion = GetPropertyValue<string>(pso, "MaximumVersion"),
RequiredVersion =
GetPropertyValue<Version>(pso, "RequiredVersion")
2016-07-29 13:02:49 -07:00
};
}
private static CommandCompletion RehydrateCommandCompletion(PSObject pso)
{
var completions = new Collection<CompletionResult>();
foreach (var match in GetPropertyValue<ArrayList>(pso, "CompletionMatches"))
2016-07-29 13:02:49 -07:00
{
completions.Add((CompletionResult)match);
}
2016-07-29 13:02:49 -07:00
var currentMatchIndex = GetPropertyValue<int>(pso, "CurrentMatchIndex");
var replacementIndex = GetPropertyValue<int>(pso, "ReplacementIndex");
var replacementLength = GetPropertyValue<int>(pso, "ReplacementLength");
return new CommandCompletion(completions, currentMatchIndex, replacementIndex, replacementLength);
}
2016-07-29 13:02:49 -07:00
private static JobStateInfo RehydrateJobStateInfo(PSObject pso)
{
var jobState = GetPropertyValue<JobState>(pso, "State");
2016-07-29 13:02:49 -07:00
Exception reason = null;
object propertyValue = null;
PSPropertyInfo property = pso.Properties["Reason"];
string message = string.Empty;
2016-07-29 13:02:49 -07:00
if (property != null)
{
propertyValue = property.Value;
}
2016-07-29 13:02:49 -07:00
if (propertyValue != null)
{
if (Deserializer.IsDeserializedInstanceOfType(propertyValue, typeof(Exception)))
{
// if we have a deserialized remote or any other exception, use its message to construct
// an exception
message = PSObject.AsPSObject(propertyValue).Properties["Message"].Value as string;
}
else if (propertyValue is Exception)
{
reason = (Exception)propertyValue;
}
else
{
message = propertyValue.ToString();
}
2016-07-29 13:02:49 -07:00
if (!string.IsNullOrEmpty(message))
{
try
{
reason = (Exception)LanguagePrimitives.ConvertTo(message, typeof(Exception), CultureInfo.InvariantCulture);
}
catch (Exception)
{
// it is ok to eat this exception since we do not want
// rehydration to fail
reason = null;
}
}
}
2016-07-29 13:02:49 -07:00
return new JobStateInfo(jobState, reason);
}
2016-07-29 13:02:49 -07:00
internal static JobStateEventArgs RehydrateJobStateEventArgs(PSObject pso)
{
var jobStateInfo = RehydrateJobStateInfo(PSObject.AsPSObject(pso.Properties["JobStateInfo"].Value));
JobStateInfo previousJobStateInfo = null;
var previousJobStateInfoProperty = pso.Properties["PreviousJobStateInfo"];
2016-07-29 13:02:49 -07:00
if (previousJobStateInfoProperty != null && previousJobStateInfoProperty.Value != null)
{
previousJobStateInfo = RehydrateJobStateInfo(PSObject.AsPSObject(previousJobStateInfoProperty.Value));
}
return new JobStateEventArgs(jobStateInfo, previousJobStateInfo);
}
internal static PSSessionOption RehydratePSSessionOption(PSObject pso)
{
PSSessionOption option = new PSSessionOption();
2016-07-29 13:02:49 -07:00
option.ApplicationArguments = GetPropertyValue<PSPrimitiveDictionary>(pso, "ApplicationArguments");
2016-07-29 13:02:49 -07:00
option.CancelTimeout = GetPropertyValue<TimeSpan>(pso, "CancelTimeout");
option.Culture = GetPropertyValue<CultureInfo>(pso, "Culture");
option.IdleTimeout = GetPropertyValue<TimeSpan>(pso, "IdleTimeout");
option.MaximumConnectionRedirectionCount = GetPropertyValue<int>(pso, "MaximumConnectionRedirectionCount");
option.MaximumReceivedDataSizePerCommand = GetPropertyValue<int?>(pso, "MaximumReceivedDataSizePerCommand");
option.MaximumReceivedObjectSize = GetPropertyValue<int?>(pso, "MaximumReceivedObjectSize");
option.NoCompression = GetPropertyValue<bool>(pso, "NoCompression");
option.NoEncryption = GetPropertyValue<bool>(pso, "NoEncryption");
2016-07-29 13:02:49 -07:00
option.NoMachineProfile = GetPropertyValue<bool>(pso, "NoMachineProfile");
option.OpenTimeout = GetPropertyValue<TimeSpan>(pso, "OpenTimeout");
option.OperationTimeout = GetPropertyValue<TimeSpan>(pso, "OperationTimeout");
option.OutputBufferingMode = GetPropertyValue<OutputBufferingMode>(pso, "OutputBufferingMode");
option.MaxConnectionRetryCount = GetPropertyValue<int>(pso, "MaxConnectionRetryCount");
option.ProxyAccessType = GetPropertyValue<ProxyAccessType>(pso, "ProxyAccessType");
option.ProxyAuthentication = GetPropertyValue<AuthenticationMechanism>(pso, "ProxyAuthentication");
option.ProxyCredential = GetPropertyValue<PSCredential>(pso, "ProxyCredential");
2016-07-29 13:02:49 -07:00
option.SkipCACheck = GetPropertyValue<bool>(pso, "SkipCACheck");
option.SkipCNCheck = GetPropertyValue<bool>(pso, "SkipCNCheck");
option.SkipRevocationCheck = GetPropertyValue<bool>(pso, "SkipRevocationCheck");
option.UICulture = GetPropertyValue<CultureInfo>(pso, "UICulture");
option.UseUTF16 = GetPropertyValue<bool>(pso, "UseUTF16");
option.IncludePortInSPN = GetPropertyValue<bool>(pso, "IncludePortInSPN");
2016-07-29 13:02:49 -07:00
return option;
}
internal static LineBreakpoint RehydrateLineBreakpoint(PSObject pso)
{
string script = GetPropertyValue<string>(pso, "Script");
int line = GetPropertyValue<int>(pso, "Line");
int column = GetPropertyValue<int>(pso, "Column");
int id = GetPropertyValue<int>(pso, "Id");
bool enabled = GetPropertyValue<bool>(pso, "Enabled");
ScriptBlock action = RehydrateScriptBlock(
GetPropertyValue<string>(pso, "Action", RehydrationFlags.MissingPropertyOk));
var bp = new LineBreakpoint(script, line, column, action, id);
bp.SetEnabled(enabled);
return bp;
}
internal static CommandBreakpoint RehydrateCommandBreakpoint(PSObject pso)
{
string script = GetPropertyValue<string>(pso, "Script", RehydrationFlags.MissingPropertyOk | RehydrationFlags.NullValueOk);
string command = GetPropertyValue<string>(pso, "Command");
int id = GetPropertyValue<int>(pso, "Id");
bool enabled = GetPropertyValue<bool>(pso, "Enabled");
WildcardPattern pattern = WildcardPattern.Get(command, WildcardOptions.Compiled | WildcardOptions.IgnoreCase);
ScriptBlock action = RehydrateScriptBlock(
GetPropertyValue<string>(pso, "Action", RehydrationFlags.MissingPropertyOk));
var bp = new CommandBreakpoint(script, pattern, command, action, id);
bp.SetEnabled(enabled);
return bp;
}
internal static VariableBreakpoint RehydrateVariableBreakpoint(PSObject pso)
{
string script = GetPropertyValue<string>(pso, "Script", RehydrationFlags.MissingPropertyOk | RehydrationFlags.NullValueOk);
string variableName = GetPropertyValue<string>(pso, "Variable");
int id = GetPropertyValue<int>(pso, "Id");
bool enabled = GetPropertyValue<bool>(pso, "Enabled");
VariableAccessMode access = GetPropertyValue<VariableAccessMode>(pso, "AccessMode");
ScriptBlock action = RehydrateScriptBlock(
GetPropertyValue<string>(pso, "Action", RehydrationFlags.MissingPropertyOk));
var bp = new VariableBreakpoint(script, variableName, access, action, id);
bp.SetEnabled(enabled);
return bp;
}
internal static BreakpointUpdatedEventArgs RehydrateBreakpointUpdatedEventArgs(PSObject pso)
{
Breakpoint bp = GetPropertyValue<Breakpoint>(pso, "Breakpoint");
BreakpointUpdateType bpUpdateType = GetPropertyValue<BreakpointUpdateType>(pso, "UpdateType");
int bpCount = GetPropertyValue<int>(pso, "BreakpointCount");
return new BreakpointUpdatedEventArgs(bp, bpUpdateType, bpCount);
}
internal static DebuggerCommand RehydrateDebuggerCommand(PSObject pso)
{
string command = GetPropertyValue<string>(pso, "Command");
bool repeatOnEnter = GetPropertyValue<bool>(pso, "RepeatOnEnter");
bool executedByDebugger = GetPropertyValue<bool>(pso, "ExecutedByDebugger");
DebuggerResumeAction? resumeAction = GetPropertyValue<DebuggerResumeAction?>(pso, "ResumeAction", RehydrationFlags.NullValueOk);
return new DebuggerCommand(command, resumeAction, repeatOnEnter, executedByDebugger);
}
internal static DebuggerCommandResults RehydrateDebuggerCommandResults(PSObject pso)
{
DebuggerResumeAction? resumeAction = GetPropertyValue<DebuggerResumeAction?>(pso, "ResumeAction", RehydrationFlags.NullValueOk);
bool evaluatedByDebugger = GetPropertyValue<bool>(pso, "EvaluatedByDebugger");
return new DebuggerCommandResults(resumeAction, evaluatedByDebugger);
}
internal static DebuggerStopEventArgs RehydrateDebuggerStopEventArgs(PSObject pso)
{
PSObject psoInvocationInfo = GetPropertyValue<PSObject>(pso, "SerializedInvocationInfo", RehydrationFlags.NullValueOk | RehydrationFlags.MissingPropertyOk);
InvocationInfo invocationInfo = (psoInvocationInfo != null) ? new InvocationInfo(psoInvocationInfo) : null;
DebuggerResumeAction resumeAction = GetPropertyValue<DebuggerResumeAction>(pso, "ResumeAction");
Collection<Breakpoint> breakpoints = new Collection<Breakpoint>();
foreach (var item in GetPropertyValue<ArrayList>(pso, "Breakpoints"))
{
Breakpoint bp = item as Breakpoint;
if (bp != null)
{
breakpoints.Add(bp);
}
}
return new DebuggerStopEventArgs(invocationInfo, breakpoints, resumeAction);
}
private static ScriptBlock RehydrateScriptBlock(string script)
{
if (!string.IsNullOrEmpty(script))
{
return ScriptBlock.Create(script);
}
return null;
}
#endregion
2016-07-29 13:02:49 -07:00
#region Rehydration of security-related types
2016-07-29 13:02:49 -07:00
private static PSCredential RehydratePSCredential(PSObject pso)
{
string userName = GetPropertyValue<string>(pso, "UserName");
System.Security.SecureString password = GetPropertyValue<System.Security.SecureString>(pso, "Password");
if (string.IsNullOrEmpty(userName))
2016-07-29 13:02:49 -07:00
{
return PSCredential.Empty;
}
else
{
return new PSCredential(userName, password);
}
}
2016-07-29 13:02:49 -07:00
internal static PSSenderInfo RehydratePSSenderInfo(PSObject pso)
{
PSObject userInfo = GetPropertyValue<PSObject>(pso, "UserInfo");
PSObject userIdentity = GetPropertyValue<PSObject>(userInfo, "Identity");
PSObject certDetails = GetPropertyValue<PSObject>(userIdentity, "CertificateDetails");
PSCertificateDetails psCertDetails = certDetails == null ? null : new PSCertificateDetails(
2016-07-29 13:02:49 -07:00
GetPropertyValue<string>(certDetails, "Subject"),
GetPropertyValue<string>(certDetails, "IssuerName"),
GetPropertyValue<string>(certDetails, "IssuerThumbprint"));
PSIdentity psIdentity = new PSIdentity(
GetPropertyValue<string>(userIdentity, "AuthenticationType"),
GetPropertyValue<bool>(userIdentity, "IsAuthenticated"),
GetPropertyValue<string>(userIdentity, "Name"),
psCertDetails);
PSPrincipal psPrincipal = new PSPrincipal(psIdentity, WindowsIdentity.GetCurrent());
PSSenderInfo senderInfo = new PSSenderInfo(psPrincipal, GetPropertyValue<string>(pso, "ConnectionString"));
senderInfo.ClientTimeZone = TimeZoneInfo.Local;
2016-07-29 13:02:49 -07:00
senderInfo.ApplicationArguments = GetPropertyValue<PSPrimitiveDictionary>(pso, "ApplicationArguments");
2016-07-29 13:02:49 -07:00
return senderInfo;
}
private static System.Security.Cryptography.X509Certificates.X509Certificate2 RehydrateX509Certificate2(PSObject pso)
{
byte[] rawData = GetPropertyValue<byte[]>(pso, "RawData");
return new System.Security.Cryptography.X509Certificates.X509Certificate2(rawData);
}
private static System.Security.Cryptography.X509Certificates.X500DistinguishedName RehydrateX500DistinguishedName(PSObject pso)
{
byte[] rawData = GetPropertyValue<byte[]>(pso, "RawData");
return new System.Security.Cryptography.X509Certificates.X500DistinguishedName(rawData);
}
private static System.Net.IPAddress RehydrateIPAddress(PSObject pso)
{
string s = pso.ToString();
return System.Net.IPAddress.Parse(s);
}
private static MailAddress RehydrateMailAddress(PSObject pso)
{
string s = pso.ToString();
return new MailAddress(s);
}
2016-07-29 13:02:49 -07:00
private static T RehydrateObjectSecurity<T>(PSObject pso)
where T : System.Security.AccessControl.ObjectSecurity, new()
{
string sddl = GetPropertyValue<string>(pso, "SDDL");
2016-07-29 13:02:49 -07:00
T t = new T();
t.SetSecurityDescriptorSddlForm(sddl);
return t;
}
2016-07-29 13:02:49 -07:00
#endregion
2016-07-29 13:02:49 -07:00
#region Rehydration of types needed by implicit remoting
2016-07-29 13:02:49 -07:00
/// <summary>
/// Gets the boolean properties of ParameterSetMetadata object encoded as an integer.
2016-07-29 13:02:49 -07:00
/// </summary>
/// <param name="instance">
/// The PSObject for which to obtain the flags
/// </param>
/// <returns>
/// Boolean properties of ParameterSetMetadata object encoded as an integer
/// </returns>
public static UInt32 GetParameterSetMetadataFlags(PSObject instance)
{
if (instance == null)
2016-07-29 13:02:49 -07:00
{
2020-05-19 19:57:52 +01:00
throw PSTraceSource.NewArgumentNullException(nameof(instance));
2016-07-29 13:02:49 -07:00
}
if (!(instance.BaseObject is ParameterSetMetadata parameterSetMetadata))
2016-07-29 13:02:49 -07:00
{
2020-05-19 19:57:52 +01:00
throw PSTraceSource.NewArgumentNullException(nameof(instance));
2016-07-29 13:02:49 -07:00
}
2016-07-29 13:02:49 -07:00
return (UInt32)(parameterSetMetadata.Flags);
}
/// <summary>
/// Gets the full remoting serialized PSObject for the InvocationInfo property
/// of the DebuggerStopEventArgs type.
/// </summary>
/// <param name="instance">InvocationInfo instance.</param>
/// <returns>PSObject containing serialized InvocationInfo.</returns>
public static PSObject GetInvocationInfo(PSObject instance)
{
if (instance == null)
{
2020-05-19 19:57:52 +01:00
throw PSTraceSource.NewArgumentNullException(nameof(instance));
}
if (!(instance.BaseObject is DebuggerStopEventArgs dbgStopEventArgs))
{
2020-05-19 19:57:52 +01:00
throw PSTraceSource.NewArgumentNullException(nameof(instance));
}
if (dbgStopEventArgs.InvocationInfo == null)
{
return null;
}
PSObject psoInvocationInfo = new PSObject();
dbgStopEventArgs.InvocationInfo.ToPSObjectForRemoting(psoInvocationInfo);
return psoInvocationInfo;
}
private static ParameterSetMetadata RehydrateParameterSetMetadata(PSObject pso)
{
int position = GetPropertyValue<int>(pso, "Position");
UInt32 flags = GetPropertyValue<UInt32>(pso, "Flags");
string helpMessage = GetPropertyValue<string>(pso, "HelpMessage");
return new ParameterSetMetadata(position, (ParameterSetMetadata.ParameterFlags)flags, helpMessage);
}
2016-07-29 13:02:49 -07:00
private static DisplayEntry RehydrateDisplayEntry(PSObject deserializedDisplayEntry)
{
var result = new DisplayEntry
{
Value = GetPropertyValue<string>(deserializedDisplayEntry, "Value"),
ValueType = GetPropertyValue<DisplayEntryValueType>(deserializedDisplayEntry, "ValueType")
};
return result;
}
2016-07-29 13:02:49 -07:00
private static EntrySelectedBy RehydrateEntrySelectedBy(PSObject deserializedEsb)
{
var result = new EntrySelectedBy
{
TypeNames = RehydrateList<List<string>, string>(deserializedEsb, "TypeNames", RehydrationFlags.MissingPropertyOk),
SelectionCondition = RehydrateList<List<DisplayEntry>, DisplayEntry>(deserializedEsb, "SelectionCondition", RehydrationFlags.MissingPropertyOk)
};
return result;
}
2016-07-29 13:02:49 -07:00
private static WideControlEntryItem RehydrateWideControlEntryItem(PSObject deserializedEntryItem)
{
var entrySelectedBy = GetPropertyValue<EntrySelectedBy>(deserializedEntryItem, "EntrySelectedBy", RehydrationFlags.MissingPropertyOk);
if (entrySelectedBy == null)
2016-07-29 13:02:49 -07:00
{
var selectedBy = RehydrateList<List<string>, string>(deserializedEntryItem, "SelectedBy", RehydrationFlags.MissingPropertyOk);
entrySelectedBy = EntrySelectedBy.Get(selectedBy, null);
}
2016-07-29 13:02:49 -07:00
var result = new WideControlEntryItem
{
DisplayEntry = GetPropertyValue<DisplayEntry>(deserializedEntryItem, "DisplayEntry"),
EntrySelectedBy = entrySelectedBy,
FormatString = GetPropertyValue<string>(deserializedEntryItem, "FormatString", RehydrationFlags.MissingPropertyOk),
2016-07-29 13:02:49 -07:00
};
return result;
}
2016-07-29 13:02:49 -07:00
private static ListControlEntryItem RehydrateListControlEntryItem(PSObject deserializedEntryItem)
{
var result = new ListControlEntryItem
{
DisplayEntry = GetPropertyValue<DisplayEntry>(deserializedEntryItem, "DisplayEntry"),
ItemSelectionCondition = GetPropertyValue<DisplayEntry>(deserializedEntryItem, "ItemSelectionCondition", RehydrationFlags.MissingPropertyOk),
FormatString = GetPropertyValue<string>(deserializedEntryItem, "FormatString", RehydrationFlags.MissingPropertyOk),
2016-07-29 13:02:49 -07:00
Label = GetPropertyValue<string>(deserializedEntryItem, "Label", RehydrationFlags.NullValueOk)
};
return result;
}
2016-07-29 13:02:49 -07:00
private static ListControlEntry RehydrateListControlEntry(PSObject deserializedEntry)
{
var entrySelectedBy = GetPropertyValue<EntrySelectedBy>(deserializedEntry, "EntrySelectedBy", RehydrationFlags.MissingPropertyOk);
if (entrySelectedBy == null)
2016-07-29 13:02:49 -07:00
{
var selectedBy = RehydrateList<List<string>, string>(deserializedEntry, "SelectedBy", RehydrationFlags.MissingPropertyOk);
entrySelectedBy = EntrySelectedBy.Get(selectedBy, null);
}
2016-07-29 13:02:49 -07:00
var result = new ListControlEntry
{
Items = RehydrateList<List<ListControlEntryItem>, ListControlEntryItem>(deserializedEntry, "Items", RehydrationFlags.NullValueBad),
EntrySelectedBy = entrySelectedBy
};
return result;
}
2016-07-29 13:02:49 -07:00
private static TableControlColumnHeader RehydrateTableControlColumnHeader(PSObject deserializedHeader)
{
var result = new TableControlColumnHeader
{
Alignment = GetPropertyValue<Alignment>(deserializedHeader, "Alignment"),
Label = GetPropertyValue<string>(deserializedHeader, "Label", RehydrationFlags.NullValueOk),
Width = GetPropertyValue<int>(deserializedHeader, "Width")
};
return result;
}
2016-07-29 13:02:49 -07:00
private static TableControlColumn RehydrateTableControlColumn(PSObject deserializedColumn)
{
var result = new TableControlColumn
{
Alignment = GetPropertyValue<Alignment>(deserializedColumn, "Alignment"),
DisplayEntry = GetPropertyValue<DisplayEntry>(deserializedColumn, "DisplayEntry"),
FormatString = GetPropertyValue<string>(deserializedColumn, "FormatString", RehydrationFlags.MissingPropertyOk)
};
return result;
}
2016-07-29 13:02:49 -07:00
private static TableControlRow RehydrateTableControlRow(PSObject deserializedRow)
{
var result = new TableControlRow
{
Wrap = GetPropertyValue<bool>(deserializedRow, "Wrap", RehydrationFlags.MissingPropertyOk),
SelectedBy = GetPropertyValue<EntrySelectedBy>(deserializedRow, "EntrySelectedBy", RehydrationFlags.MissingPropertyOk),
2016-07-29 13:02:49 -07:00
Columns = RehydrateList<List<TableControlColumn>, TableControlColumn>(deserializedRow, "Columns", RehydrationFlags.NullValueBad)
};
return result;
}
2016-07-29 13:02:49 -07:00
private static CustomControlEntry RehydrateCustomControlEntry(PSObject deserializedEntry)
{
var result = new CustomControlEntry
{
CustomItems = RehydrateList<List<CustomItemBase>, CustomItemBase>(deserializedEntry, "CustomItems", RehydrationFlags.MissingPropertyBad),
SelectedBy = GetPropertyValue<EntrySelectedBy>(deserializedEntry, "SelectedBy", RehydrationFlags.MissingPropertyOk)
2016-07-29 13:02:49 -07:00
};
return result;
}
2016-07-29 13:02:49 -07:00
private static CustomItemBase RehydrateCustomItemBase(PSObject deserializedItem)
{
CustomItemBase result;
2016-07-29 13:02:49 -07:00
if (Deserializer.IsDeserializedInstanceOfType(deserializedItem, typeof(CustomItemNewline)))
{
result = new CustomItemNewline
{
Count = GetPropertyValue<int>(deserializedItem, "Count", RehydrationFlags.MissingPropertyBad)
};
}
else if (Deserializer.IsDeserializedInstanceOfType(deserializedItem, typeof(CustomItemText)))
{
result = new CustomItemText
{
Text = GetPropertyValue<string>(deserializedItem, "Text", RehydrationFlags.MissingPropertyBad)
};
}
else if (Deserializer.IsDeserializedInstanceOfType(deserializedItem, typeof(CustomItemFrame)))
{
result = new CustomItemFrame
{
FirstLineHanging = GetPropertyValue<uint>(deserializedItem, "FirstLineHanging"),
FirstLineIndent = GetPropertyValue<uint>(deserializedItem, "FirstLineIndent"),
RightIndent = GetPropertyValue<uint>(deserializedItem, "RightIndent"),
LeftIndent = GetPropertyValue<uint>(deserializedItem, "LeftIndent"),
CustomItems = RehydrateList<List<CustomItemBase>, CustomItemBase>(deserializedItem, "CustomItems", RehydrationFlags.MissingPropertyBad)
2016-07-29 13:02:49 -07:00
};
}
else if (Deserializer.IsDeserializedInstanceOfType(deserializedItem, typeof(CustomItemExpression)))
{
result = new CustomItemExpression
{
EnumerateCollection = GetPropertyValue<bool>(deserializedItem, "EnumerateCollection"),
CustomControl = GetPropertyValue<CustomControl>(deserializedItem, "CustomControl", RehydrationFlags.MissingPropertyOk),
Expression = GetPropertyValue<DisplayEntry>(deserializedItem, "Expression", RehydrationFlags.MissingPropertyOk),
ItemSelectionCondition = GetPropertyValue<DisplayEntry>(deserializedItem, "ItemSelectionCondition", RehydrationFlags.MissingPropertyOk)
2016-07-29 13:02:49 -07:00
};
}
else
{
2020-05-19 19:57:52 +01:00
throw PSTraceSource.NewArgumentException(nameof(deserializedItem));
2016-07-29 13:02:49 -07:00
}
2016-07-29 13:02:49 -07:00
return result;
}
2016-07-29 13:02:49 -07:00
private static PSControl RehydratePSControl(PSObject deserializedControl)
{
// Earlier versions of PowerShell did not have all of the possible properties in a control, so we must
// use MissingPropertyOk to allow for connections to those older endpoints.
2016-07-29 13:02:49 -07:00
PSControl result;
if (Deserializer.IsDeserializedInstanceOfType(deserializedControl, typeof(TableControl)))
{
var tableControl = new TableControl
{
AutoSize = GetPropertyValue<bool>(deserializedControl, "AutoSize", RehydrationFlags.MissingPropertyOk),
HideTableHeaders = GetPropertyValue<bool>(deserializedControl, "HideTableHeaders", RehydrationFlags.MissingPropertyOk),
2016-07-29 13:02:49 -07:00
Headers = RehydrateList<List<TableControlColumnHeader>, TableControlColumnHeader>(deserializedControl, "Headers", RehydrationFlags.NullValueBad),
Rows = RehydrateList<List<TableControlRow>, TableControlRow>(deserializedControl, "Rows", RehydrationFlags.NullValueBad)
};
result = tableControl;
}
else if (Deserializer.IsDeserializedInstanceOfType(deserializedControl, typeof(ListControl)))
{
var listControl = new ListControl
{
Entries = RehydrateList<List<ListControlEntry>, ListControlEntry>(deserializedControl, "Entries", RehydrationFlags.NullValueBad)
};
result = listControl;
}
else if (Deserializer.IsDeserializedInstanceOfType(deserializedControl, typeof(WideControl)))
{
var wideControl = new WideControl
{
AutoSize = GetPropertyValue<bool>(deserializedControl, "Alignment", RehydrationFlags.MissingPropertyOk),
Columns = GetPropertyValue<uint>(deserializedControl, "Columns"),
Entries = RehydrateList<List<WideControlEntryItem>, WideControlEntryItem>(deserializedControl, "Entries", RehydrationFlags.NullValueBad)
};
result = wideControl;
}
else if (Deserializer.IsDeserializedInstanceOfType(deserializedControl, typeof(CustomControl)))
{
var customControl = new CustomControl
{
Entries = RehydrateList<List<CustomControlEntry>, CustomControlEntry>(deserializedControl, "Entries", RehydrationFlags.NullValueBad)
};
result = customControl;
}
else
{
throw PSTraceSource.NewArgumentException("pso");
}
2016-07-29 13:02:49 -07:00
result.GroupBy = GetPropertyValue<PSControlGroupBy>(deserializedControl, "GroupBy", RehydrationFlags.MissingPropertyOk);
result.OutOfBand = GetPropertyValue<bool>(deserializedControl, "OutOfBand", RehydrationFlags.MissingPropertyOk);
return result;
}
2016-07-29 13:02:49 -07:00
private static PSControlGroupBy RehydrateGroupBy(PSObject deserializedGroupBy)
{
var result = new PSControlGroupBy
{
CustomControl = GetPropertyValue<CustomControl>(deserializedGroupBy, "CustomControl", RehydrationFlags.MissingPropertyOk),
Expression = GetPropertyValue<DisplayEntry>(deserializedGroupBy, "Expression", RehydrationFlags.MissingPropertyOk),
Label = GetPropertyValue<string>(deserializedGroupBy, "Label", RehydrationFlags.NullValueOk)
};
return result;
}
/// <summary>
/// Gets the boolean properties of ParameterSetMetadata object encoded as an integer.
/// </summary>
/// <param name="instance">
/// The PSObject for which to obtain the flags
/// </param>
/// <returns>
/// Boolean properties of ParameterSetMetadata object encoded as an integer
/// </returns>
public static Guid GetFormatViewDefinitionInstanceId(PSObject instance)
2016-07-29 13:02:49 -07:00
{
if (instance == null)
2016-07-29 13:02:49 -07:00
{
2020-05-19 19:57:52 +01:00
throw PSTraceSource.NewArgumentNullException(nameof(instance));
2016-07-29 13:02:49 -07:00
}
if (!(instance.BaseObject is FormatViewDefinition formatViewDefinition))
2016-07-29 13:02:49 -07:00
{
2020-05-19 19:57:52 +01:00
throw PSTraceSource.NewArgumentNullException(nameof(instance));
2016-07-29 13:02:49 -07:00
}
2016-07-29 13:02:49 -07:00
return formatViewDefinition.InstanceId;
}
2016-07-29 13:02:49 -07:00
private static FormatViewDefinition RehydrateFormatViewDefinition(PSObject deserializedViewDefinition)
{
string name = GetPropertyValue<string>(deserializedViewDefinition, "Name");
Guid instanceId = GetPropertyValue<Guid>(deserializedViewDefinition, "InstanceId");
PSControl control = GetPropertyValue<PSControl>(deserializedViewDefinition, "Control");
2016-07-29 13:02:49 -07:00
return new FormatViewDefinition(name, control, instanceId);
}
2016-07-29 13:02:49 -07:00
private static ExtendedTypeDefinition RehydrateExtendedTypeDefinition(PSObject deserializedTypeDefinition)
{
// Prefer TypeNames to TypeName - as it was incorrect to create multiple ExtendedTypeDefinitions for a group of types.
// But if a new PowerShell connects to an old endpoint, TypeNames will be missing, so fall back to TypeName in that case.
2016-07-29 13:02:49 -07:00
string typeName;
var typeNames = RehydrateList<List<string>, string>(deserializedTypeDefinition, "TypeNames", RehydrationFlags.MissingPropertyOk);
if (typeNames == null || typeNames.Count == 0)
2016-07-29 13:02:49 -07:00
{
typeName = GetPropertyValue<string>(deserializedTypeDefinition, "TypeName");
2016-07-29 13:02:49 -07:00
}
else
2016-07-29 13:02:49 -07:00
{
typeName = typeNames[0];
2016-07-29 13:02:49 -07:00
}
2016-07-29 13:02:49 -07:00
List<FormatViewDefinition> viewDefinitions = RehydrateList<List<FormatViewDefinition>, FormatViewDefinition>(
deserializedTypeDefinition,
"FormatViewDefinition",
RehydrationFlags.NullValueBad);
2016-07-29 13:02:49 -07:00
var result = new ExtendedTypeDefinition(typeName, viewDefinitions);
if (typeNames != null && typeNames.Count > 1)
{
for (var i = 1; i < typeNames.Count; i++)
{
result.TypeNames.Add(typeNames[i]);
}
}
2016-07-29 13:02:49 -07:00
return result;
}
2016-07-29 13:02:49 -07:00
#endregion
}
}