4840 lines
179 KiB
C#
4840 lines
179 KiB
C#
// Copyright (c) Microsoft Corporation.
|
|
// Licensed under the MIT License.
|
|
|
|
using System.Collections.Concurrent;
|
|
using System.Collections.Generic;
|
|
using System.Collections.ObjectModel;
|
|
using System.ComponentModel;
|
|
using System.Diagnostics;
|
|
using System.Globalization;
|
|
using System.IO;
|
|
using System.Linq;
|
|
using System.Management.Automation.Host;
|
|
using System.Management.Automation.Internal;
|
|
using System.Management.Automation.Language;
|
|
using System.Reflection;
|
|
using System.Runtime.Serialization;
|
|
using System.Security;
|
|
using System.Security.Permissions;
|
|
using System.Xml;
|
|
|
|
using Dbg = System.Diagnostics.Debug;
|
|
|
|
#pragma warning disable 1634, 1691 // Stops compiler from warning about unknown warnings
|
|
|
|
namespace System.Management.Automation.Runspaces
|
|
{
|
|
// ReSharper disable RedundantCast
|
|
internal class TypesPs1xmlReader
|
|
{
|
|
public TypesPs1xmlReader(LoadContext context)
|
|
{
|
|
_context = context;
|
|
_reader = context.reader;
|
|
_readerLineInfo = (IXmlLineInfo)context.reader;
|
|
InitIDs();
|
|
}
|
|
|
|
private readonly LoadContext _context;
|
|
private readonly XmlReader _reader;
|
|
private readonly IXmlLineInfo _readerLineInfo;
|
|
|
|
#region Helpers
|
|
|
|
private string ReadElementString(string nodeName)
|
|
{
|
|
// This code is based on XmlReader.ReadElementString, which is not available in CoreCLR.
|
|
string result = string.Empty;
|
|
|
|
if (_reader.MoveToContent() != XmlNodeType.Element)
|
|
{
|
|
_context.AddError(_readerLineInfo.LineNumber, TypesXmlStrings.NodeShouldHaveInnerText, nodeName);
|
|
return null;
|
|
}
|
|
|
|
if (!_reader.IsEmptyElement)
|
|
{
|
|
_reader.Read();
|
|
while (_reader.NodeType == XmlNodeType.Text)
|
|
{
|
|
result += _reader.Value;
|
|
if (!_reader.Read())
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (_reader.NodeType != XmlNodeType.EndElement)
|
|
{
|
|
_context.AddError(_readerLineInfo.LineNumber, TypesXmlStrings.NodeShouldHaveInnerText, nodeName);
|
|
return null;
|
|
}
|
|
|
|
result = result.Trim();
|
|
_reader.Read();
|
|
}
|
|
else
|
|
{
|
|
_reader.Read();
|
|
}
|
|
|
|
if (string.IsNullOrWhiteSpace(result))
|
|
{
|
|
_context.AddError(_readerLineInfo.LineNumber, TypesXmlStrings.NodeShouldHaveInnerText, nodeName);
|
|
return null;
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
private bool? ReadIsHiddenAttribute()
|
|
{
|
|
bool? isHidden = default(bool?);
|
|
|
|
while (_reader.MoveToNextAttribute())
|
|
{
|
|
if (!isHidden.HasValue && (object)_reader.LocalName == (object)_idIsHidden)
|
|
{
|
|
isHidden = ToBoolean(_reader.Value);
|
|
}
|
|
|
|
// Unknown attributes are ignored.
|
|
}
|
|
|
|
return isHidden;
|
|
}
|
|
|
|
private void ReadIsHiddenAttributeNotSupported(string node)
|
|
{
|
|
if (ReadIsHiddenAttribute().HasValue)
|
|
{
|
|
_context.AddError(TypesXmlStrings.IsHiddenNotSupported, node, _idIsHidden);
|
|
}
|
|
}
|
|
|
|
private void ReadEndElement()
|
|
{
|
|
while (_reader.NodeType == XmlNodeType.Whitespace)
|
|
{
|
|
_reader.Skip();
|
|
}
|
|
|
|
if (_reader.NodeType == XmlNodeType.None)
|
|
{
|
|
_reader.Skip();
|
|
}
|
|
else
|
|
{
|
|
_reader.ReadEndElement();
|
|
}
|
|
}
|
|
|
|
private void UnknownNode(string node, string expectedNodes)
|
|
{
|
|
if (_reader.NodeType == XmlNodeType.Text)
|
|
{
|
|
_context.AddError(_readerLineInfo.LineNumber, TypesXmlStrings.NodeShouldNotHaveInnerText, node);
|
|
_reader.Read();
|
|
}
|
|
else
|
|
{
|
|
_context.AddError(_readerLineInfo.LineNumber, TypesXmlStrings.UnknownNode, _reader.LocalName, expectedNodes);
|
|
SkipUntillNodeEnd(_reader.LocalName);
|
|
}
|
|
}
|
|
|
|
private void SkipUntillNodeEnd(string nodeName)
|
|
{
|
|
while (_reader.Read())
|
|
{
|
|
if (_reader.IsStartElement() && _reader.LocalName.Equals(nodeName))
|
|
{
|
|
SkipUntillNodeEnd(nodeName);
|
|
}
|
|
else if ((_reader.NodeType == XmlNodeType.EndElement) && _reader.LocalName.Equals(nodeName))
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
private void NotMoreThanOnce(string node, string parent)
|
|
{
|
|
_context.AddError(_readerLineInfo.LineNumber, TypesXmlStrings.NotMoreThanOnceOne, node, parent);
|
|
}
|
|
|
|
private void NodeNotFound(int lineNumber, string node, string parent)
|
|
{
|
|
_context.AddError(lineNumber, TypesXmlStrings.NodeNotFoundOnce, node, parent);
|
|
}
|
|
|
|
private ScriptBlock GetScriptBlock(string text, int initialLine)
|
|
{
|
|
if (text == null)
|
|
{
|
|
return null;
|
|
}
|
|
|
|
ScriptBlock scriptBlock;
|
|
try
|
|
{
|
|
scriptBlock = _context.IsProductCode
|
|
? ScriptBlock.CreateDelayParsedScriptBlock(text, isProductCode: true)
|
|
: ScriptBlock.Create(text);
|
|
}
|
|
catch (ParseException pe)
|
|
{
|
|
_context.AddError(pe.Errors[0].Extent.StartLineNumber + initialLine - 1, pe.Errors[0].Message);
|
|
return null;
|
|
}
|
|
|
|
if (scriptBlock != null)
|
|
{
|
|
if (_context.IsFullyTrusted)
|
|
{
|
|
scriptBlock.LanguageMode = PSLanguageMode.FullLanguage;
|
|
}
|
|
}
|
|
|
|
return scriptBlock;
|
|
}
|
|
|
|
private Type ResolveType(string typeName, int line)
|
|
{
|
|
Exception exception;
|
|
var type = TypeResolver.ResolveType(typeName, out exception);
|
|
if (exception != null)
|
|
{
|
|
_context.AddError(line, exception.Message);
|
|
}
|
|
else if (type == null)
|
|
{
|
|
_context.AddError(line, ParserStrings.TypeNotFound, typeName);
|
|
}
|
|
|
|
return type;
|
|
}
|
|
|
|
private bool ToBoolean(string value)
|
|
{
|
|
value = value.Trim();
|
|
if (string.Equals(value, "true", StringComparison.OrdinalIgnoreCase))
|
|
{
|
|
return true;
|
|
}
|
|
|
|
if (string.Equals(value, "false", StringComparison.OrdinalIgnoreCase))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
_context.AddError(_readerLineInfo.LineNumber, TypesXmlStrings.ValueShouldBeTrueOrFalse, value);
|
|
return false;
|
|
}
|
|
|
|
private T Converter<T>(object value, string name)
|
|
{
|
|
T result;
|
|
|
|
if (!LanguagePrimitives.TryConvertTo(value, out result))
|
|
{
|
|
_context.AddError(TypesXmlStrings.ErrorConvertingNote, name, typeof(T));
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
private bool BoolConverter(object value, string name)
|
|
{
|
|
if (value is string s)
|
|
{
|
|
return ToBoolean(s);
|
|
}
|
|
|
|
return Converter<bool>(value, name);
|
|
}
|
|
|
|
private void CheckStandardNote<T>(TypeMemberData member, TypeData typeData, Action<TypeData, T> setter, Func<object, string, T> converter)
|
|
{
|
|
var note = member as NotePropertyData;
|
|
if (note != null)
|
|
{
|
|
T value;
|
|
if (note.Value.GetType() != typeof(T))
|
|
{
|
|
value = converter(note.Value, note.Name);
|
|
}
|
|
else
|
|
{
|
|
value = (T)note.Value;
|
|
}
|
|
|
|
setter(typeData, value);
|
|
}
|
|
else
|
|
{
|
|
_context.AddError(TypesXmlStrings.MemberShouldBeNote, member.Name);
|
|
}
|
|
}
|
|
|
|
private static bool CheckStandardPropertySet(TypeMemberData member, TypeData typeData, Action<TypeData, PropertySetData> setter)
|
|
{
|
|
var propertySet = member as PropertySetData;
|
|
if (propertySet != null)
|
|
{
|
|
setter(typeData, propertySet);
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
#endregion Helpers
|
|
|
|
public IEnumerable<TypeData> Read()
|
|
{
|
|
IEnumerable<TypeData> result = null;
|
|
_reader.MoveToContent();
|
|
if (_reader.NodeType == XmlNodeType.Element)
|
|
{
|
|
if ((object)_reader.LocalName == (object)_idTypes)
|
|
{
|
|
result = Read_Types();
|
|
}
|
|
}
|
|
|
|
if (result == null)
|
|
NodeNotFound(0, _idTypes, "Document");
|
|
|
|
return result;
|
|
}
|
|
|
|
private IEnumerable<TypeData> Read_Types()
|
|
{
|
|
ReadIsHiddenAttributeNotSupported(_idTypes);
|
|
_reader.MoveToElement();
|
|
|
|
if (_reader.IsEmptyElement)
|
|
{
|
|
_reader.Skip();
|
|
yield break;
|
|
}
|
|
|
|
_reader.ReadStartElement();
|
|
_reader.MoveToContent();
|
|
while (_reader.NodeType != XmlNodeType.EndElement && _reader.NodeType != XmlNodeType.None)
|
|
{
|
|
if (_reader.NodeType == XmlNodeType.Element)
|
|
{
|
|
if ((object)_reader.LocalName == (object)_idType)
|
|
{
|
|
var p = Read_Type();
|
|
if (p != null)
|
|
{
|
|
yield return p;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
UnknownNode(_idTypes, "Type");
|
|
}
|
|
}
|
|
else
|
|
{
|
|
UnknownNode(_idTypes, "Type");
|
|
}
|
|
|
|
_reader.MoveToContent();
|
|
}
|
|
|
|
ReadEndElement();
|
|
}
|
|
|
|
private TypeData Read_Type()
|
|
{
|
|
var errorCount = _context.errors.Count;
|
|
var lineNumber = _readerLineInfo.LineNumber;
|
|
|
|
MemberSetData standardMembers = null;
|
|
string name = null;
|
|
Collection<TypeMemberData> members = null;
|
|
Type typeConverter = null;
|
|
Type typeAdapter = null;
|
|
|
|
ReadIsHiddenAttributeNotSupported(_idType);
|
|
_reader.MoveToElement();
|
|
if (_reader.IsEmptyElement)
|
|
{
|
|
_reader.Skip();
|
|
}
|
|
else
|
|
{
|
|
_reader.ReadStartElement();
|
|
_reader.MoveToContent();
|
|
while (_reader.NodeType != XmlNodeType.EndElement && _reader.NodeType != XmlNodeType.None)
|
|
{
|
|
if (_reader.NodeType == XmlNodeType.Element)
|
|
{
|
|
if ((object)_reader.LocalName == (object)_idName)
|
|
{
|
|
if (name != null)
|
|
{
|
|
NotMoreThanOnce(_idName, _idType);
|
|
}
|
|
|
|
name = ReadElementString(_idName);
|
|
}
|
|
else if ((object)_reader.LocalName == (object)_idMembers)
|
|
{
|
|
if (members != null)
|
|
{
|
|
NotMoreThanOnce(_idMembers, _idType);
|
|
}
|
|
|
|
members = Read_Members(out standardMembers);
|
|
}
|
|
else if ((object)_reader.LocalName == (object)_idTypeConverter)
|
|
{
|
|
if (typeConverter != null)
|
|
{
|
|
NotMoreThanOnce(_idTypeConverter, _idType);
|
|
}
|
|
|
|
typeConverter = Read_TypeX(_idTypeConverter);
|
|
}
|
|
else if ((object)_reader.LocalName == (object)_idTypeAdapter)
|
|
{
|
|
if (typeAdapter != null)
|
|
{
|
|
NotMoreThanOnce(_idTypeAdapter, _idType);
|
|
}
|
|
|
|
typeAdapter = Read_TypeX(_idTypeAdapter);
|
|
}
|
|
else
|
|
{
|
|
UnknownNode(_idType, "Name,Members,TypeConverter,TypeAdapter");
|
|
}
|
|
}
|
|
else
|
|
{
|
|
UnknownNode(_idType, "Name,Members,TypeConverter,TypeAdapter");
|
|
}
|
|
|
|
_reader.MoveToContent();
|
|
}
|
|
|
|
ReadEndElement();
|
|
}
|
|
|
|
if (string.IsNullOrWhiteSpace(name))
|
|
{
|
|
NodeNotFound(lineNumber, _idName, _idType);
|
|
}
|
|
else if (members == null && typeConverter == null && typeAdapter == null)
|
|
{
|
|
_context.AddError(name, lineNumber, TypesXmlStrings.TypeNodeShouldHaveMembersOrTypeConverters);
|
|
}
|
|
|
|
if (_context.errors.Count != errorCount)
|
|
{
|
|
return null;
|
|
}
|
|
|
|
var typeData = new TypeData(name, true);
|
|
if (members != null)
|
|
{
|
|
foreach (var m in members)
|
|
{
|
|
typeData.Members.Add(m.Name, m);
|
|
}
|
|
}
|
|
|
|
typeData.TypeAdapter = typeAdapter;
|
|
typeData.TypeConverter = typeConverter;
|
|
|
|
if (standardMembers != null)
|
|
{
|
|
foreach (var m in standardMembers.Members)
|
|
{
|
|
if (m.Name.Equals(TypeTable.DefaultDisplayProperty, StringComparison.OrdinalIgnoreCase))
|
|
{
|
|
CheckStandardNote(m, typeData, static (t, v) => t.DefaultDisplayProperty = v, Converter<string>);
|
|
}
|
|
else if (m.Name.Equals(TypeTable.DefaultDisplayPropertySet, StringComparison.OrdinalIgnoreCase))
|
|
{
|
|
if (!CheckStandardPropertySet(m, typeData, (t, p) => t.DefaultDisplayPropertySet = p))
|
|
{
|
|
_context.AddError(TypesXmlStrings.MemberShouldHaveType, TypeTable.DefaultDisplayPropertySet, _idPropertySet);
|
|
}
|
|
}
|
|
else if (m.Name.Equals(TypeTable.DefaultKeyPropertySet, StringComparison.OrdinalIgnoreCase))
|
|
{
|
|
if (!CheckStandardPropertySet(m, typeData, (t, p) => t.DefaultKeyPropertySet = p))
|
|
{
|
|
_context.AddError(TypesXmlStrings.MemberShouldHaveType, TypeTable.DefaultKeyPropertySet, _idPropertySet);
|
|
}
|
|
}
|
|
else if (m.Name.Equals(TypeTable.SerializationMethodNode, StringComparison.OrdinalIgnoreCase))
|
|
{
|
|
CheckStandardNote(m, typeData, static (t, v) => t.SerializationMethod = v, Converter<string>);
|
|
}
|
|
else if (m.Name.Equals(TypeTable.SerializationDepth, StringComparison.OrdinalIgnoreCase))
|
|
{
|
|
CheckStandardNote(m, typeData, static (t, v) => t.SerializationDepth = v, Converter<uint>);
|
|
}
|
|
else if (m.Name.Equals(TypeTable.StringSerializationSource, StringComparison.OrdinalIgnoreCase))
|
|
{
|
|
var aliasData = m as AliasPropertyData;
|
|
if (aliasData != null)
|
|
{
|
|
typeData.StringSerializationSource = aliasData.ReferencedMemberName;
|
|
}
|
|
else
|
|
{
|
|
typeData.StringSerializationSourceProperty = m;
|
|
}
|
|
}
|
|
else if (m.Name.Equals(TypeTable.PropertySerializationSet, StringComparison.OrdinalIgnoreCase))
|
|
{
|
|
if (!CheckStandardPropertySet(m, typeData, (t, p) => t.PropertySerializationSet = p))
|
|
{
|
|
_context.AddError(TypesXmlStrings.MemberShouldHaveType, TypeTable.PropertySerializationSet, _idPropertySet);
|
|
}
|
|
}
|
|
else if (m.Name.Equals(TypeTable.InheritPropertySerializationSet, StringComparison.OrdinalIgnoreCase))
|
|
{
|
|
CheckStandardNote(m, typeData, static (t, v) => t.InheritPropertySerializationSet = v, BoolConverter);
|
|
}
|
|
else if (m.Name.Equals(TypeTable.TargetTypeForDeserialization, StringComparison.OrdinalIgnoreCase))
|
|
{
|
|
CheckStandardNote(m, typeData, static (t, v) => t.TargetTypeForDeserialization = v, Converter<Type>);
|
|
}
|
|
else
|
|
{
|
|
_context.AddError(TypesXmlStrings.NotAStandardMember, m.Name);
|
|
}
|
|
}
|
|
}
|
|
|
|
return typeData;
|
|
}
|
|
|
|
private Type Read_TypeX(string elementName)
|
|
{
|
|
var errorCount = _context.errors.Count;
|
|
var lineNumber = _readerLineInfo.LineNumber;
|
|
int typeLineNumber = 0;
|
|
|
|
string typeName = null;
|
|
|
|
ReadIsHiddenAttributeNotSupported(elementName);
|
|
_reader.MoveToElement();
|
|
if (_reader.IsEmptyElement)
|
|
{
|
|
_reader.Skip();
|
|
}
|
|
else
|
|
{
|
|
_reader.ReadStartElement();
|
|
_reader.MoveToContent();
|
|
while (_reader.NodeType != XmlNodeType.EndElement && _reader.NodeType != XmlNodeType.None)
|
|
{
|
|
if (_reader.NodeType == XmlNodeType.Element)
|
|
{
|
|
if ((object)_reader.LocalName == (object)_idTypeName)
|
|
{
|
|
if (typeName != null)
|
|
{
|
|
NotMoreThanOnce(_idTypeName, elementName);
|
|
}
|
|
|
|
typeLineNumber = _readerLineInfo.LineNumber;
|
|
typeName = ReadElementString(_idTypeName);
|
|
}
|
|
else
|
|
{
|
|
UnknownNode(elementName, "TypeName");
|
|
}
|
|
}
|
|
else
|
|
{
|
|
UnknownNode(elementName, "TypeName");
|
|
}
|
|
|
|
_reader.MoveToContent();
|
|
}
|
|
|
|
ReadEndElement();
|
|
}
|
|
|
|
if (string.IsNullOrWhiteSpace(typeName))
|
|
{
|
|
NodeNotFound(lineNumber, _idTypeName, elementName);
|
|
}
|
|
|
|
if (_context.errors.Count != errorCount)
|
|
{
|
|
return null;
|
|
}
|
|
|
|
return ResolveType(typeName, typeLineNumber);
|
|
}
|
|
|
|
private Collection<TypeMemberData> Read_Members(out MemberSetData standardMembers)
|
|
{
|
|
var errorCount = _context.errors.Count;
|
|
|
|
standardMembers = null;
|
|
var members = new Collection<TypeMemberData>();
|
|
|
|
ReadIsHiddenAttributeNotSupported(_idMembers);
|
|
_reader.MoveToElement();
|
|
if (_reader.IsEmptyElement)
|
|
{
|
|
_reader.Skip();
|
|
}
|
|
else
|
|
{
|
|
_reader.ReadStartElement();
|
|
_reader.MoveToContent();
|
|
while (_reader.NodeType != XmlNodeType.EndElement && _reader.NodeType != XmlNodeType.None)
|
|
{
|
|
if (_reader.NodeType == XmlNodeType.Element)
|
|
{
|
|
if ((object)_reader.LocalName == (object)_idNoteProperty)
|
|
{
|
|
var p = Read_NoteProperty();
|
|
if (p != null)
|
|
{
|
|
members.Add(p);
|
|
}
|
|
}
|
|
else if ((object)_reader.LocalName == (object)_idAliasProperty)
|
|
{
|
|
var p = Read_AliasProperty();
|
|
if (p != null)
|
|
{
|
|
members.Add(p);
|
|
}
|
|
}
|
|
else if ((object)_reader.LocalName == (object)_idScriptProperty)
|
|
{
|
|
var p = Read_ScriptProperty();
|
|
if (p != null)
|
|
{
|
|
members.Add(p);
|
|
}
|
|
}
|
|
else if ((object)_reader.LocalName == (object)_idCodeProperty)
|
|
{
|
|
var p = Read_CodeProperty();
|
|
if (p != null)
|
|
{
|
|
members.Add(p);
|
|
}
|
|
}
|
|
else if ((object)_reader.LocalName == (object)_idScriptMethod)
|
|
{
|
|
var p = Read_ScriptMethod();
|
|
if (p != null)
|
|
{
|
|
members.Add(p);
|
|
}
|
|
}
|
|
else if ((object)_reader.LocalName == (object)_idCodeMethod)
|
|
{
|
|
var p = Read_CodeMethod();
|
|
if (p != null)
|
|
{
|
|
members.Add(p);
|
|
}
|
|
}
|
|
else if ((object)_reader.LocalName == (object)_idPropertySet)
|
|
{
|
|
var p = Read_PropertySet();
|
|
if (p != null)
|
|
{
|
|
members.Add(p);
|
|
}
|
|
}
|
|
else if ((object)_reader.LocalName == (object)_idMemberSet)
|
|
{
|
|
var p = Read_MemberSet();
|
|
if (p != null)
|
|
{
|
|
if (p.Name.Equals(TypeTable.PSStandardMembers, StringComparison.OrdinalIgnoreCase))
|
|
{
|
|
standardMembers = p;
|
|
}
|
|
else
|
|
{
|
|
members.Add(p);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
UnknownNode(_idMembers, "NoteProperty,AliasProperty,ScriptProperty,CodeProperty,ScriptMethod,CodeMethod,PropertySet,MemberSet");
|
|
}
|
|
}
|
|
else
|
|
{
|
|
UnknownNode(_idMembers, "NoteProperty,AliasProperty,ScriptProperty,CodeProperty,ScriptMethod,CodeMethod,PropertySet,MemberSet");
|
|
}
|
|
|
|
_reader.MoveToContent();
|
|
}
|
|
|
|
ReadEndElement();
|
|
}
|
|
|
|
return _context.errors.Count == errorCount ? members : null;
|
|
}
|
|
|
|
private MemberSetData Read_MemberSet()
|
|
{
|
|
var errorCount = _context.errors.Count;
|
|
var lineNumber = _readerLineInfo.LineNumber;
|
|
|
|
string name = null;
|
|
Collection<TypeMemberData> members = null;
|
|
bool? inheritMembers = default(bool?);
|
|
|
|
bool? isHidden = ReadIsHiddenAttribute();
|
|
_reader.MoveToElement();
|
|
if (_reader.IsEmptyElement)
|
|
{
|
|
_reader.Skip();
|
|
}
|
|
else
|
|
{
|
|
_reader.ReadStartElement();
|
|
_reader.MoveToContent();
|
|
while (_reader.NodeType != XmlNodeType.EndElement && _reader.NodeType != XmlNodeType.None)
|
|
{
|
|
if (_reader.NodeType == XmlNodeType.Element)
|
|
{
|
|
if ((object)_reader.LocalName == (object)_idName)
|
|
{
|
|
if (name != null)
|
|
{
|
|
NotMoreThanOnce(_idName, _idMemberSet);
|
|
}
|
|
|
|
name = ReadElementString(_idName);
|
|
}
|
|
else if ((object)_reader.LocalName == (object)_idInheritMembers)
|
|
{
|
|
if (inheritMembers.HasValue)
|
|
{
|
|
NotMoreThanOnce(_idInheritMembers, _idMemberSet);
|
|
}
|
|
|
|
inheritMembers = ToBoolean(ReadElementString(_idMemberSet));
|
|
}
|
|
else if ((object)_reader.LocalName == (object)_idMembers)
|
|
{
|
|
if (members != null)
|
|
{
|
|
NotMoreThanOnce(_idMembers, _idMemberSet);
|
|
}
|
|
|
|
MemberSetData standardMembers;
|
|
members = Read_Members(out standardMembers);
|
|
if (standardMembers != null)
|
|
{
|
|
// Somewhat pointless - but if we see PSStandardMembers inside a memberset, it's
|
|
// really just another ordinary member.
|
|
members.Add(standardMembers);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
UnknownNode(_idMemberSet, "Name,InheritMembers,Members");
|
|
}
|
|
}
|
|
else
|
|
{
|
|
UnknownNode(_idMemberSet, "Name,InheritMembers,Members");
|
|
}
|
|
|
|
_reader.MoveToContent();
|
|
}
|
|
|
|
ReadEndElement();
|
|
}
|
|
|
|
if (string.IsNullOrWhiteSpace(name))
|
|
{
|
|
NodeNotFound(lineNumber, _idName, _idMemberSet);
|
|
}
|
|
|
|
// Somewhat pointlessly (backcompat), we allow a missing Member node
|
|
if (members == null)
|
|
{
|
|
members = new Collection<TypeMemberData>();
|
|
}
|
|
|
|
if (_context.errors.Count != errorCount)
|
|
{
|
|
return null;
|
|
}
|
|
|
|
var result = new MemberSetData(name, members)
|
|
{
|
|
IsHidden = isHidden.GetValueOrDefault()
|
|
};
|
|
|
|
if (inheritMembers.HasValue)
|
|
{
|
|
result.InheritMembers = inheritMembers.Value;
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
private PropertySetData Read_PropertySet()
|
|
{
|
|
var errorCount = _context.errors.Count;
|
|
var lineNumber = _readerLineInfo.LineNumber;
|
|
|
|
string name = null;
|
|
List<string> referencedProperties = null;
|
|
|
|
bool? isHidden = ReadIsHiddenAttribute();
|
|
_reader.MoveToElement();
|
|
if (_reader.IsEmptyElement)
|
|
{
|
|
_reader.Skip();
|
|
}
|
|
else
|
|
{
|
|
_reader.ReadStartElement();
|
|
_reader.MoveToContent();
|
|
while (_reader.NodeType != XmlNodeType.EndElement && _reader.NodeType != XmlNodeType.None)
|
|
{
|
|
if (_reader.NodeType == XmlNodeType.Element)
|
|
{
|
|
if ((object)_reader.LocalName == (object)_idName)
|
|
{
|
|
if (name != null)
|
|
{
|
|
NotMoreThanOnce(_idName, _idPropertySet);
|
|
}
|
|
|
|
name = ReadElementString(_idName);
|
|
}
|
|
else if ((object)_reader.LocalName == (object)_idReferencedProperties)
|
|
{
|
|
if (_reader.IsEmptyElement)
|
|
{
|
|
_reader.Skip();
|
|
}
|
|
else
|
|
{
|
|
_reader.ReadStartElement();
|
|
_reader.MoveToContent();
|
|
while (_reader.NodeType != XmlNodeType.EndElement && _reader.NodeType != XmlNodeType.None)
|
|
{
|
|
if (_reader.NodeType == XmlNodeType.Element)
|
|
{
|
|
if ((object)_reader.LocalName == (object)_idName)
|
|
{
|
|
if (referencedProperties == null)
|
|
{
|
|
referencedProperties = new List<string>(8);
|
|
}
|
|
|
|
referencedProperties.Add(ReadElementString(_idName));
|
|
}
|
|
else
|
|
{
|
|
UnknownNode(_idPropertySet, "Name");
|
|
}
|
|
}
|
|
else
|
|
{
|
|
UnknownNode(_idPropertySet, "Name");
|
|
}
|
|
|
|
_reader.MoveToContent();
|
|
}
|
|
|
|
ReadEndElement();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
UnknownNode(_idPropertySet, "Name,ReferencedProperties");
|
|
}
|
|
}
|
|
else
|
|
{
|
|
UnknownNode(_idPropertySet, "Name,ReferencedProperties");
|
|
}
|
|
|
|
_reader.MoveToContent();
|
|
}
|
|
|
|
ReadEndElement();
|
|
}
|
|
|
|
if (string.IsNullOrWhiteSpace(name))
|
|
NodeNotFound(lineNumber, _idName, _idPropertySet);
|
|
if (referencedProperties == null)
|
|
NodeNotFound(lineNumber, _idReferencedProperties, _idPropertySet);
|
|
|
|
if (_context.errors.Count != errorCount)
|
|
{
|
|
return null;
|
|
}
|
|
|
|
return new PropertySetData(referencedProperties)
|
|
{
|
|
Name = name,
|
|
IsHidden = isHidden.GetValueOrDefault()
|
|
};
|
|
}
|
|
|
|
private CodeMethodData Read_CodeMethod()
|
|
{
|
|
var errorCount = _context.errors.Count;
|
|
var lineNumber = _readerLineInfo.LineNumber;
|
|
var methodLineNumber = 0;
|
|
|
|
string name = null;
|
|
MethodInfo codeReference = null;
|
|
|
|
ReadIsHiddenAttributeNotSupported(_idCodeMethod);
|
|
_reader.MoveToElement();
|
|
if (_reader.IsEmptyElement)
|
|
{
|
|
_reader.Skip();
|
|
}
|
|
else
|
|
{
|
|
_reader.ReadStartElement();
|
|
_reader.MoveToContent();
|
|
while (_reader.NodeType != XmlNodeType.EndElement && _reader.NodeType != XmlNodeType.None)
|
|
{
|
|
if (_reader.NodeType == XmlNodeType.Element)
|
|
{
|
|
if ((object)_reader.LocalName == (object)_idName)
|
|
{
|
|
if (name != null)
|
|
{
|
|
NotMoreThanOnce(_idName, _idCodeMethod);
|
|
}
|
|
|
|
name = ReadElementString(_idName);
|
|
}
|
|
else if ((object)_reader.LocalName == (object)_idCodeReference)
|
|
{
|
|
if (codeReference != null)
|
|
{
|
|
NotMoreThanOnce(_idCodeReference, _idCodeMethod);
|
|
}
|
|
|
|
methodLineNumber = _readerLineInfo.LineNumber;
|
|
codeReference = Read_CodeReference();
|
|
if (codeReference == null)
|
|
_context.AddError(methodLineNumber, ExtendedTypeSystem.CodeMethodMethodFormat);
|
|
}
|
|
else
|
|
{
|
|
UnknownNode(_idCodeMethod, "Name,CodeReference");
|
|
}
|
|
}
|
|
else
|
|
{
|
|
UnknownNode(_idCodeMethod, "Name,CodeReference");
|
|
}
|
|
|
|
_reader.MoveToContent();
|
|
}
|
|
|
|
ReadEndElement();
|
|
}
|
|
|
|
if (string.IsNullOrWhiteSpace(name))
|
|
NodeNotFound(lineNumber, _idName, _idCodeMethod);
|
|
if (codeReference == null && methodLineNumber == 0)
|
|
NodeNotFound(lineNumber, _idCodeReference, _idCodeMethod);
|
|
if (codeReference != null && !PSCodeMethod.CheckMethodInfo(codeReference))
|
|
_context.AddError(methodLineNumber, ExtendedTypeSystem.CodeMethodMethodFormat);
|
|
|
|
if (_context.errors.Count != errorCount)
|
|
{
|
|
return null;
|
|
}
|
|
|
|
return new CodeMethodData(name, codeReference);
|
|
}
|
|
|
|
private MethodInfo Read_CodeReference()
|
|
{
|
|
var errorCount = _context.errors.Count;
|
|
var lineNumber = _readerLineInfo.LineNumber;
|
|
int typeLineNumber = 0;
|
|
int methodLineNumber = 0;
|
|
|
|
string typeName = null;
|
|
string methodName = null;
|
|
|
|
ReadIsHiddenAttributeNotSupported(_idCodeReference);
|
|
_reader.MoveToElement();
|
|
if (_reader.IsEmptyElement)
|
|
{
|
|
_reader.Skip();
|
|
}
|
|
else
|
|
{
|
|
_reader.ReadStartElement();
|
|
_reader.MoveToContent();
|
|
while (_reader.NodeType != XmlNodeType.EndElement && _reader.NodeType != XmlNodeType.None)
|
|
{
|
|
if (_reader.NodeType == XmlNodeType.Element)
|
|
{
|
|
if ((object)_reader.LocalName == (object)_idTypeName)
|
|
{
|
|
if (typeName != null)
|
|
{
|
|
NotMoreThanOnce(_idTypeName, _idCodeReference);
|
|
}
|
|
|
|
typeLineNumber = _readerLineInfo.LineNumber;
|
|
typeName = ReadElementString(_idTypeName);
|
|
}
|
|
else if ((object)_reader.LocalName == (object)_idMethodName)
|
|
{
|
|
if (methodName != null)
|
|
{
|
|
NotMoreThanOnce(_idMethodName, _idCodeReference);
|
|
}
|
|
|
|
methodLineNumber = _readerLineInfo.LineNumber;
|
|
methodName = ReadElementString(_idMethodName);
|
|
}
|
|
else
|
|
{
|
|
UnknownNode(_idCodeReference, "TypeName,MethodName");
|
|
}
|
|
}
|
|
else
|
|
{
|
|
UnknownNode(_idCodeReference, "TypeName,MethodName");
|
|
}
|
|
|
|
_reader.MoveToContent();
|
|
}
|
|
|
|
ReadEndElement();
|
|
}
|
|
|
|
if (string.IsNullOrWhiteSpace(typeName))
|
|
NodeNotFound(lineNumber, _idTypeName, _idCodeReference);
|
|
if (string.IsNullOrWhiteSpace(methodName))
|
|
NodeNotFound(lineNumber, _idMethodName, _idCodeReference);
|
|
|
|
if (_context.errors.Count != errorCount)
|
|
{
|
|
return null;
|
|
}
|
|
|
|
MethodInfo member = null;
|
|
var type = ResolveType(typeName, typeLineNumber);
|
|
if (type != null)
|
|
{
|
|
try
|
|
{
|
|
member = type.GetMethod(methodName, BindingFlags.Static | BindingFlags.Public | BindingFlags.IgnoreCase);
|
|
}
|
|
catch (AmbiguousMatchException e)
|
|
{
|
|
_context.AddError(methodLineNumber, e.Message);
|
|
}
|
|
}
|
|
|
|
return member;
|
|
}
|
|
|
|
private ScriptMethodData Read_ScriptMethod()
|
|
{
|
|
var errorCount = _context.errors.Count;
|
|
var lineNumber = _readerLineInfo.LineNumber;
|
|
|
|
string name = null;
|
|
string script = null;
|
|
int scriptLineNumber = 0;
|
|
|
|
ReadIsHiddenAttributeNotSupported(_idScriptMethod);
|
|
_reader.MoveToElement();
|
|
if (_reader.IsEmptyElement)
|
|
{
|
|
_reader.Skip();
|
|
}
|
|
else
|
|
{
|
|
_reader.ReadStartElement();
|
|
_reader.MoveToContent();
|
|
while (_reader.NodeType != XmlNodeType.EndElement && _reader.NodeType != XmlNodeType.None)
|
|
{
|
|
if (_reader.NodeType == XmlNodeType.Element)
|
|
{
|
|
if ((object)_reader.LocalName == (object)_idName)
|
|
{
|
|
if (name != null)
|
|
{
|
|
NotMoreThanOnce(_idName, _idScriptMethod);
|
|
}
|
|
|
|
name = ReadElementString(_idName);
|
|
}
|
|
else if ((object)_reader.LocalName == (object)_idScript)
|
|
{
|
|
if (script != null)
|
|
{
|
|
NotMoreThanOnce(_idScript, _idScriptMethod);
|
|
}
|
|
|
|
scriptLineNumber = _readerLineInfo.LineNumber;
|
|
script = ReadElementString(_idScript);
|
|
}
|
|
else
|
|
{
|
|
UnknownNode(_idScriptMethod, "Name,Script");
|
|
}
|
|
}
|
|
else
|
|
{
|
|
UnknownNode(_idScriptMethod, "Name,Script");
|
|
}
|
|
|
|
_reader.MoveToContent();
|
|
}
|
|
|
|
ReadEndElement();
|
|
}
|
|
|
|
if (string.IsNullOrWhiteSpace(name))
|
|
{
|
|
NodeNotFound(lineNumber, _idName, _idScriptMethod);
|
|
}
|
|
|
|
if (string.IsNullOrWhiteSpace(script))
|
|
{
|
|
NodeNotFound(lineNumber, _idScript, _idScriptMethod);
|
|
}
|
|
|
|
ScriptBlock scriptBlock = GetScriptBlock(script, scriptLineNumber);
|
|
|
|
if (_context.errors.Count != errorCount)
|
|
{
|
|
return null;
|
|
}
|
|
|
|
return new ScriptMethodData(name, scriptBlock);
|
|
}
|
|
|
|
private CodePropertyData Read_CodeProperty()
|
|
{
|
|
var errorCount = _context.errors.Count;
|
|
var lineNumber = _readerLineInfo.LineNumber;
|
|
var getterLineNumber = 0;
|
|
var setterLineNumber = 0;
|
|
|
|
string name = null;
|
|
MethodInfo getter = null;
|
|
MethodInfo setter = null;
|
|
|
|
var isHidden = ReadIsHiddenAttribute();
|
|
_reader.MoveToElement();
|
|
if (_reader.IsEmptyElement)
|
|
{
|
|
_reader.Skip();
|
|
}
|
|
else
|
|
{
|
|
_reader.ReadStartElement();
|
|
_reader.MoveToContent();
|
|
while (_reader.NodeType != XmlNodeType.EndElement && _reader.NodeType != XmlNodeType.None)
|
|
{
|
|
if (_reader.NodeType == XmlNodeType.Element)
|
|
{
|
|
if ((object)_reader.LocalName == (object)_idName)
|
|
{
|
|
if (name != null)
|
|
{
|
|
NotMoreThanOnce(_idName, _idCodeProperty);
|
|
}
|
|
|
|
name = ReadElementString(_idName);
|
|
}
|
|
else if ((object)_reader.LocalName == (object)_idGetCodeReference)
|
|
{
|
|
if (getter != null)
|
|
{
|
|
NotMoreThanOnce(_idGetCodeReference, _idCodeProperty);
|
|
}
|
|
|
|
getterLineNumber = _readerLineInfo.LineNumber;
|
|
getter = Read_CodeReference();
|
|
if (getter == null)
|
|
{
|
|
_context.AddError(getterLineNumber, ExtendedTypeSystem.CodePropertyGetterAndSetterNull);
|
|
}
|
|
}
|
|
else if ((object)_reader.LocalName == (object)_idSetCodeReference)
|
|
{
|
|
if (setter != null)
|
|
{
|
|
NotMoreThanOnce(_idSetCodeReference, _idCodeProperty);
|
|
}
|
|
|
|
setterLineNumber = _readerLineInfo.LineNumber;
|
|
setter = Read_CodeReference();
|
|
if (setter == null)
|
|
{
|
|
_context.AddError(setterLineNumber, ExtendedTypeSystem.CodePropertyGetterAndSetterNull);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
UnknownNode(_idCodeProperty, "Name,GetCodeReference,SetCodeReference");
|
|
}
|
|
}
|
|
else
|
|
{
|
|
UnknownNode(_idCodeProperty, "Name,GetCodeReference,SetCodeReference");
|
|
}
|
|
|
|
_reader.MoveToContent();
|
|
}
|
|
|
|
ReadEndElement();
|
|
}
|
|
|
|
if (string.IsNullOrEmpty(name))
|
|
{
|
|
NodeNotFound(lineNumber, _idName, _idCodeProperty);
|
|
}
|
|
|
|
if (getter == null && setter == null && getterLineNumber == 0 && setterLineNumber == 0)
|
|
{
|
|
_context.AddError(lineNumber, TypesXmlStrings.CodePropertyShouldHaveGetterOrSetter);
|
|
}
|
|
|
|
if (getter != null && !PSCodeProperty.CheckGetterMethodInfo(getter))
|
|
{
|
|
_context.AddError(getterLineNumber, ExtendedTypeSystem.CodePropertyGetterFormat);
|
|
}
|
|
|
|
if (setter != null && !PSCodeProperty.CheckSetterMethodInfo(setter, getter))
|
|
{
|
|
_context.AddError(setterLineNumber, ExtendedTypeSystem.CodePropertySetterFormat);
|
|
}
|
|
|
|
if (_context.errors.Count != errorCount)
|
|
{
|
|
return null;
|
|
}
|
|
|
|
return new CodePropertyData(name, getter, setter)
|
|
{
|
|
IsHidden = isHidden.GetValueOrDefault()
|
|
};
|
|
}
|
|
|
|
private ScriptPropertyData Read_ScriptProperty()
|
|
{
|
|
var errorCount = _context.errors.Count;
|
|
var lineNumber = _readerLineInfo.LineNumber;
|
|
var getterInitialLine = 0;
|
|
var setterInitialLine = 0;
|
|
|
|
string name = null;
|
|
string getScriptBlock = null;
|
|
string setScriptBlock = null;
|
|
|
|
var isHidden = ReadIsHiddenAttribute();
|
|
_reader.MoveToElement();
|
|
if (_reader.IsEmptyElement)
|
|
{
|
|
_reader.Skip();
|
|
}
|
|
else
|
|
{
|
|
_reader.ReadStartElement();
|
|
_reader.MoveToContent();
|
|
while (_reader.NodeType != XmlNodeType.EndElement && _reader.NodeType != XmlNodeType.None)
|
|
{
|
|
if (_reader.NodeType == XmlNodeType.Element)
|
|
{
|
|
if ((object)_reader.LocalName == (object)_idName)
|
|
{
|
|
if (name != null)
|
|
{
|
|
NotMoreThanOnce(_idName, _idScriptProperty);
|
|
}
|
|
|
|
name = ReadElementString(_idName);
|
|
}
|
|
else if ((object)_reader.LocalName == (object)_idGetScriptBlock)
|
|
{
|
|
if (getScriptBlock != null)
|
|
{
|
|
NotMoreThanOnce(_idGetScriptBlock, _idScriptProperty);
|
|
}
|
|
|
|
getterInitialLine = _readerLineInfo.LineNumber;
|
|
getScriptBlock = ReadElementString(_idGetScriptBlock);
|
|
}
|
|
else if ((object)_reader.LocalName == (object)_idSetScriptBlock)
|
|
{
|
|
if (setScriptBlock != null)
|
|
{
|
|
NotMoreThanOnce(_idSetScriptBlock, _idScriptProperty);
|
|
}
|
|
|
|
setterInitialLine = _readerLineInfo.LineNumber;
|
|
setScriptBlock = ReadElementString(_idSetScriptBlock);
|
|
}
|
|
else
|
|
{
|
|
UnknownNode(_idScriptProperty, "Name,GetScriptBlock,SetScriptBlock");
|
|
}
|
|
}
|
|
else
|
|
{
|
|
UnknownNode(_idScriptProperty, "Name,GetScriptBlock,SetScriptBlock");
|
|
}
|
|
|
|
_reader.MoveToContent();
|
|
}
|
|
|
|
ReadEndElement();
|
|
}
|
|
|
|
if (string.IsNullOrWhiteSpace(name))
|
|
{
|
|
NodeNotFound(lineNumber, _idName, _idScriptProperty);
|
|
}
|
|
|
|
if (string.IsNullOrWhiteSpace(getScriptBlock) && string.IsNullOrWhiteSpace(setScriptBlock))
|
|
{
|
|
_context.AddError(lineNumber, TypesXmlStrings.ScriptPropertyShouldHaveGetterOrSetter);
|
|
}
|
|
|
|
ScriptBlock getter = GetScriptBlock(getScriptBlock, getterInitialLine);
|
|
ScriptBlock setter = GetScriptBlock(setScriptBlock, setterInitialLine);
|
|
|
|
if (_context.errors.Count != errorCount)
|
|
{
|
|
return null;
|
|
}
|
|
|
|
return new ScriptPropertyData(name, getter, setter)
|
|
{
|
|
IsHidden = isHidden.GetValueOrDefault()
|
|
};
|
|
}
|
|
|
|
private AliasPropertyData Read_AliasProperty()
|
|
{
|
|
var errorCount = _context.errors.Count;
|
|
var lineNumber = _readerLineInfo.LineNumber;
|
|
var typeLineNumber = 0;
|
|
|
|
string name = null;
|
|
string referencedMemberName = null;
|
|
string typeName = null;
|
|
|
|
var isHidden = ReadIsHiddenAttribute();
|
|
_reader.MoveToElement();
|
|
if (_reader.IsEmptyElement)
|
|
{
|
|
_reader.Skip();
|
|
}
|
|
else
|
|
{
|
|
_reader.ReadStartElement();
|
|
_reader.MoveToContent();
|
|
while (_reader.NodeType != XmlNodeType.EndElement && _reader.NodeType != XmlNodeType.None)
|
|
{
|
|
if (_reader.NodeType == XmlNodeType.Element)
|
|
{
|
|
if ((object)_reader.LocalName == (object)_idName)
|
|
{
|
|
if (name != null)
|
|
{
|
|
NotMoreThanOnce(_idName, _idAliasProperty);
|
|
}
|
|
|
|
name = ReadElementString(_idAliasProperty);
|
|
}
|
|
else if ((object)_reader.LocalName == (object)_idReferencedMemberName)
|
|
{
|
|
if (referencedMemberName != null)
|
|
{
|
|
NotMoreThanOnce(_idReferencedMemberName, _idAliasProperty);
|
|
}
|
|
|
|
referencedMemberName = ReadElementString(_idReferencedMemberName);
|
|
}
|
|
else if ((object)_reader.LocalName == (object)_idTypeName)
|
|
{
|
|
if (typeName != null)
|
|
{
|
|
NotMoreThanOnce(_idTypeName, _idAliasProperty);
|
|
}
|
|
|
|
typeLineNumber = _readerLineInfo.LineNumber;
|
|
typeName = ReadElementString(_idTypeName);
|
|
}
|
|
else
|
|
{
|
|
UnknownNode(_idAliasProperty, "Name,ReferencedMemberName,TypeName");
|
|
}
|
|
}
|
|
else
|
|
{
|
|
UnknownNode(_idAliasProperty, "Name,ReferencedMemberName,TypeName");
|
|
}
|
|
|
|
_reader.MoveToContent();
|
|
}
|
|
|
|
ReadEndElement();
|
|
}
|
|
|
|
if (string.IsNullOrWhiteSpace(name))
|
|
{
|
|
NodeNotFound(lineNumber, _idName, _idAliasProperty);
|
|
}
|
|
|
|
if (string.IsNullOrWhiteSpace(referencedMemberName))
|
|
{
|
|
NodeNotFound(lineNumber, _idReferencedMemberName, _idAliasProperty);
|
|
}
|
|
|
|
Type convertToType = (typeName != null) ? ResolveType(typeName, typeLineNumber) : null;
|
|
|
|
if (_context.errors.Count != errorCount)
|
|
{
|
|
return null;
|
|
}
|
|
|
|
return new AliasPropertyData(name, referencedMemberName, convertToType)
|
|
{
|
|
IsHidden = isHidden.GetValueOrDefault()
|
|
};
|
|
}
|
|
|
|
private NotePropertyData Read_NoteProperty()
|
|
{
|
|
var errorCount = _context.errors.Count;
|
|
var lineNumber = _readerLineInfo.LineNumber;
|
|
int typeLineNumber = 0;
|
|
|
|
string name = null;
|
|
string valueAsString = null;
|
|
string typeName = null;
|
|
|
|
var isHidden = ReadIsHiddenAttribute();
|
|
_reader.MoveToElement();
|
|
if (_reader.IsEmptyElement)
|
|
{
|
|
_reader.Skip();
|
|
}
|
|
else
|
|
{
|
|
_reader.ReadStartElement();
|
|
_reader.MoveToContent();
|
|
while (_reader.NodeType != XmlNodeType.EndElement && _reader.NodeType != XmlNodeType.None)
|
|
{
|
|
if (_reader.NodeType == XmlNodeType.Element)
|
|
{
|
|
if ((object)_reader.LocalName == (object)_idName)
|
|
{
|
|
if (name != null)
|
|
{
|
|
NotMoreThanOnce(_idName, _idNoteProperty);
|
|
}
|
|
|
|
name = ReadElementString(_idName);
|
|
}
|
|
else if ((object)_reader.LocalName == (object)_idValue)
|
|
{
|
|
if (valueAsString != null)
|
|
{
|
|
NotMoreThanOnce(_idValue, _idNoteProperty);
|
|
}
|
|
|
|
valueAsString = ReadElementString(_idValue);
|
|
}
|
|
else if ((object)_reader.LocalName == (object)_idTypeName)
|
|
{
|
|
if (typeName != null)
|
|
{
|
|
NotMoreThanOnce(_idTypeName, _idNoteProperty);
|
|
}
|
|
|
|
typeLineNumber = _readerLineInfo.LineNumber;
|
|
typeName = ReadElementString(_idTypeName);
|
|
}
|
|
else
|
|
{
|
|
UnknownNode(_idNoteProperty, "Name,Value,TypeName");
|
|
}
|
|
}
|
|
else
|
|
{
|
|
UnknownNode(_idNoteProperty, "Name,Value,TypeName");
|
|
}
|
|
|
|
_reader.MoveToContent();
|
|
}
|
|
|
|
ReadEndElement();
|
|
}
|
|
|
|
if (string.IsNullOrWhiteSpace(name))
|
|
{
|
|
NodeNotFound(lineNumber, _idName, _idNoteProperty);
|
|
}
|
|
|
|
if (string.IsNullOrWhiteSpace(valueAsString))
|
|
{
|
|
NodeNotFound(lineNumber, _idValue, _idNoteProperty);
|
|
}
|
|
|
|
object value = valueAsString;
|
|
Type convertToType = (typeName != null) ? ResolveType(typeName, typeLineNumber) : null;
|
|
if (convertToType != null)
|
|
{
|
|
try
|
|
{
|
|
value = LanguagePrimitives.ConvertTo(value, convertToType, CultureInfo.InvariantCulture);
|
|
}
|
|
catch (PSInvalidCastException e)
|
|
{
|
|
_context.AddError(typeLineNumber, e.Message);
|
|
}
|
|
}
|
|
|
|
if (_context.errors.Count != errorCount)
|
|
{
|
|
return null;
|
|
}
|
|
|
|
return new NotePropertyData(name, value)
|
|
{
|
|
IsHidden = isHidden.GetValueOrDefault()
|
|
};
|
|
}
|
|
|
|
private string _idTypeName;
|
|
private string _idSetCodeReference;
|
|
private string _idScriptMethod;
|
|
private string _idValue;
|
|
private string _idScriptProperty;
|
|
private string _idIsHidden;
|
|
private string _idScript;
|
|
private string _idCodeMethod;
|
|
private string _idMemberSet;
|
|
private string _idCodeProperty;
|
|
private string _idMembers;
|
|
private string _idName;
|
|
private string _idGetCodeReference;
|
|
private string _idMethodName;
|
|
private string _idInheritMembers;
|
|
private string _idNoteProperty;
|
|
private string _idTypeAdapter;
|
|
private string _idSetScriptBlock;
|
|
private string _idPropertySet;
|
|
private string _idTypeConverter;
|
|
private string _idAliasProperty;
|
|
private string _idType;
|
|
private string _idCodeReference;
|
|
private string _idTypes;
|
|
private string _idReferencedProperties;
|
|
private string _idGetScriptBlock;
|
|
private string _idReferencedMemberName;
|
|
|
|
protected void InitIDs()
|
|
{
|
|
var xmlNameTable = _reader.NameTable;
|
|
_idTypeName = xmlNameTable.Add("TypeName");
|
|
_idSetCodeReference = xmlNameTable.Add("SetCodeReference");
|
|
_idScriptMethod = xmlNameTable.Add("ScriptMethod");
|
|
_idValue = xmlNameTable.Add("Value");
|
|
_idScriptProperty = xmlNameTable.Add("ScriptProperty");
|
|
_idIsHidden = xmlNameTable.Add("IsHidden");
|
|
_idScript = xmlNameTable.Add("Script");
|
|
_idCodeMethod = xmlNameTable.Add("CodeMethod");
|
|
_idMemberSet = xmlNameTable.Add("MemberSet");
|
|
_idCodeProperty = xmlNameTable.Add("CodeProperty");
|
|
_idMembers = xmlNameTable.Add("Members");
|
|
_idName = xmlNameTable.Add("Name");
|
|
_idGetCodeReference = xmlNameTable.Add("GetCodeReference");
|
|
_idMethodName = xmlNameTable.Add("MethodName");
|
|
_idInheritMembers = xmlNameTable.Add("InheritMembers");
|
|
_idNoteProperty = xmlNameTable.Add("NoteProperty");
|
|
_idTypeAdapter = xmlNameTable.Add("TypeAdapter");
|
|
_idSetScriptBlock = xmlNameTable.Add("SetScriptBlock");
|
|
_idPropertySet = xmlNameTable.Add("PropertySet");
|
|
_idTypeConverter = xmlNameTable.Add("TypeConverter");
|
|
_idAliasProperty = xmlNameTable.Add("AliasProperty");
|
|
_idType = xmlNameTable.Add("Type");
|
|
_idCodeReference = xmlNameTable.Add("CodeReference");
|
|
_idTypes = xmlNameTable.Add("Types");
|
|
_idReferencedProperties = xmlNameTable.Add("ReferencedProperties");
|
|
_idGetScriptBlock = xmlNameTable.Add("GetScriptBlock");
|
|
_idReferencedMemberName = xmlNameTable.Add("ReferencedMemberName");
|
|
}
|
|
}
|
|
// ReSharper restore RedundantCast
|
|
|
|
/// <summary>
|
|
/// Internal class to provide a Hashtable key out of a Collection of strings
|
|
/// preserving the evaluation of the key.
|
|
/// </summary>
|
|
internal class ConsolidatedString : Collection<string>
|
|
{
|
|
protected override void SetItem(int index, string item)
|
|
{
|
|
if (string.IsNullOrEmpty(item))
|
|
{
|
|
throw PSTraceSource.NewArgumentException(nameof(item));
|
|
}
|
|
|
|
base.SetItem(index, item);
|
|
UpdateKey();
|
|
}
|
|
|
|
protected override void ClearItems()
|
|
{
|
|
base.ClearItems();
|
|
UpdateKey();
|
|
}
|
|
|
|
protected override void InsertItem(int index, string item)
|
|
{
|
|
if (string.IsNullOrEmpty(item))
|
|
{
|
|
throw PSTraceSource.NewArgumentException(nameof(item));
|
|
}
|
|
|
|
base.InsertItem(index, item);
|
|
UpdateKey();
|
|
}
|
|
|
|
protected override void RemoveItem(int index)
|
|
{
|
|
base.RemoveItem(index);
|
|
UpdateKey();
|
|
}
|
|
|
|
internal string Key { get; private set; }
|
|
|
|
internal bool IsReadOnly { get { return ((ICollection<string>)this).IsReadOnly; } }
|
|
|
|
private void UpdateKey()
|
|
{
|
|
Key = string.Join("@@@", this);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Copy constructor (deep copy)
|
|
/// </summary>
|
|
/// <param name="other"></param>
|
|
public ConsolidatedString(ConsolidatedString other)
|
|
: base(new List<string>(other))
|
|
{
|
|
this.Key = other.Key;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Construct an optionally readonly list of strings.
|
|
/// </summary>
|
|
internal ConsolidatedString(IEnumerable<string> strings, bool interned)
|
|
: base(interned ? (IList<string>)new ReadOnlyCollection<string>(strings.ToList()) : strings.ToList())
|
|
{
|
|
UpdateKey();
|
|
}
|
|
|
|
public ConsolidatedString(IEnumerable<string> strings)
|
|
: base(strings.ToList())
|
|
{
|
|
for (int i = 0; i < this.Count; i++)
|
|
{
|
|
string str = this[i];
|
|
if (string.IsNullOrEmpty(str))
|
|
{
|
|
throw PSTraceSource.NewArgumentException(nameof(strings));
|
|
}
|
|
}
|
|
|
|
UpdateKey();
|
|
}
|
|
|
|
internal static readonly ConsolidatedString Empty = new ConsolidatedString(Array.Empty<string>());
|
|
|
|
internal static readonly IEqualityComparer<ConsolidatedString> EqualityComparer = new ConsolidatedStringEqualityComparer();
|
|
|
|
private sealed class ConsolidatedStringEqualityComparer : IEqualityComparer<ConsolidatedString>
|
|
{
|
|
bool IEqualityComparer<ConsolidatedString>.Equals(ConsolidatedString x, ConsolidatedString y)
|
|
{
|
|
return x.Key.Equals(y.Key, StringComparison.Ordinal);
|
|
}
|
|
|
|
int IEqualityComparer<ConsolidatedString>.GetHashCode(ConsolidatedString obj)
|
|
{
|
|
return obj.Key.GetHashCode();
|
|
}
|
|
}
|
|
}
|
|
|
|
internal class LoadContext
|
|
{
|
|
internal XmlReader reader;
|
|
internal ConcurrentBag<string> errors;
|
|
internal string fileName;
|
|
internal string PSSnapinName;
|
|
internal bool isFullyTrusted;
|
|
|
|
internal LoadContext(string PSSnapinName, string fileName, ConcurrentBag<string> errors)
|
|
{
|
|
this.reader = null;
|
|
this.fileName = fileName;
|
|
this.errors = errors;
|
|
this.PSSnapinName = PSSnapinName;
|
|
this.isFullyTrusted = false;
|
|
}
|
|
|
|
internal bool IsFullyTrusted
|
|
{
|
|
get { return isFullyTrusted; }
|
|
|
|
set { isFullyTrusted = value; }
|
|
}
|
|
|
|
internal bool IsProductCode { get; set; }
|
|
|
|
internal void AddError(string resourceString, params object[] formatArguments)
|
|
{
|
|
string errorMsg = StringUtil.Format(resourceString, formatArguments);
|
|
string errorLine = StringUtil.Format(TypesXmlStrings.FileError, this.PSSnapinName, this.fileName, errorMsg);
|
|
this.errors.Add(errorLine);
|
|
}
|
|
|
|
internal void AddError(int errorLineNumber, string resourceString, params object[] formatArguments)
|
|
{
|
|
string errorMsg = StringUtil.Format(resourceString, formatArguments);
|
|
string errorLine = StringUtil.Format(TypesXmlStrings.FileLineError, this.PSSnapinName, this.fileName, errorLineNumber, errorMsg);
|
|
this.errors.Add(errorLine);
|
|
}
|
|
|
|
internal void AddError(string typeName, int errorLineNumber, string resourceString, params object[] formatArguments)
|
|
{
|
|
string errorMsg = StringUtil.Format(resourceString, formatArguments);
|
|
string errorLine = StringUtil.Format(TypesXmlStrings.FileLineTypeError, this.PSSnapinName, this.fileName, errorLineNumber, typeName, errorMsg);
|
|
this.errors.Add(errorLine);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// This exception is used by TypeTable constructor to indicate errors
|
|
/// occured during construction time.
|
|
/// </summary>
|
|
[Serializable]
|
|
public class TypeTableLoadException : RuntimeException
|
|
{
|
|
private readonly Collection<string> _errors;
|
|
|
|
#region Constructors
|
|
|
|
/// <summary>
|
|
/// This is the default constructor.
|
|
/// </summary>
|
|
public TypeTableLoadException()
|
|
{
|
|
SetDefaultErrorRecord();
|
|
}
|
|
|
|
/// <summary>
|
|
/// This constructor takes a localized error message.
|
|
/// </summary>
|
|
/// <param name="message">
|
|
/// A localized error message.
|
|
/// </param>
|
|
public TypeTableLoadException(string message)
|
|
: base(message)
|
|
{
|
|
SetDefaultErrorRecord();
|
|
}
|
|
|
|
/// <summary>
|
|
/// This constructor takes a localized message and an inner exception.
|
|
/// </summary>
|
|
/// <param name="message">
|
|
/// Localized error message.
|
|
/// </param>
|
|
/// <param name="innerException">
|
|
/// Inner exception.
|
|
/// </param>
|
|
public TypeTableLoadException(string message, Exception innerException)
|
|
: base(message, innerException)
|
|
{
|
|
SetDefaultErrorRecord();
|
|
}
|
|
|
|
/// <summary>
|
|
/// This constructor takes a collection of errors occurred during construction
|
|
/// time.
|
|
/// </summary>
|
|
/// <param name="loadErrors">
|
|
/// The errors that occured
|
|
/// </param>
|
|
internal TypeTableLoadException(ConcurrentBag<string> loadErrors)
|
|
: base(TypesXmlStrings.TypeTableLoadErrors)
|
|
|
|
{
|
|
_errors = new Collection<string>(loadErrors.ToArray());
|
|
SetDefaultErrorRecord();
|
|
}
|
|
|
|
/// <summary>
|
|
/// This constructor is required by serialization.
|
|
/// </summary>
|
|
/// <param name="info"></param>
|
|
/// <param name="context"></param>
|
|
protected TypeTableLoadException(SerializationInfo info, StreamingContext context)
|
|
: base(info, context)
|
|
{
|
|
if (info == null)
|
|
{
|
|
throw new PSArgumentNullException(nameof(info));
|
|
}
|
|
|
|
int errorCount = info.GetInt32("ErrorCount");
|
|
if (errorCount > 0)
|
|
{
|
|
_errors = new Collection<string>();
|
|
for (int index = 0; index < errorCount; index++)
|
|
{
|
|
string key = string.Format(CultureInfo.InvariantCulture, "Error{0}", index);
|
|
_errors.Add(info.GetString(key));
|
|
}
|
|
}
|
|
}
|
|
|
|
#endregion Constructors
|
|
|
|
/// <summary>
|
|
/// Serializes the exception data.
|
|
/// </summary>
|
|
/// <param name="info">Serialization information.</param>
|
|
/// <param name="context">Streaming context.</param>
|
|
public override void GetObjectData(SerializationInfo info, StreamingContext context)
|
|
{
|
|
if (info == null)
|
|
{
|
|
throw new PSArgumentNullException(nameof(info));
|
|
}
|
|
|
|
base.GetObjectData(info, context);
|
|
|
|
// If there are simple fields, serialize them with info.AddValue
|
|
if (_errors != null)
|
|
{
|
|
int errorCount = _errors.Count;
|
|
info.AddValue("ErrorCount", errorCount);
|
|
|
|
for (int index = 0; index < errorCount; index++)
|
|
{
|
|
string key = string.Format(CultureInfo.InvariantCulture, "Error{0}", index);
|
|
info.AddValue(key, _errors[index]);
|
|
}
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Set the default ErrorRecord.
|
|
/// </summary>
|
|
protected void SetDefaultErrorRecord()
|
|
{
|
|
SetErrorCategory(ErrorCategory.InvalidData);
|
|
SetErrorId(typeof(TypeTableLoadException).FullName);
|
|
}
|
|
|
|
/// <summary>
|
|
/// The specific TypeTable load errors.
|
|
/// </summary>
|
|
public Collection<string> Errors
|
|
{
|
|
get
|
|
{
|
|
return _errors;
|
|
}
|
|
}
|
|
}
|
|
|
|
#region TypeData
|
|
|
|
/// <summary>
|
|
/// TypeData represent a Type Definition.
|
|
/// </summary>
|
|
public sealed class TypeData
|
|
{
|
|
internal const string NoteProperty = "NoteProperty";
|
|
internal const string AliasProperty = "AliasProperty";
|
|
internal const string ScriptProperty = "ScriptProperty";
|
|
internal const string CodeProperty = "CodeProperty";
|
|
internal const string ScriptMethod = "ScriptMethod";
|
|
internal const string CodeMethod = "CodeMethod";
|
|
internal const string PropertySet = "PropertySet";
|
|
internal const string MemberSet = "MemberSet";
|
|
|
|
private TypeData()
|
|
{
|
|
StandardMembers = new Dictionary<string, TypeMemberData>(StringComparer.OrdinalIgnoreCase);
|
|
Members = new Dictionary<string, TypeMemberData>(StringComparer.OrdinalIgnoreCase);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Initialize a TypeData instance by providing the typeName.
|
|
/// </summary>
|
|
/// <param name="typeName"></param>
|
|
public TypeData(string typeName) : this()
|
|
{
|
|
if (string.IsNullOrWhiteSpace(typeName))
|
|
throw PSTraceSource.NewArgumentNullException(nameof(typeName));
|
|
this.TypeName = typeName;
|
|
}
|
|
|
|
internal TypeData(string typeName, bool typesXml) : this()
|
|
{
|
|
this.fromTypesXmlFile = typesXml;
|
|
this.TypeName = typeName;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Initialize a TypeData instance by providing a Type.
|
|
/// </summary>
|
|
/// <param name="type"></param>
|
|
public TypeData(Type type) : this()
|
|
{
|
|
if (type == null)
|
|
{
|
|
throw PSTraceSource.NewArgumentNullException(nameof(type));
|
|
}
|
|
|
|
this.TypeName = type.FullName;
|
|
}
|
|
|
|
internal bool fromTypesXmlFile { get; }
|
|
|
|
/// <summary>
|
|
/// Get the TypeName.
|
|
/// </summary>
|
|
public string TypeName { get; }
|
|
|
|
/// <summary>
|
|
/// Get the members of this TypeData instance.
|
|
/// The Key of the dictionary is the member's name, and the Value is a TypeMemberData instance.
|
|
/// </summary>
|
|
public Dictionary<string, TypeMemberData> Members { get; }
|
|
|
|
/// <summary>
|
|
/// The type converter.
|
|
/// </summary>
|
|
public Type TypeConverter { get; set; }
|
|
|
|
/// <summary>
|
|
/// The type adapter.
|
|
/// </summary>
|
|
public Type TypeAdapter { get; set; }
|
|
|
|
/// <summary>
|
|
/// Set to true if override the existing definition.
|
|
/// </summary>
|
|
public bool IsOverride { get; set; }
|
|
|
|
#region StandardMember
|
|
|
|
internal Dictionary<string, TypeMemberData> StandardMembers { get; }
|
|
|
|
/// <summary>
|
|
/// The serializationMethod.
|
|
/// </summary>
|
|
public string SerializationMethod
|
|
{
|
|
get
|
|
{
|
|
return _serializationMethod;
|
|
}
|
|
|
|
set
|
|
{
|
|
_serializationMethod = value;
|
|
if (_serializationMethod == null)
|
|
{
|
|
StandardMembers.Remove(TypeTable.SerializationMethodNode);
|
|
return;
|
|
}
|
|
|
|
TypeMemberData typeMemberData;
|
|
if (StandardMembers.TryGetValue(TypeTable.SerializationMethodNode, out typeMemberData))
|
|
{
|
|
((NotePropertyData)typeMemberData).Value = _serializationMethod;
|
|
}
|
|
else
|
|
{
|
|
NotePropertyData note = new NotePropertyData(TypeTable.SerializationMethodNode, _serializationMethod);
|
|
StandardMembers.Add(TypeTable.SerializationMethodNode, note);
|
|
}
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// The targetTypeForDeserialization.
|
|
/// </summary>
|
|
public Type TargetTypeForDeserialization
|
|
{
|
|
get
|
|
{
|
|
return _targetTypeForDeserialization;
|
|
}
|
|
|
|
set
|
|
{
|
|
_targetTypeForDeserialization = value;
|
|
if (_targetTypeForDeserialization == null)
|
|
{
|
|
StandardMembers.Remove(TypeTable.TargetTypeForDeserialization);
|
|
return;
|
|
}
|
|
|
|
TypeMemberData typeMemberData;
|
|
if (StandardMembers.TryGetValue(TypeTable.TargetTypeForDeserialization, out typeMemberData))
|
|
{
|
|
((NotePropertyData)typeMemberData).Value = _targetTypeForDeserialization;
|
|
}
|
|
else
|
|
{
|
|
NotePropertyData note = new NotePropertyData(TypeTable.TargetTypeForDeserialization, _targetTypeForDeserialization);
|
|
StandardMembers.Add(TypeTable.TargetTypeForDeserialization, note);
|
|
}
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// The serializationDepth.
|
|
/// </summary>
|
|
public uint SerializationDepth
|
|
{
|
|
get
|
|
{
|
|
return _serializationDepth;
|
|
}
|
|
|
|
set
|
|
{
|
|
_serializationDepth = value;
|
|
|
|
TypeMemberData typeMemberData;
|
|
if (StandardMembers.TryGetValue(TypeTable.SerializationDepth, out typeMemberData))
|
|
{
|
|
((NotePropertyData)typeMemberData).Value = _serializationDepth;
|
|
}
|
|
else
|
|
{
|
|
NotePropertyData note = new NotePropertyData(TypeTable.SerializationDepth, _serializationDepth);
|
|
StandardMembers.Add(TypeTable.SerializationDepth, note);
|
|
}
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// The defaultDisplayProperty.
|
|
/// </summary>
|
|
public string DefaultDisplayProperty
|
|
{
|
|
get
|
|
{
|
|
return _defaultDisplayProperty;
|
|
}
|
|
|
|
set
|
|
{
|
|
_defaultDisplayProperty = value;
|
|
if (_defaultDisplayProperty == null)
|
|
{
|
|
StandardMembers.Remove(TypeTable.DefaultDisplayProperty);
|
|
return;
|
|
}
|
|
|
|
TypeMemberData typeMemberData;
|
|
if (StandardMembers.TryGetValue(TypeTable.DefaultDisplayProperty, out typeMemberData))
|
|
{
|
|
((NotePropertyData)typeMemberData).Value = _defaultDisplayProperty;
|
|
}
|
|
else
|
|
{
|
|
NotePropertyData note = new NotePropertyData(TypeTable.DefaultDisplayProperty, _defaultDisplayProperty);
|
|
StandardMembers.Add(TypeTable.DefaultDisplayProperty, note);
|
|
}
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// The InheritPropertySerializationSet.
|
|
/// </summary>
|
|
public bool InheritPropertySerializationSet
|
|
{
|
|
get
|
|
{
|
|
return _inheritPropertySerializationSet;
|
|
}
|
|
|
|
set
|
|
{
|
|
_inheritPropertySerializationSet = value;
|
|
TypeMemberData typeMemberData;
|
|
if (StandardMembers.TryGetValue(TypeTable.InheritPropertySerializationSet, out typeMemberData))
|
|
{
|
|
((NotePropertyData)typeMemberData).Value = _inheritPropertySerializationSet;
|
|
}
|
|
else
|
|
{
|
|
NotePropertyData note = new NotePropertyData(TypeTable.InheritPropertySerializationSet, _inheritPropertySerializationSet);
|
|
StandardMembers.Add(TypeTable.InheritPropertySerializationSet, note);
|
|
}
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// The stringSerializationSource.
|
|
/// </summary>
|
|
public string StringSerializationSource
|
|
{
|
|
get
|
|
{
|
|
return _stringSerializationSource;
|
|
}
|
|
|
|
set
|
|
{
|
|
if (value == null)
|
|
{
|
|
// If we set the standard member via StringSerializationSourceProperty, we
|
|
// don't want to remove it, so only remove the member if our previous
|
|
// backing field was not null.
|
|
if (_stringSerializationSource != null)
|
|
{
|
|
StandardMembers.Remove(TypeTable.StringSerializationSource);
|
|
}
|
|
|
|
_stringSerializationSource = null;
|
|
return;
|
|
}
|
|
|
|
_stringSerializationSource = value;
|
|
if (_stringSerializationSourceProperty != null)
|
|
{
|
|
// Remove existing property
|
|
StandardMembers.Remove(TypeTable.StringSerializationSource);
|
|
_stringSerializationSourceProperty = null;
|
|
}
|
|
|
|
TypeMemberData typeMemberData;
|
|
if (StandardMembers.TryGetValue(TypeTable.StringSerializationSource, out typeMemberData))
|
|
{
|
|
((AliasPropertyData)typeMemberData).ReferencedMemberName = _stringSerializationSource;
|
|
}
|
|
else
|
|
{
|
|
AliasPropertyData alias = new AliasPropertyData(TypeTable.StringSerializationSource, _stringSerializationSource);
|
|
StandardMembers.Add(TypeTable.StringSerializationSource, alias);
|
|
}
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// The StringSerializationSource when the property is not an AliasProperty.
|
|
/// If the property is an AliasProperty, prefer <see cref="StringSerializationSource"/>.
|
|
/// </summary>
|
|
public TypeMemberData StringSerializationSourceProperty
|
|
{
|
|
get
|
|
{
|
|
return _stringSerializationSourceProperty;
|
|
}
|
|
|
|
set
|
|
{
|
|
if (value == null)
|
|
{
|
|
// If we set the standard member via StringSerializationSource, we
|
|
// don't want to remove it, so only remove the member if our previous
|
|
// backing field was not null.
|
|
if (_stringSerializationSourceProperty != null)
|
|
{
|
|
StandardMembers.Remove(TypeTable.StringSerializationSource);
|
|
}
|
|
|
|
_stringSerializationSourceProperty = null;
|
|
return;
|
|
}
|
|
|
|
if (value is not NotePropertyData && value is not ScriptPropertyData && value is not CodePropertyData)
|
|
{
|
|
throw PSTraceSource.NewArgumentException("value");
|
|
}
|
|
|
|
// Remove existing property
|
|
StandardMembers.Remove(TypeTable.StringSerializationSource);
|
|
|
|
_stringSerializationSourceProperty = value;
|
|
_stringSerializationSource = null;
|
|
|
|
StandardMembers.Add(TypeTable.StringSerializationSource, value);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// The defaultDisplayPropertySet.
|
|
/// </summary>
|
|
public PropertySetData DefaultDisplayPropertySet
|
|
{
|
|
get
|
|
{
|
|
return _defaultDisplayPropertySet;
|
|
}
|
|
|
|
set
|
|
{
|
|
_defaultDisplayPropertySet = value;
|
|
if (_defaultDisplayPropertySet != null)
|
|
{
|
|
_defaultDisplayPropertySet.Name = TypeTable.DefaultDisplayPropertySet;
|
|
}
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// The defaultKeyPropertySet.
|
|
/// </summary>
|
|
public PropertySetData DefaultKeyPropertySet
|
|
{
|
|
get
|
|
{
|
|
return _defaultKeyPropertySet;
|
|
}
|
|
|
|
set
|
|
{
|
|
_defaultKeyPropertySet = value;
|
|
if (_defaultKeyPropertySet != null)
|
|
{
|
|
_defaultKeyPropertySet.Name = TypeTable.DefaultKeyPropertySet;
|
|
}
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// The PropertySerializationSet.
|
|
/// </summary>
|
|
public PropertySetData PropertySerializationSet
|
|
{
|
|
get
|
|
{
|
|
return _propertySerializationSet;
|
|
}
|
|
|
|
set
|
|
{
|
|
_propertySerializationSet = value;
|
|
if (_propertySerializationSet != null)
|
|
{
|
|
_propertySerializationSet.Name = TypeTable.PropertySerializationSet;
|
|
}
|
|
}
|
|
}
|
|
|
|
// They are of NoteProperty
|
|
private string _serializationMethod;
|
|
private Type _targetTypeForDeserialization;
|
|
private uint _serializationDepth;
|
|
private string _defaultDisplayProperty;
|
|
|
|
// InheritPropertySerializationSet should be true or false
|
|
private bool _inheritPropertySerializationSet;
|
|
|
|
// It is of AliasProperty
|
|
private string _stringSerializationSource;
|
|
|
|
// Except when it's not
|
|
private TypeMemberData _stringSerializationSourceProperty;
|
|
|
|
// They are of propertySet
|
|
private PropertySetData _defaultDisplayPropertySet;
|
|
private PropertySetData _defaultKeyPropertySet;
|
|
private PropertySetData _propertySerializationSet;
|
|
|
|
#endregion StandardMember
|
|
|
|
/// <summary>
|
|
/// Return a TypeData that is a copy of this one.
|
|
/// </summary>
|
|
/// <returns></returns>
|
|
public TypeData Copy()
|
|
{
|
|
TypeData newTypeData = new TypeData(this.TypeName);
|
|
|
|
foreach (KeyValuePair<string, TypeMemberData> entry in this.Members)
|
|
{
|
|
newTypeData.Members.Add(entry.Key, entry.Value.Copy());
|
|
}
|
|
|
|
newTypeData.TypeConverter = this.TypeConverter;
|
|
newTypeData.TypeAdapter = this.TypeAdapter;
|
|
newTypeData.IsOverride = this.IsOverride;
|
|
|
|
foreach (KeyValuePair<string, TypeMemberData> entry in this.StandardMembers)
|
|
{
|
|
switch (entry.Key)
|
|
{
|
|
case TypeTable.SerializationMethodNode:
|
|
newTypeData.SerializationMethod = this.SerializationMethod;
|
|
break;
|
|
case TypeTable.TargetTypeForDeserialization:
|
|
newTypeData.TargetTypeForDeserialization = this.TargetTypeForDeserialization;
|
|
break;
|
|
case TypeTable.SerializationDepth:
|
|
newTypeData.SerializationDepth = this.SerializationDepth;
|
|
break;
|
|
case TypeTable.DefaultDisplayProperty:
|
|
newTypeData.DefaultDisplayProperty = this.DefaultDisplayProperty;
|
|
break;
|
|
case TypeTable.InheritPropertySerializationSet:
|
|
newTypeData.InheritPropertySerializationSet = this.InheritPropertySerializationSet;
|
|
break;
|
|
case TypeTable.StringSerializationSource:
|
|
newTypeData.StringSerializationSource = this.StringSerializationSource;
|
|
break;
|
|
default:
|
|
Dbg.Fail("Standard members should at most contain six kinds of elements");
|
|
break;
|
|
}
|
|
}
|
|
|
|
newTypeData.DefaultDisplayPropertySet = this.DefaultDisplayPropertySet == null
|
|
? null
|
|
: (PropertySetData)this.DefaultDisplayPropertySet.Copy();
|
|
newTypeData.DefaultKeyPropertySet = this.DefaultKeyPropertySet == null
|
|
? null
|
|
: (PropertySetData)this.DefaultKeyPropertySet.Copy();
|
|
newTypeData.PropertySerializationSet = this.PropertySerializationSet == null
|
|
? null
|
|
: (PropertySetData)this.PropertySerializationSet.Copy();
|
|
|
|
return newTypeData;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// TypeMemberData is the base class for type members.
|
|
/// The type members derived from this class are:
|
|
/// NotePropertyData,
|
|
/// AliasPropertyData,
|
|
/// ScriptPropertyData,
|
|
/// CodePropertyData,
|
|
/// ScriptMethodData,
|
|
/// CodeMethodData.
|
|
/// </summary>
|
|
public abstract class TypeMemberData
|
|
{
|
|
/// <summary>
|
|
/// TypeMemberData constructor.
|
|
/// </summary>
|
|
/// <param name="name"></param>
|
|
internal TypeMemberData(string name)
|
|
{
|
|
if (string.IsNullOrWhiteSpace(name))
|
|
{
|
|
throw PSTraceSource.NewArgumentException(nameof(name));
|
|
}
|
|
|
|
Name = name;
|
|
}
|
|
|
|
internal TypeMemberData()
|
|
{
|
|
}
|
|
|
|
/// <summary>
|
|
/// The name of the member.
|
|
/// </summary>
|
|
public string Name { get; protected set; }
|
|
|
|
/// <summary>
|
|
/// Return a TypeMemberData that is a copy of this one.
|
|
/// </summary>
|
|
/// <returns></returns>
|
|
internal abstract TypeMemberData Copy();
|
|
|
|
internal abstract void Process(ConcurrentBag<string> errors, string typeName, PSMemberInfoInternalCollection<PSMemberInfo> membersCollection, bool isOverride);
|
|
}
|
|
|
|
/// <summary>
|
|
/// NotePropertyData represents a NoteProperty definition.
|
|
/// </summary>
|
|
[DebuggerDisplay("NoteProperty: {Name,nq} = {Value,nq}")]
|
|
public sealed class NotePropertyData : TypeMemberData
|
|
{
|
|
/// <summary>
|
|
/// NotePropertyData constructor.
|
|
/// </summary>
|
|
/// <param name="name"></param>
|
|
/// <param name="value"></param>
|
|
public NotePropertyData(string name, object value)
|
|
: base(name)
|
|
{
|
|
Value = value;
|
|
}
|
|
|
|
/// <summary>
|
|
/// The value of this NoteProperty.
|
|
/// </summary>
|
|
public object Value { get; set; }
|
|
|
|
/// <summary>
|
|
/// Set true if the member is supposed to be hidden.
|
|
/// </summary>
|
|
public bool IsHidden { get; set; }
|
|
|
|
/// <summary>
|
|
/// Return a new NotePropertyData that is a copy of this one.
|
|
/// </summary>
|
|
/// <returns></returns>
|
|
internal override TypeMemberData Copy()
|
|
{
|
|
NotePropertyData newNote = new NotePropertyData(this.Name, this.Value) { IsHidden = this.IsHidden };
|
|
return newNote;
|
|
}
|
|
|
|
internal override void Process(ConcurrentBag<string> errors, string typeName, PSMemberInfoInternalCollection<PSMemberInfo> membersCollection, bool isOverride)
|
|
{
|
|
TypeTable.ProcessNoteData(errors, typeName, this, membersCollection, isOverride);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// AliasPropertyData represents a AliasProperty definition.
|
|
/// </summary>
|
|
[DebuggerDisplay("AliasProperty: {Name,nq} = {ReferencedMemberName,nq}")]
|
|
public sealed class AliasPropertyData : TypeMemberData
|
|
{
|
|
/// <summary>
|
|
/// AliasPropertyData constructor.
|
|
/// </summary>
|
|
/// <param name="name"></param>
|
|
/// <param name="referencedMemberName"></param>
|
|
public AliasPropertyData(string name, string referencedMemberName)
|
|
: base(name)
|
|
{
|
|
ReferencedMemberName = referencedMemberName;
|
|
}
|
|
|
|
/// <summary>
|
|
/// AliasPropertyData constructor.
|
|
/// </summary>
|
|
/// <param name="name"></param>
|
|
/// <param name="referencedMemberName"></param>
|
|
/// <param name="type"></param>
|
|
public AliasPropertyData(string name, string referencedMemberName, Type type)
|
|
: base(name)
|
|
{
|
|
ReferencedMemberName = referencedMemberName;
|
|
MemberType = type;
|
|
}
|
|
|
|
/// <summary>
|
|
/// The name of the referenced member.
|
|
/// </summary>
|
|
public string ReferencedMemberName { get; set; }
|
|
|
|
/// <summary>
|
|
/// Specify the Type to which the referenced member value will be
|
|
/// converted to.
|
|
/// </summary>
|
|
public Type MemberType { get; set; }
|
|
|
|
/// <summary>
|
|
/// Set true if the member is supposed to be hidden.
|
|
/// </summary>
|
|
public bool IsHidden { get; set; }
|
|
|
|
/// <summary>
|
|
/// Return a new AliasPropertyData that is a copy of this one.
|
|
/// </summary>
|
|
/// <returns></returns>
|
|
internal override TypeMemberData Copy()
|
|
{
|
|
AliasPropertyData newAlias = new AliasPropertyData(this.Name, this.ReferencedMemberName, this.MemberType)
|
|
{
|
|
IsHidden = this.IsHidden
|
|
};
|
|
return newAlias;
|
|
}
|
|
|
|
internal override void Process(ConcurrentBag<string> errors, string typeName, PSMemberInfoInternalCollection<PSMemberInfo> membersCollection, bool isOverride)
|
|
{
|
|
TypeTable.ProcessAliasData(errors, typeName, this, membersCollection, isOverride);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// ScriptPropertyData represents a ScriptProperty definition.
|
|
/// </summary>
|
|
[DebuggerDisplay("ScriptProperty: {Name,nq}")]
|
|
public sealed class ScriptPropertyData : TypeMemberData
|
|
{
|
|
/// <summary>
|
|
/// Initialize the ScriptPropertyData as a read only property.
|
|
/// </summary>
|
|
/// <param name="name"></param>
|
|
/// <param name="getScriptBlock"></param>
|
|
public ScriptPropertyData(string name, ScriptBlock getScriptBlock)
|
|
: base(name)
|
|
{
|
|
GetScriptBlock = getScriptBlock;
|
|
}
|
|
|
|
/// <summary>
|
|
/// ScriptPropertyData constructor.
|
|
/// </summary>
|
|
/// <param name="name"></param>
|
|
/// <param name="getScriptBlock"></param>
|
|
/// <param name="setScriptBlock"></param>
|
|
public ScriptPropertyData(string name, ScriptBlock getScriptBlock, ScriptBlock setScriptBlock)
|
|
: base(name)
|
|
{
|
|
GetScriptBlock = getScriptBlock;
|
|
SetScriptBlock = setScriptBlock;
|
|
}
|
|
|
|
/// <summary>
|
|
/// The getter ScriptBlock.
|
|
/// </summary>
|
|
public ScriptBlock GetScriptBlock { get; set; }
|
|
|
|
/// <summary>
|
|
/// The setter ScriptBlock.
|
|
/// </summary>
|
|
public ScriptBlock SetScriptBlock { get; set; }
|
|
|
|
/// <summary>
|
|
/// Set true if the member is supposed to be hidden.
|
|
/// </summary>
|
|
public bool IsHidden { get; set; }
|
|
|
|
/// <summary>
|
|
/// Return a new ScriptPropertyData that is a copy of this one.
|
|
/// </summary>
|
|
/// <returns></returns>
|
|
internal override TypeMemberData Copy()
|
|
{
|
|
ScriptPropertyData newScriptProperty = new ScriptPropertyData(this.Name, this.GetScriptBlock, this.SetScriptBlock)
|
|
{
|
|
IsHidden = this.IsHidden
|
|
};
|
|
return newScriptProperty;
|
|
}
|
|
|
|
internal override void Process(ConcurrentBag<string> errors, string typeName, PSMemberInfoInternalCollection<PSMemberInfo> membersCollection, bool isOverride)
|
|
{
|
|
TypeTable.ProcessScriptPropertyData(errors, typeName, this, membersCollection, isOverride);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// CodePropertyData represents a CodeProperty definition.
|
|
/// </summary>
|
|
public sealed class CodePropertyData : TypeMemberData
|
|
{
|
|
/// <summary>
|
|
/// Initialize the CodePropertyData as a read only property.
|
|
/// </summary>
|
|
/// <param name="name"></param>
|
|
/// <param name="getMethod"></param>
|
|
public CodePropertyData(string name, MethodInfo getMethod)
|
|
: base(name)
|
|
{
|
|
GetCodeReference = getMethod;
|
|
}
|
|
|
|
/// <summary>
|
|
/// CodePropertyData constructor.
|
|
/// </summary>
|
|
/// <param name="name"></param>
|
|
/// <param name="getMethod"></param>
|
|
/// <param name="setMethod"></param>
|
|
public CodePropertyData(string name, MethodInfo getMethod, MethodInfo setMethod)
|
|
: base(name)
|
|
{
|
|
GetCodeReference = getMethod;
|
|
SetCodeReference = setMethod;
|
|
}
|
|
|
|
/// <summary>
|
|
/// The getter code reference.
|
|
/// </summary>
|
|
public MethodInfo GetCodeReference { get; set; }
|
|
|
|
/// <summary>
|
|
/// The setter code reference.
|
|
/// </summary>
|
|
public MethodInfo SetCodeReference { get; set; }
|
|
|
|
/// <summary>
|
|
/// Set true if the member is supposed to be hidden.
|
|
/// </summary>
|
|
public bool IsHidden { get; set; }
|
|
|
|
/// <summary>
|
|
/// Return a CodePropertyData that is a copy of this one.
|
|
/// </summary>
|
|
/// <returns></returns>
|
|
internal override TypeMemberData Copy()
|
|
{
|
|
CodePropertyData newCodeProperty = new CodePropertyData(this.Name, this.GetCodeReference, this.SetCodeReference)
|
|
{
|
|
IsHidden = this.IsHidden
|
|
};
|
|
return newCodeProperty;
|
|
}
|
|
|
|
internal override void Process(ConcurrentBag<string> errors, string typeName, PSMemberInfoInternalCollection<PSMemberInfo> membersCollection, bool isOverride)
|
|
{
|
|
TypeTable.ProcessCodePropertyData(errors, typeName, this, membersCollection, isOverride);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// ScriptMethodData represents a ScriptMethod definition.
|
|
/// </summary>
|
|
[DebuggerDisplay(@"ScriptMethod: {Name,nq}")]
|
|
public sealed class ScriptMethodData : TypeMemberData
|
|
{
|
|
/// <summary>
|
|
/// ScriptMethodData constructor.
|
|
/// </summary>
|
|
/// <param name="name"></param>
|
|
/// <param name="scriptToInvoke"></param>
|
|
public ScriptMethodData(string name, ScriptBlock scriptToInvoke)
|
|
: base(name)
|
|
{
|
|
Script = scriptToInvoke;
|
|
}
|
|
|
|
/// <summary>
|
|
/// The script method.
|
|
/// </summary>
|
|
public ScriptBlock Script { get; set; }
|
|
|
|
/// <summary>
|
|
/// Return a ScriptMethodData that is a copy of this one.
|
|
/// </summary>
|
|
/// <returns></returns>
|
|
internal override TypeMemberData Copy()
|
|
{
|
|
ScriptMethodData newScriptMethod = new ScriptMethodData(this.Name, this.Script);
|
|
return newScriptMethod;
|
|
}
|
|
|
|
internal override void Process(ConcurrentBag<string> errors, string typeName, PSMemberInfoInternalCollection<PSMemberInfo> membersCollection, bool isOverride)
|
|
{
|
|
TypeTable.ProcessScriptMethodData(errors, typeName, this, membersCollection, isOverride);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// CodeMethodData represents a CodeMethodData definition.
|
|
/// </summary>
|
|
[DebuggerDisplay("CodeMethod: {Name,nq}")]
|
|
public sealed class CodeMethodData : TypeMemberData
|
|
{
|
|
/// <summary>
|
|
/// CodeMethodData constructor.
|
|
/// </summary>
|
|
/// <param name="name"></param>
|
|
/// <param name="methodToCall"></param>
|
|
public CodeMethodData(string name, MethodInfo methodToCall)
|
|
: base(name)
|
|
{
|
|
CodeReference = methodToCall;
|
|
}
|
|
|
|
/// <summary>
|
|
/// The code reference.
|
|
/// </summary>
|
|
public MethodInfo CodeReference { get; set; }
|
|
|
|
/// <summary>
|
|
/// Return a CodeMethodData that is a copy of this one.
|
|
/// </summary>
|
|
/// <returns></returns>
|
|
internal override TypeMemberData Copy()
|
|
{
|
|
CodeMethodData newCodeMethod = new CodeMethodData(this.Name, this.CodeReference);
|
|
return newCodeMethod;
|
|
}
|
|
|
|
internal override void Process(ConcurrentBag<string> errors, string typeName, PSMemberInfoInternalCollection<PSMemberInfo> membersCollection, bool isOverride)
|
|
{
|
|
TypeTable.ProcessCodeMethodData(errors, typeName, this, membersCollection, isOverride);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// PropertySetData represent a PropertySet definition.
|
|
/// </summary>
|
|
[DebuggerDisplay("PropertySet: {Name,nq}")]
|
|
public sealed class PropertySetData : TypeMemberData
|
|
{
|
|
/// <summary>
|
|
/// PropertySetData constructor.
|
|
/// </summary>
|
|
/// <param name="referencedProperties"></param>
|
|
public PropertySetData(IEnumerable<string> referencedProperties)
|
|
{
|
|
if (referencedProperties == null)
|
|
{
|
|
throw PSTraceSource.NewArgumentNullException(nameof(referencedProperties));
|
|
}
|
|
|
|
ReferencedProperties = new Collection<string>(new List<string>(referencedProperties));
|
|
}
|
|
|
|
/// <summary>
|
|
/// The referenced properties.
|
|
/// </summary>
|
|
public Collection<string> ReferencedProperties { get; }
|
|
|
|
/// <summary>
|
|
/// The PropertySet name.
|
|
/// </summary>
|
|
internal new string Name { get { return base.Name; } set { base.Name = value; } }
|
|
|
|
/// <summary>
|
|
/// Set true if the member is supposed to be hidden.
|
|
/// </summary>
|
|
public bool IsHidden { get; set; }
|
|
|
|
/// <summary>
|
|
/// Return a new PropertySetData that is a copy of this one.
|
|
/// </summary>
|
|
/// <returns></returns>
|
|
internal override TypeMemberData Copy()
|
|
{
|
|
var newPropertySet = new PropertySetData(this.ReferencedProperties)
|
|
{
|
|
Name = this.Name,
|
|
IsHidden = this.IsHidden
|
|
};
|
|
return newPropertySet;
|
|
}
|
|
|
|
internal override void Process(ConcurrentBag<string> errors, string typeName, PSMemberInfoInternalCollection<PSMemberInfo> membersCollection, bool isOverride)
|
|
{
|
|
TypeTable.ProcessPropertySetData(errors, typeName, this, membersCollection, isOverride);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// MemberSetData represents a MemberSet definition.
|
|
/// </summary>
|
|
public class MemberSetData : TypeMemberData
|
|
{
|
|
/// <summary>
|
|
/// MemberSetData constructor.
|
|
/// </summary>
|
|
/// <param name="name">The name of the MemberSet.</param>
|
|
/// <param name="members">The members of the MemberSet.</param>
|
|
public MemberSetData(string name, Collection<TypeMemberData> members)
|
|
: base(name)
|
|
{
|
|
Members = members;
|
|
InheritMembers = true;
|
|
}
|
|
|
|
/// <summary>
|
|
/// The members of the MemberSet.
|
|
/// </summary>
|
|
public Collection<TypeMemberData> Members { get; }
|
|
|
|
/// <summary>
|
|
/// Set true if the member is supposed to be hidden.
|
|
/// </summary>
|
|
public bool IsHidden { get; set; }
|
|
|
|
/// <summary>
|
|
/// Indicating if the MemberSet will inherit members of the MemberSet
|
|
/// of the same name in the "parent" class.
|
|
/// </summary>
|
|
public bool InheritMembers { get; set; }
|
|
|
|
internal override TypeMemberData Copy()
|
|
{
|
|
MemberSetData newMemberSetData = new MemberSetData(Name, Members)
|
|
{
|
|
IsHidden = this.IsHidden,
|
|
InheritMembers = this.InheritMembers
|
|
};
|
|
return newMemberSetData;
|
|
}
|
|
|
|
internal override void Process(ConcurrentBag<string> errors, string typeName, PSMemberInfoInternalCollection<PSMemberInfo> membersCollection, bool isOverride)
|
|
{
|
|
TypeTable.ProcessMemberSetData(errors, typeName, this, membersCollection, isOverride);
|
|
}
|
|
}
|
|
|
|
#endregion TypeData
|
|
|
|
/// <summary>
|
|
/// A class that keeps the information from types.ps1xml files in a cache table.
|
|
/// </summary>
|
|
public sealed partial class TypeTable
|
|
{
|
|
#region private
|
|
|
|
#region strings
|
|
|
|
internal const string PSStandardMembers = "PSStandardMembers";
|
|
internal const string SerializationDepth = "SerializationDepth";
|
|
internal const string StringSerializationSource = "StringSerializationSource";
|
|
internal const string SerializationMethodNode = "SerializationMethod";
|
|
internal const string TargetTypeForDeserialization = "TargetTypeForDeserialization";
|
|
internal const string PropertySerializationSet = "PropertySerializationSet";
|
|
internal const string InheritPropertySerializationSet = "InheritPropertySerializationSet";
|
|
internal const string Types = "Types";
|
|
internal const string Type = "Type";
|
|
internal const string DefaultDisplayPropertySet = "DefaultDisplayPropertySet";
|
|
internal const string DefaultKeyPropertySet = "DefaultKeyPropertySet";
|
|
internal const string DefaultDisplayProperty = "DefaultDisplayProperty";
|
|
|
|
// this is used for extended properties like Note,Alias,Script,Code
|
|
internal const string IsHiddenAttribute = "IsHidden";
|
|
|
|
#endregion strings
|
|
|
|
#region fields
|
|
|
|
/// <summary>
|
|
/// Table from type name list into PSMemberInfoInternalCollection.
|
|
/// </summary>
|
|
private readonly ConcurrentDictionary<string, PSMemberInfoInternalCollection<PSMemberInfo>> _consolidatedMembers =
|
|
new ConcurrentDictionary<string, PSMemberInfoInternalCollection<PSMemberInfo>>(
|
|
concurrencyLevel: 1, capacity: 256, StringComparer.OrdinalIgnoreCase);
|
|
|
|
/// <summary>
|
|
/// Table from type name list into Collection of strings.
|
|
/// </summary>
|
|
private readonly ConcurrentDictionary<string, Collection<string>> _consolidatedSpecificProperties =
|
|
new ConcurrentDictionary<string, Collection<string>>(
|
|
concurrencyLevel: 1, capacity: 10, StringComparer.OrdinalIgnoreCase);
|
|
|
|
/// <summary>
|
|
/// Table from type name into PSMemberInfoInternalCollection.
|
|
/// </summary>
|
|
private readonly ConcurrentDictionary<string, PSMemberInfoInternalCollection<PSMemberInfo>> _extendedMembers =
|
|
new ConcurrentDictionary<string, PSMemberInfoInternalCollection<PSMemberInfo>>(
|
|
concurrencyLevel: 3, capacity: 300, StringComparer.OrdinalIgnoreCase);
|
|
|
|
/// <summary>
|
|
/// Points to a Hashtable from type name to type converter.
|
|
/// </summary>
|
|
private readonly ConcurrentDictionary<string, object> _typeConverters
|
|
= new ConcurrentDictionary<string, object>(
|
|
concurrencyLevel: 1, capacity: 5, StringComparer.OrdinalIgnoreCase);
|
|
|
|
/// <summary>
|
|
/// Points to a Hashtable from type name to type adapter.
|
|
/// </summary>
|
|
private readonly ConcurrentDictionary<string, PSObject.AdapterSet> _typeAdapters =
|
|
new ConcurrentDictionary<string, PSObject.AdapterSet>(
|
|
concurrencyLevel: 1, capacity: 5, StringComparer.OrdinalIgnoreCase);
|
|
|
|
// this is used to throw errors when updating a shared TypeTable.
|
|
internal readonly bool isShared;
|
|
private readonly List<string> _typeFileList;
|
|
|
|
// The member factory is cached to avoid allocating Func<> delegates on each call
|
|
private readonly Func<string, ConsolidatedString, PSMemberInfoInternalCollection<PSMemberInfo>> _memberFactoryFunc;
|
|
|
|
// This holds all the type information that is in the typetable
|
|
// Holds file name if types file was used to update the types
|
|
// Holds typename/typedata instance if typename/typedata was used to update the types
|
|
// InitialSessionStateEntryCollection is thread safe, so no locks are needed during updates here.
|
|
internal InitialSessionStateEntryCollection<SessionStateTypeEntry> typesInfo =
|
|
new InitialSessionStateEntryCollection<SessionStateTypeEntry>();
|
|
|
|
internal const SerializationMethod DefaultSerializationMethod = SerializationMethod.AllPublicProperties;
|
|
|
|
internal const bool DefaultInheritPropertySerializationSet = true;
|
|
|
|
private static readonly string[] s_standardMembers = new string[]
|
|
{
|
|
DefaultDisplayProperty,
|
|
DefaultDisplayPropertySet,
|
|
DefaultKeyPropertySet,
|
|
SerializationMethodNode,
|
|
SerializationDepth,
|
|
StringSerializationSource,
|
|
PropertySerializationSet,
|
|
InheritPropertySerializationSet,
|
|
TargetTypeForDeserialization
|
|
};
|
|
|
|
// Built-in type file paths.
|
|
internal static readonly string TypesFilePath;
|
|
internal static readonly string TypesV3FilePath;
|
|
internal static readonly string GetEventTypesFilePath;
|
|
|
|
#endregion
|
|
|
|
#region Update
|
|
|
|
#region process nodes
|
|
|
|
private static void AddMember(ConcurrentBag<string> errors, string typeName, PSMemberInfo member, PSMemberInfoInternalCollection<PSMemberInfo> membersCollection, bool isOverride)
|
|
{
|
|
if (PSMemberInfoCollection<PSMemberInfo>.IsReservedName(member.name))
|
|
{
|
|
AddError(errors, typeName, TypesXmlStrings.ReservedNameMember, member.name);
|
|
return;
|
|
}
|
|
|
|
if (membersCollection[member.name] != null && !isOverride)
|
|
{
|
|
AddError(errors, typeName, TypesXmlStrings.DuplicateMember, member.name);
|
|
return;
|
|
}
|
|
|
|
member.IsInstance = false;
|
|
|
|
if (membersCollection[member.name] == null)
|
|
{
|
|
membersCollection.Add(member);
|
|
}
|
|
else
|
|
{
|
|
membersCollection.Replace(member);
|
|
}
|
|
}
|
|
|
|
#region CheckStandardMembers
|
|
private static bool GetCheckNote(ConcurrentBag<string> errors, string typeName, PSMemberInfoInternalCollection<PSMemberInfo> members, string noteName, Type noteType, out PSNoteProperty note)
|
|
{
|
|
note = null;
|
|
PSMemberInfo noteAsMemberInfo = null;
|
|
for (int i = 0; i < members.Count; i++)
|
|
{
|
|
PSMemberInfo member = members[i];
|
|
if (string.Equals(member.Name, noteName, StringComparison.OrdinalIgnoreCase))
|
|
{
|
|
noteAsMemberInfo = member;
|
|
}
|
|
}
|
|
|
|
if (noteAsMemberInfo == null)
|
|
{
|
|
return true;
|
|
}
|
|
|
|
note = noteAsMemberInfo as PSNoteProperty;
|
|
if (note == null)
|
|
{
|
|
AddError(errors, typeName, TypesXmlStrings.MemberShouldBeNote, noteAsMemberInfo.Name);
|
|
return false;
|
|
}
|
|
|
|
object sourceValue = note.Value;
|
|
if (noteType.GetTypeCode().Equals(TypeCode.Boolean))
|
|
{
|
|
string sourceValueAsString = sourceValue as string;
|
|
if (sourceValueAsString != null)
|
|
{
|
|
if (sourceValueAsString.Length == 0)
|
|
{
|
|
note.noteValue = true;
|
|
}
|
|
else
|
|
{
|
|
note.noteValue = !string.Equals(sourceValueAsString, "false", StringComparison.OrdinalIgnoreCase);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
}
|
|
|
|
try
|
|
{
|
|
note.noteValue = LanguagePrimitives.ConvertTo(sourceValue, noteType, CultureInfo.InvariantCulture);
|
|
}
|
|
catch (PSInvalidCastException e)
|
|
{
|
|
AddError(errors, typeName, TypesXmlStrings.ErrorConvertingNote, note.Name, e.Message);
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
private static bool EnsureNotPresent(ConcurrentBag<string> errors, string typeName, PSMemberInfoInternalCollection<PSMemberInfo> members, string memberName)
|
|
{
|
|
for (int i = 0; i < members.Count; i++)
|
|
{
|
|
PSMemberInfo member = members[i];
|
|
if (string.Equals(member.Name, memberName, StringComparison.OrdinalIgnoreCase))
|
|
{
|
|
AddError(errors, typeName, TypesXmlStrings.MemberShouldNotBePresent, member.Name);
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
private static bool GetCheckMemberType(ConcurrentBag<string> errors, string typeName, PSMemberInfoInternalCollection<PSMemberInfo> members, string noteName, Type memberType, out PSMemberInfo member)
|
|
{
|
|
member = null;
|
|
for (int i = 0; i < members.Count; i++)
|
|
{
|
|
PSMemberInfo m = members[i];
|
|
if (string.Equals(m.Name, noteName, StringComparison.OrdinalIgnoreCase))
|
|
{
|
|
member = m;
|
|
}
|
|
}
|
|
|
|
if (member == null)
|
|
{
|
|
return true;
|
|
}
|
|
|
|
if (memberType.IsInstanceOfType(member))
|
|
{
|
|
return true;
|
|
}
|
|
|
|
AddError(errors, typeName, TypesXmlStrings.MemberShouldHaveType, member.Name, memberType.Name);
|
|
member = null;
|
|
return false;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Issue appropriate errors and remove members as necessary if:
|
|
/// - The serialization settings do not fall into one of the combinations of the table below
|
|
/// - If the serialization settings notes' values cannot be converted to the propper type
|
|
/// - If serialization settings members are of the wrong member type
|
|
/// - DefaultDisplayPropertySet is not an PSPropertySet
|
|
/// - DefaultDisplayProperty is not an PSPropertyInfo
|
|
/// - DefaultKeyPropertySet is not an PSPropertySet
|
|
///
|
|
/// SerializationMethod InheritPropertySerializationSet PropertySerializationSet SerializationDepth StringSerializationSource
|
|
/// --------------------- ------------------------------- ------------------------ ------------------- ---------------------------
|
|
/// String must NOT be present must NOT be present must NOT be present optional
|
|
/// SpecificProperties optional must be present optional optional
|
|
/// AllPublicProperties must NOT be present must NOT be present optional optional.
|
|
/// </summary>
|
|
private static bool CheckStandardMembers(ConcurrentBag<string> errors, string typeName, PSMemberInfoInternalCollection<PSMemberInfo> members)
|
|
{
|
|
#region Remove all non standard members
|
|
List<string> membersToBeIgnored = new List<string>();
|
|
for (int i = 0; i < members.Count; i++)
|
|
{
|
|
bool found = false;
|
|
string memberName = members[i].Name;
|
|
for (int j = 0; j < s_standardMembers.Length; j++)
|
|
{
|
|
if (string.Equals(memberName, s_standardMembers[j], StringComparison.OrdinalIgnoreCase))
|
|
{
|
|
found = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!found)
|
|
{
|
|
membersToBeIgnored.Add(memberName);
|
|
AddError(errors, typeName, TypesXmlStrings.NotAStandardMember, memberName);
|
|
}
|
|
}
|
|
|
|
foreach (string memberToBeIgnored in membersToBeIgnored)
|
|
{
|
|
members.Remove(memberToBeIgnored);
|
|
}
|
|
#endregion Remove all non standard members
|
|
|
|
bool serializationSettingsOk;
|
|
do // false loop
|
|
{
|
|
PSNoteProperty serializationMethodNote;
|
|
serializationSettingsOk = GetCheckNote(errors, typeName, members, SerializationMethodNode, typeof(SerializationMethod), out serializationMethodNote);
|
|
if (!serializationSettingsOk)
|
|
{
|
|
break;
|
|
}
|
|
|
|
SerializationMethod serializationMethod = SerializationMethod.AllPublicProperties;
|
|
if (serializationMethodNote != null)
|
|
{
|
|
serializationMethod = (SerializationMethod)serializationMethodNote.Value;
|
|
}
|
|
|
|
if (serializationMethod == SerializationMethod.String)
|
|
{
|
|
serializationSettingsOk = EnsureNotPresent(errors, typeName, members, InheritPropertySerializationSet);
|
|
if (!serializationSettingsOk)
|
|
{
|
|
break;
|
|
}
|
|
|
|
serializationSettingsOk = EnsureNotPresent(errors, typeName, members, PropertySerializationSet);
|
|
if (!serializationSettingsOk)
|
|
{
|
|
break;
|
|
}
|
|
|
|
serializationSettingsOk = EnsureNotPresent(errors, typeName, members, SerializationDepth);
|
|
if (!serializationSettingsOk)
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
else if (serializationMethod == SerializationMethod.SpecificProperties)
|
|
{
|
|
PSNoteProperty inheritPropertiesNote;
|
|
serializationSettingsOk = GetCheckNote(errors, typeName, members, InheritPropertySerializationSet, typeof(bool), out inheritPropertiesNote);
|
|
if (!serializationSettingsOk)
|
|
{
|
|
break;
|
|
}
|
|
|
|
PSMemberInfo propertySerializationSet;
|
|
serializationSettingsOk = GetCheckMemberType(errors, typeName, members,
|
|
PropertySerializationSet, typeof(PSPropertySet),
|
|
out propertySerializationSet);
|
|
if (!serializationSettingsOk)
|
|
{
|
|
break;
|
|
}
|
|
|
|
if (inheritPropertiesNote != null && inheritPropertiesNote.Value.Equals(false) && propertySerializationSet == null)
|
|
{
|
|
AddError(
|
|
errors,
|
|
typeName,
|
|
TypesXmlStrings.MemberMustBePresent,
|
|
PropertySerializationSet,
|
|
SerializationMethodNode,
|
|
nameof(SerializationMethod.SpecificProperties),
|
|
InheritPropertySerializationSet,
|
|
"false");
|
|
serializationSettingsOk = false;
|
|
break;
|
|
}
|
|
|
|
PSNoteProperty noteProperty;
|
|
serializationSettingsOk = GetCheckNote(errors, typeName, members, SerializationDepth, typeof(int), out noteProperty);
|
|
if (!serializationSettingsOk)
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
else if (serializationMethod == SerializationMethod.AllPublicProperties)
|
|
{
|
|
serializationSettingsOk = EnsureNotPresent(errors, typeName, members, InheritPropertySerializationSet);
|
|
if (!serializationSettingsOk)
|
|
{
|
|
break;
|
|
}
|
|
|
|
serializationSettingsOk = EnsureNotPresent(errors, typeName, members, PropertySerializationSet);
|
|
if (!serializationSettingsOk)
|
|
{
|
|
break;
|
|
}
|
|
|
|
PSNoteProperty noteProperty;
|
|
serializationSettingsOk = GetCheckNote(errors, typeName, members, SerializationDepth, typeof(int), out noteProperty);
|
|
if (!serializationSettingsOk)
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
PSMemberInfo serializationSource;
|
|
serializationSettingsOk = GetCheckMemberType(errors, typeName, members, StringSerializationSource, typeof(PSPropertyInfo), out serializationSource);
|
|
if (!serializationSettingsOk)
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
while (false);
|
|
|
|
if (!serializationSettingsOk)
|
|
{
|
|
AddError(errors, typeName, TypesXmlStrings.SerializationSettingsIgnored);
|
|
members.Remove(InheritPropertySerializationSet);
|
|
members.Remove(SerializationMethodNode);
|
|
members.Remove(StringSerializationSource);
|
|
members.Remove(PropertySerializationSet);
|
|
members.Remove(SerializationDepth);
|
|
}
|
|
|
|
PSMemberInfo otherMember;
|
|
if (!GetCheckMemberType(errors, typeName, members, DefaultDisplayPropertySet, typeof(PSPropertySet), out otherMember))
|
|
{
|
|
members.Remove(DefaultDisplayPropertySet);
|
|
}
|
|
|
|
if (!GetCheckMemberType(errors, typeName, members, DefaultKeyPropertySet, typeof(PSPropertySet), out otherMember))
|
|
{
|
|
members.Remove(DefaultKeyPropertySet);
|
|
}
|
|
|
|
PSNoteProperty defaultDisplayProperty;
|
|
if (!GetCheckNote(errors, typeName, members, DefaultDisplayProperty, typeof(string), out defaultDisplayProperty))
|
|
{
|
|
members.Remove(DefaultDisplayProperty);
|
|
}
|
|
|
|
PSNoteProperty targetTypeForDeserialization;
|
|
if (!GetCheckNote(errors, typeName, members, TargetTypeForDeserialization, typeof(Type), out targetTypeForDeserialization))
|
|
{
|
|
members.Remove(TargetTypeForDeserialization);
|
|
}
|
|
else
|
|
{
|
|
if (targetTypeForDeserialization != null)
|
|
{
|
|
// GetCheckNote converts the value from string to System.Type.. We should store value as Type
|
|
// as this will save time spent converting string to Type.
|
|
members.Remove(TargetTypeForDeserialization);
|
|
members.Add(targetTypeForDeserialization, true);
|
|
}
|
|
}
|
|
|
|
return serializationSettingsOk;
|
|
}
|
|
#endregion CheckStandardMembers
|
|
|
|
/// <summary>
|
|
/// Helper for ProcessTypeConverter/ProcessTypeAdapter from TypeData.
|
|
/// </summary>
|
|
/// <param name="errors"></param>
|
|
/// <param name="typeName"></param>
|
|
/// <param name="type"></param>
|
|
/// <param name="errorFormatString"></param>
|
|
/// <param name="instance"></param>
|
|
/// <returns></returns>
|
|
private static bool CreateInstance(ConcurrentBag<string> errors, string typeName, Type type, string errorFormatString, out object instance)
|
|
{
|
|
instance = null;
|
|
|
|
Exception instanceException = null;
|
|
|
|
#pragma warning disable 56500
|
|
try
|
|
{
|
|
instance = Activator.CreateInstance(type);
|
|
}
|
|
catch (TargetInvocationException e)
|
|
{
|
|
instanceException = e.InnerException ?? e;
|
|
}
|
|
catch (Exception e)
|
|
{
|
|
instanceException = e;
|
|
}
|
|
#pragma warning restore 56500
|
|
|
|
if (instanceException != null)
|
|
{
|
|
AddError(errors, typeName, errorFormatString, type.FullName, instanceException.Message);
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
#endregion process nodes
|
|
|
|
#region process type data
|
|
|
|
private static void AddError(ConcurrentBag<string> errors, string typeName, string resourceString, params object[] formatArguments)
|
|
{
|
|
string errorMsg = StringUtil.Format(resourceString, formatArguments);
|
|
string errorLine = StringUtil.Format(TypesXmlStrings.TypeDataTypeError, typeName, errorMsg);
|
|
errors.Add(errorLine);
|
|
}
|
|
|
|
#region add members from TypeData
|
|
|
|
private static void ProcessMembersData(
|
|
ConcurrentBag<string> errors,
|
|
string typeName,
|
|
Dictionary<string, TypeMemberData> membersData,
|
|
PSMemberInfoInternalCollection<PSMemberInfo> membersCollection,
|
|
bool isOverride)
|
|
{
|
|
foreach (TypeMemberData typeMember in membersData.Values)
|
|
{
|
|
typeMember.Process(errors, typeName, membersCollection, isOverride);
|
|
}
|
|
}
|
|
|
|
internal static void ProcessNoteData(ConcurrentBag<string> errors, string typeName, NotePropertyData nodeData, PSMemberInfoInternalCollection<PSMemberInfo> membersCollection, bool isOverride)
|
|
{
|
|
// value could be null
|
|
PSNoteProperty note = new PSNoteProperty(nodeData.Name, nodeData.Value) { IsHidden = nodeData.IsHidden };
|
|
AddMember(errors, typeName, note, membersCollection, isOverride);
|
|
}
|
|
|
|
internal static void ProcessAliasData(ConcurrentBag<string> errors, string typeName, AliasPropertyData aliasData, PSMemberInfoInternalCollection<PSMemberInfo> membersCollection, bool isOverride)
|
|
{
|
|
// ReferencedMemberName should not be an empty string
|
|
if (string.IsNullOrEmpty(aliasData.ReferencedMemberName))
|
|
{
|
|
AddError(errors, typeName, TypesXmlStrings.TypeDataShouldHaveValue, "AliasPropertyData", "ReferencedMemberName");
|
|
return;
|
|
}
|
|
|
|
PSAliasProperty alias = new PSAliasProperty(aliasData.Name, aliasData.ReferencedMemberName, aliasData.MemberType)
|
|
{
|
|
IsHidden = aliasData.IsHidden
|
|
};
|
|
AddMember(errors, typeName, alias, membersCollection, isOverride);
|
|
}
|
|
|
|
internal static void ProcessScriptPropertyData(ConcurrentBag<string> errors, string typeName, ScriptPropertyData scriptPropertyData, PSMemberInfoInternalCollection<PSMemberInfo> membersCollection, bool isOverride)
|
|
{
|
|
ScriptBlock getter = scriptPropertyData.GetScriptBlock;
|
|
ScriptBlock setter = scriptPropertyData.SetScriptBlock;
|
|
|
|
if (setter == null && getter == null)
|
|
{
|
|
AddError(errors, typeName, TypesXmlStrings.ScriptPropertyShouldHaveGetterOrSetter);
|
|
return;
|
|
}
|
|
|
|
PSScriptProperty scriptProperty = new PSScriptProperty(scriptPropertyData.Name, getter, setter, true)
|
|
{
|
|
IsHidden = scriptPropertyData.IsHidden
|
|
};
|
|
AddMember(errors, typeName, scriptProperty, membersCollection, isOverride);
|
|
}
|
|
|
|
internal static void ProcessCodePropertyData(ConcurrentBag<string> errors, string typeName, CodePropertyData codePropertyData, PSMemberInfoInternalCollection<PSMemberInfo> membersCollection, bool isOverride)
|
|
{
|
|
if ((codePropertyData.GetCodeReference == null) && (codePropertyData.SetCodeReference == null))
|
|
{
|
|
AddError(errors, typeName, TypesXmlStrings.CodePropertyShouldHaveGetterOrSetter);
|
|
return;
|
|
}
|
|
|
|
PSCodeProperty codeProperty;
|
|
try
|
|
{
|
|
codeProperty = new PSCodeProperty(codePropertyData.Name, codePropertyData.GetCodeReference, codePropertyData.SetCodeReference);
|
|
}
|
|
catch (ExtendedTypeSystemException exception)
|
|
{
|
|
AddError(errors, typeName, TypesXmlStrings.Exception, exception.Message);
|
|
return;
|
|
}
|
|
|
|
codeProperty.IsHidden = codePropertyData.IsHidden;
|
|
AddMember(errors, typeName, codeProperty, membersCollection, isOverride);
|
|
}
|
|
|
|
internal static void ProcessScriptMethodData(ConcurrentBag<string> errors, string typeName, ScriptMethodData scriptMethodData, PSMemberInfoInternalCollection<PSMemberInfo> membersCollection, bool isOverride)
|
|
{
|
|
if (scriptMethodData.Script == null)
|
|
{
|
|
AddError(errors, typeName, TypesXmlStrings.TypeDataShouldHaveValue, "ScriptMethodData", "Script");
|
|
return;
|
|
}
|
|
|
|
PSScriptMethod scriptMethod = new PSScriptMethod(scriptMethodData.Name, scriptMethodData.Script, true);
|
|
AddMember(errors, typeName, scriptMethod, membersCollection, isOverride);
|
|
}
|
|
|
|
internal static void ProcessCodeMethodData(ConcurrentBag<string> errors, string typeName, CodeMethodData codeMethodData, PSMemberInfoInternalCollection<PSMemberInfo> membersCollection, bool isOverride)
|
|
{
|
|
if (codeMethodData.CodeReference == null)
|
|
{
|
|
AddError(errors, typeName, TypesXmlStrings.TypeDataShouldHaveValue, "CodeMethodData", "CodeReference");
|
|
return;
|
|
}
|
|
|
|
PSCodeMethod codeMethod;
|
|
try
|
|
{
|
|
codeMethod = new PSCodeMethod(codeMethodData.Name, codeMethodData.CodeReference);
|
|
}
|
|
catch (ExtendedTypeSystemException exception)
|
|
{
|
|
AddError(errors, typeName, TypesXmlStrings.Exception, exception.Message);
|
|
return;
|
|
}
|
|
|
|
AddMember(errors, typeName, codeMethod, membersCollection, isOverride);
|
|
}
|
|
|
|
internal static void ProcessPropertySetData(ConcurrentBag<string> errors, string typeName, PropertySetData propertySetData, PSMemberInfoInternalCollection<PSMemberInfo> membersCollection, bool isOverride)
|
|
{
|
|
if (propertySetData.ReferencedProperties == null || propertySetData.ReferencedProperties.Count == 0)
|
|
{
|
|
AddError(errors, typeName, TypesXmlStrings.TypeDataShouldHaveValue, "PropertySetData", "ReferencedProperties");
|
|
return;
|
|
}
|
|
|
|
// the node cardinality is OneToMany
|
|
var referencedProperties = new List<string>(propertySetData.ReferencedProperties.Count);
|
|
foreach (string name in propertySetData.ReferencedProperties)
|
|
{
|
|
if (string.IsNullOrEmpty(name))
|
|
{
|
|
AddError(errors, typeName, TypesXmlStrings.TypeDataShouldNotBeNullOrEmpty, "PropertySetData", "ReferencedProperties");
|
|
continue;
|
|
}
|
|
|
|
referencedProperties.Add(name);
|
|
}
|
|
|
|
if (referencedProperties.Count == 0)
|
|
{
|
|
return;
|
|
}
|
|
|
|
var propertySet = new PSPropertySet(propertySetData.Name, referencedProperties)
|
|
{
|
|
IsHidden = propertySetData.IsHidden
|
|
};
|
|
AddMember(errors, typeName, propertySet, membersCollection, isOverride);
|
|
}
|
|
|
|
internal static void ProcessMemberSetData(ConcurrentBag<string> errors, string typeName, MemberSetData memberSetData, PSMemberInfoInternalCollection<PSMemberInfo> membersCollection, bool isOverride)
|
|
{
|
|
var memberSetMembers = new PSMemberInfoInternalCollection<PSMemberInfo>(memberSetData.Members.Count);
|
|
foreach (var m in memberSetData.Members)
|
|
{
|
|
m.Process(errors, typeName, memberSetMembers, isOverride);
|
|
}
|
|
|
|
var memberSet = new PSMemberSet(memberSetData.Name, memberSetMembers)
|
|
{
|
|
IsHidden = memberSetData.IsHidden,
|
|
inheritMembers = memberSetData.InheritMembers
|
|
};
|
|
AddMember(errors, typeName, memberSet, membersCollection, isOverride);
|
|
}
|
|
|
|
private static void ProcessStandardMembers(
|
|
ConcurrentBag<string> errors,
|
|
string typeName,
|
|
Dictionary<string, TypeMemberData> standardMembers,
|
|
List<PropertySetData> propertySets,
|
|
PSMemberInfoInternalCollection<PSMemberInfo> membersCollection,
|
|
bool isOverride)
|
|
{
|
|
int newMemberCount = standardMembers.Count + propertySets.Count;
|
|
|
|
// If StandardMembers do not exists, we follow the original logic to create the StandardMembers
|
|
if (membersCollection[PSStandardMembers] == null)
|
|
{
|
|
var memberSetMembers = new PSMemberInfoInternalCollection<PSMemberInfo>(newMemberCount);
|
|
|
|
ProcessMembersData(errors, typeName, standardMembers, memberSetMembers, false);
|
|
foreach (PropertySetData propertySet in propertySets)
|
|
{
|
|
ProcessPropertySetData(errors, typeName, propertySet, memberSetMembers, false);
|
|
}
|
|
|
|
CheckStandardMembers(errors, typeName, memberSetMembers);
|
|
PSMemberSet standardMemberSet = new PSMemberSet(PSStandardMembers, memberSetMembers)
|
|
{
|
|
inheritMembers = true,
|
|
IsHidden = true,
|
|
ShouldSerialize = false
|
|
};
|
|
AddMember(errors, typeName, standardMemberSet, membersCollection, false);
|
|
return;
|
|
}
|
|
|
|
// StandardMembers exist
|
|
var psStandardMemberSet = (PSMemberSet)membersCollection[PSStandardMembers];
|
|
|
|
// Copy existing internal PSStandard members
|
|
int totalMemberCount = psStandardMemberSet.InternalMembers.Count + newMemberCount;
|
|
var existingMembers = new PSMemberInfoInternalCollection<PSMemberInfo>(totalMemberCount);
|
|
var oldMembersCopy = new PSMemberInfoInternalCollection<PSMemberInfo>(totalMemberCount);
|
|
foreach (var existingMember in psStandardMemberSet.InternalMembers)
|
|
{
|
|
existingMembers.Add(existingMember.Copy());
|
|
oldMembersCopy.Add(existingMember.Copy());
|
|
}
|
|
|
|
// Process the Members directly into the 'existingMembers' collection
|
|
ProcessMembersData(errors, typeName, standardMembers, existingMembers, isOverride);
|
|
foreach (PropertySetData propertySet in propertySets)
|
|
{
|
|
ProcessPropertySetData(errors, typeName, propertySet, existingMembers, isOverride);
|
|
}
|
|
|
|
if (CheckStandardMembers(errors, typeName, existingMembers))
|
|
{
|
|
// No conflict in serialization settings, replace the old StandardMembers with the new one
|
|
PSMemberSet standardMemberSet = new PSMemberSet(PSStandardMembers, existingMembers)
|
|
{
|
|
inheritMembers = true,
|
|
IsHidden = true,
|
|
ShouldSerialize = false
|
|
};
|
|
AddMember(errors, typeName, standardMemberSet, membersCollection, true);
|
|
}
|
|
else
|
|
{
|
|
// There are conflicts in serialization settings, add non-serializationSetting configurations
|
|
// into the original member collection. Replace the old StandardMembers with the new one
|
|
foreach (PSMemberInfo member in existingMembers)
|
|
{
|
|
if (oldMembersCopy[member.name] == null)
|
|
{
|
|
oldMembersCopy.Add(member);
|
|
}
|
|
}
|
|
|
|
PSMemberSet standardMemberSet = new PSMemberSet(PSStandardMembers, oldMembersCopy)
|
|
{
|
|
inheritMembers = true,
|
|
IsHidden = true,
|
|
ShouldSerialize = false
|
|
};
|
|
AddMember(errors, typeName, standardMemberSet, membersCollection, true);
|
|
}
|
|
}
|
|
|
|
private static void ProcessStandardMembers(
|
|
ConcurrentBag<string> errors,
|
|
string typeName,
|
|
PSMemberInfoInternalCollection<PSMemberInfo> memberSetMembers,
|
|
PSMemberInfoInternalCollection<PSMemberInfo> typeMemberCollection,
|
|
bool isOverride)
|
|
{
|
|
// If StandardMembers do not exists, we follow the original logic to create the StandardMembers
|
|
if (typeMemberCollection[PSStandardMembers] == null)
|
|
{
|
|
CheckStandardMembers(errors, typeName, memberSetMembers);
|
|
PSMemberSet standardMemberSet = new PSMemberSet(PSStandardMembers, memberSetMembers)
|
|
{
|
|
inheritMembers = true,
|
|
IsHidden = true,
|
|
ShouldSerialize = false
|
|
};
|
|
AddMember(errors, typeName, standardMemberSet, typeMemberCollection, false);
|
|
return;
|
|
}
|
|
|
|
// StandardMembers exist
|
|
var psStandardMemberSet = (PSMemberSet)typeMemberCollection[PSStandardMembers];
|
|
|
|
// Copy existing internal PSStandard members
|
|
int totalMemberCount = psStandardMemberSet.InternalMembers.Count + memberSetMembers.Count;
|
|
var existingMembers = new PSMemberInfoInternalCollection<PSMemberInfo>(totalMemberCount);
|
|
var oldMembersCopy = new PSMemberInfoInternalCollection<PSMemberInfo>(totalMemberCount);
|
|
foreach (var existingMember in psStandardMemberSet.InternalMembers)
|
|
{
|
|
existingMembers.Add(existingMember.Copy());
|
|
oldMembersCopy.Add(existingMember.Copy());
|
|
}
|
|
|
|
// Process the Members directly into the 'existingMembers' collection
|
|
foreach (PSMemberInfo member in memberSetMembers)
|
|
{
|
|
AddMember(errors, typeName, member, existingMembers, isOverride);
|
|
}
|
|
|
|
if (CheckStandardMembers(errors, typeName, existingMembers))
|
|
{
|
|
// No conflict in serialization settings, replace the old StandardMembers with the new one
|
|
PSMemberSet standardMemberSet = new PSMemberSet(PSStandardMembers, existingMembers)
|
|
{
|
|
inheritMembers = true,
|
|
IsHidden = true,
|
|
ShouldSerialize = false
|
|
};
|
|
AddMember(errors, typeName, standardMemberSet, typeMemberCollection, isOverride: true);
|
|
}
|
|
else
|
|
{
|
|
// There are conflicts in serialization settings, add non-serializationSetting configurations
|
|
// into the original member collection. Replace the old StandardMembers with the new one
|
|
foreach (PSMemberInfo member in existingMembers)
|
|
{
|
|
if (oldMembersCopy[member.name] == null)
|
|
{
|
|
oldMembersCopy.Add(member);
|
|
}
|
|
}
|
|
|
|
PSMemberSet standardMemberSet = new PSMemberSet(PSStandardMembers, oldMembersCopy)
|
|
{
|
|
inheritMembers = true,
|
|
IsHidden = true,
|
|
ShouldSerialize = false
|
|
};
|
|
AddMember(errors, typeName, standardMemberSet, typeMemberCollection, isOverride: true);
|
|
}
|
|
}
|
|
|
|
private static void ProcessTypeConverter(
|
|
ConcurrentBag<string> errors,
|
|
string typeName,
|
|
Type converterType,
|
|
ConcurrentDictionary<string, object> typeConverters,
|
|
bool isOverride)
|
|
{
|
|
if (CreateInstance(errors, typeName, converterType, TypesXmlStrings.UnableToInstantiateTypeConverter, out object instance))
|
|
{
|
|
if ((instance is TypeConverter) || (instance is PSTypeConverter))
|
|
{
|
|
LanguagePrimitives.UpdateTypeConvertFromTypeTable(typeName);
|
|
}
|
|
else
|
|
{
|
|
AddError(errors, typeName, TypesXmlStrings.TypeIsNotTypeConverter, converterType.FullName);
|
|
}
|
|
}
|
|
|
|
if (instance != null && !typeConverters.TryAdd(typeName, instance))
|
|
{
|
|
if (!isOverride)
|
|
{
|
|
AddError(errors, typeName, TypesXmlStrings.TypeConverterAlreadyPresent);
|
|
}
|
|
|
|
// If IsOverride == true, eat the TypeConverterAlreadyPresent failure.
|
|
}
|
|
}
|
|
|
|
private static void ProcessTypeAdapter(
|
|
ConcurrentBag<string> errors,
|
|
string typeName,
|
|
Type adapterType,
|
|
ConcurrentDictionary<string, PSObject.AdapterSet> typeAdapters,
|
|
bool isOverride)
|
|
{
|
|
PSObject.AdapterSet adapterSet = null;
|
|
if (CreateInstance(errors, typeName, adapterType, TypesXmlStrings.UnableToInstantiateTypeAdapter, out object instance))
|
|
{
|
|
PSPropertyAdapter psPropertyAdapter = instance as PSPropertyAdapter;
|
|
|
|
if (psPropertyAdapter == null)
|
|
{
|
|
AddError(errors, typeName, TypesXmlStrings.TypeIsNotTypeAdapter, adapterType.FullName);
|
|
}
|
|
else
|
|
{
|
|
if (LanguagePrimitives.TryConvertTo(typeName, out Type adaptedType))
|
|
{
|
|
adapterSet = PSObject.CreateThirdPartyAdapterSet(adaptedType, psPropertyAdapter);
|
|
}
|
|
else
|
|
{
|
|
AddError(errors, typeName, TypesXmlStrings.InvalidAdaptedType, typeName);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (adapterSet != null && !typeAdapters.TryAdd(typeName, adapterSet))
|
|
{
|
|
if (!isOverride)
|
|
{
|
|
AddError(errors, typeName, TypesXmlStrings.TypeAdapterAlreadyPresent);
|
|
}
|
|
|
|
// If IsOverride == true, eat the TypeConverterAlreadyPresent failure.
|
|
}
|
|
}
|
|
|
|
private void ProcessTypeDataToAdd(ConcurrentBag<string> errors, TypeData typeData)
|
|
{
|
|
string typeName = typeData.TypeName;
|
|
Dbg.Assert(!string.IsNullOrEmpty(typeName), "TypeData class guarantees the typeName is not null and not empty");
|
|
|
|
var propertySets = new List<PropertySetData>();
|
|
if (typeData.DefaultDisplayPropertySet != null)
|
|
{
|
|
propertySets.Add(typeData.DefaultDisplayPropertySet);
|
|
}
|
|
|
|
if (typeData.DefaultKeyPropertySet != null)
|
|
{
|
|
propertySets.Add(typeData.DefaultKeyPropertySet);
|
|
}
|
|
|
|
if (typeData.PropertySerializationSet != null)
|
|
{
|
|
propertySets.Add(typeData.PropertySerializationSet);
|
|
}
|
|
|
|
if (typeData.Members.Count == 0 && typeData.StandardMembers.Count == 0
|
|
&& typeData.TypeConverter == null && typeData.TypeAdapter == null
|
|
&& propertySets.Count == 0 && !typeData.fromTypesXmlFile)
|
|
{
|
|
AddError(errors, typeName, TypesXmlStrings.TypeDataShouldNotBeEmpty);
|
|
return;
|
|
}
|
|
|
|
PSMemberInfoInternalCollection<PSMemberInfo> typeMembers = null;
|
|
bool hasStandardMembers = typeData.StandardMembers.Count > 0 || propertySets.Count > 0;
|
|
int collectionSize = typeData.Members.Count + (hasStandardMembers ? 1 : 0);
|
|
|
|
if (typeData.Members.Count > 0)
|
|
{
|
|
typeMembers = _extendedMembers.GetOrAdd(typeName, GetValueFactoryBasedOnInitCapacity(collectionSize));
|
|
ProcessMembersData(errors, typeName, typeData.Members, typeMembers, typeData.IsOverride);
|
|
|
|
foreach (var memberName in typeData.Members.Keys)
|
|
{
|
|
PSGetMemberBinder.TypeTableMemberAdded(memberName);
|
|
}
|
|
}
|
|
|
|
if (hasStandardMembers)
|
|
{
|
|
if (typeMembers == null)
|
|
{
|
|
typeMembers = _extendedMembers.GetOrAdd(typeName, GetValueFactoryBasedOnInitCapacity(capacity: 1));
|
|
}
|
|
|
|
ProcessStandardMembers(errors, typeName, typeData.StandardMembers, propertySets, typeMembers, typeData.IsOverride);
|
|
}
|
|
|
|
if (typeData.TypeConverter != null)
|
|
{
|
|
ProcessTypeConverter(errors, typeName, typeData.TypeConverter, _typeConverters, typeData.IsOverride);
|
|
}
|
|
|
|
if (typeData.TypeAdapter != null)
|
|
{
|
|
ProcessTypeAdapter(errors, typeName, typeData.TypeAdapter, _typeAdapters, typeData.IsOverride);
|
|
}
|
|
|
|
// Record the information that this typedata was removed from the typetable
|
|
// The next time the typetable is updated, we will need to exclude this typedata from the typetable
|
|
typesInfo.Add(new SessionStateTypeEntry(typeData, isRemove: false));
|
|
}
|
|
|
|
#endregion add members from TypeData
|
|
|
|
#region remove type data
|
|
|
|
private void ProcessTypeDataToRemove(ConcurrentBag<string> errors, TypeData typeData)
|
|
{
|
|
string typeName = typeData.TypeName;
|
|
Dbg.Assert(!string.IsNullOrEmpty(typeName), "TypeData class guarantees the typeName is not null and not empty");
|
|
|
|
// We always remove the whole type
|
|
bool typeExist = false;
|
|
PSMemberInfoInternalCollection<PSMemberInfo> memberCollection;
|
|
if (_extendedMembers.TryRemove(typeName, out memberCollection))
|
|
{
|
|
typeExist = true;
|
|
foreach (var m in memberCollection)
|
|
{
|
|
// Invalidate any cached rules with this name.
|
|
PSGetMemberBinder.TypeTableMemberPossiblyUpdated(m.Name);
|
|
}
|
|
}
|
|
|
|
if (_typeConverters.TryRemove(typeName, out _))
|
|
{
|
|
typeExist = true;
|
|
LanguagePrimitives.UpdateTypeConvertFromTypeTable(typeName);
|
|
}
|
|
|
|
if (_typeAdapters.TryRemove(typeName, out _))
|
|
{
|
|
typeExist = true;
|
|
}
|
|
|
|
if (!typeExist)
|
|
{
|
|
AddError(errors, typeName, TypesXmlStrings.TypeNotFound, typeName);
|
|
}
|
|
else
|
|
{
|
|
typesInfo.Add(new SessionStateTypeEntry(typeData, isRemove: true));
|
|
}
|
|
}
|
|
|
|
#endregion remove type data
|
|
|
|
#endregion process type data
|
|
|
|
private void Update(LoadContext context)
|
|
{
|
|
try
|
|
{
|
|
var xs = new TypesPs1xmlReader(context);
|
|
var types = xs.Read();
|
|
if (types != null)
|
|
{
|
|
foreach (var t in types)
|
|
{
|
|
ProcessTypeDataToAdd(context.errors, t);
|
|
}
|
|
}
|
|
}
|
|
catch (Exception e)
|
|
{
|
|
context.AddError(TypesXmlStrings.Exception, e.Message);
|
|
}
|
|
}
|
|
|
|
private void Update(ConcurrentBag<string> errors, TypeData typeData, bool isRemove)
|
|
{
|
|
if (!isRemove)
|
|
{
|
|
ProcessTypeDataToAdd(errors, typeData);
|
|
}
|
|
else
|
|
{
|
|
ProcessTypeDataToRemove(errors, typeData);
|
|
}
|
|
}
|
|
|
|
#endregion Update
|
|
|
|
#endregion private
|
|
|
|
#region Constructor
|
|
|
|
static TypeTable()
|
|
{
|
|
s_valueFactoryCache = new Func<string, PSMemberInfoInternalCollection<PSMemberInfo>>[ValueFactoryCacheCount];
|
|
|
|
// Rather than set these members every time we process the standard members, do it
|
|
// just once at startup.
|
|
foreach (var sm in s_standardMembers)
|
|
{
|
|
PSGetMemberBinder.TypeTableMemberAdded(sm);
|
|
}
|
|
|
|
PSGetMemberBinder.TypeTableMemberAdded(PSStandardMembers);
|
|
|
|
// Set the built-in type file paths.
|
|
var psHome = Utils.DefaultPowerShellAppBase;
|
|
TypesFilePath = Path.Combine(psHome, "types.ps1xml");
|
|
TypesV3FilePath = Path.Combine(psHome, "typesv3.ps1xml");
|
|
GetEventTypesFilePath = Path.Combine(psHome, "GetEvent.types.ps1xml");
|
|
}
|
|
|
|
/// <summary>
|
|
/// </summary>
|
|
internal TypeTable() : this(isShared: false)
|
|
{
|
|
}
|
|
|
|
internal TypeTable(bool isShared)
|
|
{
|
|
this.isShared = isShared;
|
|
_typeFileList = new List<string>();
|
|
_memberFactoryFunc = MemberFactory;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Constructor that creates a TypeTable from a set of type files.
|
|
/// </summary>
|
|
/// <param name="typeFiles">
|
|
/// Type files to load for type information.
|
|
/// </param>
|
|
/// <exception cref="ArgumentException">
|
|
/// 1. Path {0} is not fully qualified. Specify a fully qualified type file path.
|
|
/// </exception>
|
|
/// <exception cref="TypeTableLoadException">
|
|
/// 1. There were errors loading TypeTable. Look in the Errors property to get
|
|
/// detailed error messages.
|
|
/// </exception>
|
|
public TypeTable(IEnumerable<string> typeFiles) : this(typeFiles, null, null)
|
|
{
|
|
}
|
|
|
|
/// <summary>
|
|
/// Load types.ps1xml, typesv3.ps1xml into the typetable.
|
|
/// </summary>
|
|
/// <exception cref="System.Security.SecurityException">
|
|
/// If caller doesn't have permission to read the PowerShell registry key.
|
|
/// </exception>
|
|
/// <returns>TypeTable.</returns>
|
|
public static TypeTable LoadDefaultTypeFiles()
|
|
{
|
|
return new TypeTable(GetDefaultTypeFiles());
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets the default types files available in PowerShell.
|
|
/// </summary>
|
|
/// <returns>List of type files.</returns>
|
|
public static List<string> GetDefaultTypeFiles()
|
|
{
|
|
return new List<string>() { TypesFilePath, TypesV3FilePath };
|
|
}
|
|
|
|
/// <summary>
|
|
/// Constructor that creates a TypeTable from a set of type files.
|
|
/// </summary>
|
|
/// <param name="typeFiles">
|
|
/// Type files to load for type information.
|
|
/// </param>
|
|
/// <param name="authorizationManager">
|
|
/// Authorization manager to perform signature checks before reading ps1xml files (or null of no checks are needed)
|
|
/// </param>
|
|
/// <param name="host">
|
|
/// Host passed to <paramref name="authorizationManager"/>. Can be null if no interactive questions should be asked.
|
|
/// </param>
|
|
/// <exception cref="ArgumentException">
|
|
/// 1. Path {0} is not fully qualified. Specify a fully qualified type file path.
|
|
/// </exception>
|
|
/// <exception cref="TypeTableLoadException">
|
|
/// 1. There were errors loading TypeTable. Look in the Errors property to get
|
|
/// detailed error messages.
|
|
/// </exception>
|
|
internal TypeTable(IEnumerable<string> typeFiles, AuthorizationManager authorizationManager, PSHost host) : this(isShared: true)
|
|
{
|
|
if (typeFiles == null)
|
|
{
|
|
throw PSTraceSource.NewArgumentNullException(nameof(typeFiles));
|
|
}
|
|
|
|
ConcurrentBag<string> errors = new ConcurrentBag<string>();
|
|
foreach (string typefile in typeFiles)
|
|
{
|
|
if (string.IsNullOrEmpty(typefile) || (!Path.IsPathRooted(typefile)))
|
|
{
|
|
throw PSTraceSource.NewArgumentException("typeFile", TypesXmlStrings.TypeFileNotRooted, typefile);
|
|
}
|
|
|
|
bool unused;
|
|
Initialize(string.Empty, typefile, errors, authorizationManager, host, out unused);
|
|
_typeFileList.Add(typefile);
|
|
}
|
|
|
|
if (!errors.IsEmpty)
|
|
{
|
|
throw new TypeTableLoadException(errors);
|
|
}
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region internal methods
|
|
|
|
/// <summary>
|
|
/// The first type in the type hierarchy is guaranteed to have SpecificProperties.
|
|
/// </summary>
|
|
/// <param name="types"></param>
|
|
/// <returns>Null if this should not be serialized with SpecificProperties.</returns>
|
|
internal Collection<string> GetSpecificProperties(ConsolidatedString types)
|
|
{
|
|
if (types == null || string.IsNullOrEmpty(types.Key))
|
|
{
|
|
return new Collection<string>();
|
|
}
|
|
|
|
Collection<string> result = _consolidatedSpecificProperties.GetOrAdd(types.Key, key =>
|
|
{
|
|
var retValueTable = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
|
|
foreach (string type in types)
|
|
{
|
|
if (!_extendedMembers.TryGetValue(type, out var typeMembers))
|
|
{
|
|
continue;
|
|
}
|
|
|
|
PSMemberSet settings = typeMembers[PSStandardMembers] as PSMemberSet;
|
|
if (!(settings?.Members[PropertySerializationSet] is PSPropertySet typeProperties))
|
|
{
|
|
continue;
|
|
}
|
|
|
|
foreach (string reference in typeProperties.ReferencedPropertyNames)
|
|
{
|
|
retValueTable.Add(reference);
|
|
}
|
|
|
|
bool inherit = (bool)PSObject.GetNoteSettingValue(
|
|
settings,
|
|
InheritPropertySerializationSet,
|
|
DefaultInheritPropertySerializationSet,
|
|
expectedType: typeof(bool),
|
|
shouldReplicateInstance: false,
|
|
ownerObject: null);
|
|
if (!inherit)
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
var retValue = new Collection<string>();
|
|
foreach (var value in retValueTable)
|
|
{
|
|
retValue.Add(value);
|
|
}
|
|
|
|
return retValue;
|
|
});
|
|
return result;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets the MemberInfoCollection for types. This method will cache its
|
|
/// return value for future reference to the same types.
|
|
/// </summary>
|
|
/// <param name="types">List of types to get the member from.</param>
|
|
/// <returns></returns>
|
|
internal PSMemberInfoInternalCollection<T> GetMembers<T>(ConsolidatedString types) where T : PSMemberInfo
|
|
{
|
|
return PSObject.TransformMemberInfoCollection<PSMemberInfo, T>(GetMembers(types));
|
|
}
|
|
|
|
internal T GetFirstMemberOrDefault<T>(ConsolidatedString types, MemberNamePredicate predicate) where T : PSMemberInfo
|
|
{
|
|
return GetMembers(types).FirstOrDefault(member => member is T && predicate(member.Name)) as T;
|
|
}
|
|
|
|
internal PSMemberInfoInternalCollection<PSMemberInfo> GetMembers(ConsolidatedString types)
|
|
{
|
|
if ((types == null) || string.IsNullOrEmpty(types.Key))
|
|
{
|
|
return new PSMemberInfoInternalCollection<PSMemberInfo>();
|
|
}
|
|
|
|
return _consolidatedMembers.GetOrAdd(types.Key, _memberFactoryFunc, types);
|
|
}
|
|
|
|
private PSMemberInfoInternalCollection<PSMemberInfo> MemberFactory(string k, ConsolidatedString types)
|
|
{
|
|
var retValue = new PSMemberInfoInternalCollection<PSMemberInfo>();
|
|
for (int i = types.Count - 1; i >= 0; i--)
|
|
{
|
|
if (!_extendedMembers.TryGetValue(types[i], out var typeMembers))
|
|
{
|
|
continue;
|
|
}
|
|
|
|
foreach (PSMemberInfo typeMember in typeMembers)
|
|
{
|
|
PSMemberInfo currentMember = retValue[typeMember.Name];
|
|
|
|
// If the member was not present, we add it
|
|
if (currentMember == null)
|
|
{
|
|
retValue.Add(typeMember.Copy());
|
|
continue;
|
|
}
|
|
|
|
// There was a currentMember with the same name as typeMember
|
|
PSMemberSet currentMemberAsMemberSet = currentMember as PSMemberSet;
|
|
PSMemberSet typeMemberAsMemberSet = typeMember as PSMemberSet;
|
|
|
|
// if we are not in a memberset inherit members situation we just replace
|
|
// the current member with the new more specific member
|
|
if (currentMemberAsMemberSet == null || typeMemberAsMemberSet == null ||
|
|
!typeMemberAsMemberSet.InheritMembers)
|
|
{
|
|
retValue.Remove(typeMember.Name);
|
|
retValue.Add(typeMember.Copy());
|
|
continue;
|
|
}
|
|
|
|
// We are in a MemberSet InheritMembers situation, so we add the members in
|
|
// typeMembers to the existing memberset.
|
|
foreach (PSMemberInfo typeMemberAsMemberSetMember in typeMemberAsMemberSet.Members)
|
|
{
|
|
if (currentMemberAsMemberSet.Members[typeMemberAsMemberSetMember.Name] == null)
|
|
{
|
|
((PSMemberInfoIntegratingCollection<PSMemberInfo>)currentMemberAsMemberSet.Members)
|
|
.AddToTypesXmlCache(typeMemberAsMemberSetMember, false);
|
|
continue;
|
|
}
|
|
|
|
// there is a name conflict, the new member wins.
|
|
Diagnostics.Assert(
|
|
!typeMemberAsMemberSetMember.IsHidden,
|
|
"new member in types.xml cannot be hidden");
|
|
currentMemberAsMemberSet.InternalMembers.Replace(typeMemberAsMemberSetMember);
|
|
}
|
|
}
|
|
}
|
|
|
|
return retValue;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets the type converter for the typeName.
|
|
/// </summary>
|
|
/// <param name="typeName">Type name with the converter.</param>
|
|
/// <returns>The type converter for the typeName or null, if there is no type converter.</returns>
|
|
internal object GetTypeConverter(string typeName)
|
|
{
|
|
if (string.IsNullOrEmpty(typeName))
|
|
{
|
|
return null;
|
|
}
|
|
|
|
object result;
|
|
_typeConverters.TryGetValue(typeName, out result);
|
|
return result;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets the type adapter for the given type.
|
|
/// </summary>
|
|
/// <returns>The type adapter or null, if there is no adapter.</returns>
|
|
internal PSObject.AdapterSet GetTypeAdapter(Type type)
|
|
{
|
|
if (type == null)
|
|
{
|
|
return null;
|
|
}
|
|
|
|
/*
|
|
* BUGBUG: This should be a look-up based on Type.IsAssignableFrom instead of a
|
|
* strict match on the type name. The look-up should be similar to the
|
|
* code below, except that child classes must override base classes
|
|
* in the same hierarchy.
|
|
*
|
|
* The same applies to TypeConverters
|
|
*
|
|
* The matching for both converters and adapters should be revisited in
|
|
* the M3 milestone.
|
|
*/
|
|
#if true
|
|
PSObject.AdapterSet result;
|
|
_typeAdapters.TryGetValue(type.FullName, out result);
|
|
return result;
|
|
#else
|
|
foreach (PSObject.AdapterSet adapterSet in this.typeAdapters.Values)
|
|
{
|
|
ThirdPartyAdapter adapter = (ThirdPartyAdapter)adapterSet.OriginalAdapter;
|
|
|
|
if (adapter.AdaptedType.IsAssignableFrom(type))
|
|
{
|
|
return adapterSet;
|
|
}
|
|
}
|
|
|
|
return null;
|
|
#endif
|
|
}
|
|
|
|
private TypeMemberData GetTypeMemberDataFromPSMemberInfo(PSMemberInfo member)
|
|
{
|
|
var note = member as PSNoteProperty;
|
|
if (note != null)
|
|
{
|
|
return new NotePropertyData(note.Name, note.Value);
|
|
}
|
|
|
|
var alias = member as PSAliasProperty;
|
|
if (alias != null)
|
|
{
|
|
return new AliasPropertyData(alias.Name, alias.ReferencedMemberName);
|
|
}
|
|
|
|
var scriptProperty = member as PSScriptProperty;
|
|
if (scriptProperty != null)
|
|
{
|
|
ScriptBlock getter = scriptProperty.IsGettable ? scriptProperty.GetterScript : null;
|
|
ScriptBlock setter = scriptProperty.IsSettable ? scriptProperty.SetterScript : null;
|
|
return new ScriptPropertyData(scriptProperty.Name, getter, setter);
|
|
}
|
|
|
|
var codeProperty = member as PSCodeProperty;
|
|
if (codeProperty != null)
|
|
{
|
|
MethodInfo getter = codeProperty.IsGettable ? codeProperty.GetterCodeReference : null;
|
|
MethodInfo setter = codeProperty.IsSettable ? codeProperty.SetterCodeReference : null;
|
|
return new CodePropertyData(codeProperty.Name, getter, setter);
|
|
}
|
|
|
|
var scriptMethod = member as PSScriptMethod;
|
|
if (scriptMethod != null)
|
|
{
|
|
return new ScriptMethodData(scriptMethod.Name, scriptMethod.Script);
|
|
}
|
|
|
|
var codeMethod = member as PSCodeMethod;
|
|
if (codeMethod != null)
|
|
{
|
|
return new CodeMethodData(codeMethod.Name, codeMethod.CodeReference);
|
|
}
|
|
|
|
var memberSet = member as PSMemberSet;
|
|
if (memberSet != null)
|
|
{
|
|
if (memberSet.Name.Equals(PSStandardMembers, StringComparison.OrdinalIgnoreCase))
|
|
{
|
|
return null;
|
|
}
|
|
|
|
var membersData = new Collection<TypeMemberData>();
|
|
foreach (var m in memberSet.Members)
|
|
{
|
|
membersData.Add(GetTypeMemberDataFromPSMemberInfo(m));
|
|
}
|
|
|
|
return new MemberSetData(memberSet.Name, membersData);
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Load a PSMemberInfo instance to the passed-in TypeData.
|
|
/// </summary>
|
|
/// <param name="member"></param>
|
|
/// <param name="typeData"></param>
|
|
private void LoadMembersToTypeData(PSMemberInfo member, TypeData typeData)
|
|
{
|
|
Dbg.Assert(member != null, "caller should guarantee that member is not null");
|
|
Dbg.Assert(typeData != null, "caller should guarantee that typeData is not null");
|
|
|
|
var memberData = GetTypeMemberDataFromPSMemberInfo(member);
|
|
if (memberData != null)
|
|
{
|
|
typeData.Members.Add(memberData.Name, memberData);
|
|
return;
|
|
}
|
|
|
|
var memberSet = member as PSMemberSet;
|
|
if (memberSet != null)
|
|
{
|
|
if (memberSet.Name.Equals(PSStandardMembers, StringComparison.OrdinalIgnoreCase))
|
|
{
|
|
LoadStandardMembersToTypeData(memberSet, typeData);
|
|
}
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Helper function to convert an object to a specific type.
|
|
/// </summary>
|
|
/// <typeparam name="T"></typeparam>
|
|
/// <param name="sourceValue"></param>
|
|
/// <returns></returns>
|
|
private static T GetParameterType<T>(object sourceValue)
|
|
{
|
|
return (T)LanguagePrimitives.ConvertTo(sourceValue, typeof(T), CultureInfo.InvariantCulture);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Load the standard members into the passed-in TypeData.
|
|
/// </summary>
|
|
private void LoadStandardMembersToTypeData(PSMemberSet memberSet, TypeData typeData)
|
|
{
|
|
foreach (PSMemberInfo member in memberSet.InternalMembers)
|
|
{
|
|
PSMemberInfo standardMember = member.Copy();
|
|
var memberName = standardMember.Name;
|
|
if (memberName.Equals(SerializationMethodNode, StringComparison.OrdinalIgnoreCase))
|
|
{
|
|
typeData.SerializationMethod = GetParameterType<string>(((PSNoteProperty)standardMember).Value);
|
|
}
|
|
else if (memberName.Equals(TargetTypeForDeserialization, StringComparison.OrdinalIgnoreCase))
|
|
{
|
|
// Value property of the TargetTypeForDeserialization member is guaranteed to be of System.Type
|
|
typeData.TargetTypeForDeserialization = GetParameterType<Type>(((PSNoteProperty)standardMember).Value);
|
|
}
|
|
else if (memberName.Equals(SerializationDepth, StringComparison.OrdinalIgnoreCase))
|
|
{
|
|
// Value property of the SerializationDepth member is guaranteed to be of type int
|
|
typeData.SerializationDepth = GetParameterType<uint>(((PSNoteProperty)standardMember).Value);
|
|
}
|
|
else if (memberName.Equals(DefaultDisplayProperty, StringComparison.OrdinalIgnoreCase))
|
|
{
|
|
// Value property of the DefaultDisplayProperty is guaranteed to be of type string
|
|
typeData.DefaultDisplayProperty = GetParameterType<string>(((PSNoteProperty)standardMember).Value);
|
|
}
|
|
else if (memberName.Equals(InheritPropertySerializationSet, StringComparison.OrdinalIgnoreCase))
|
|
{
|
|
// Value property of the InheritPropertySerializationSet is guaranteed to be of type bool
|
|
typeData.InheritPropertySerializationSet = GetParameterType<bool>(((PSNoteProperty)standardMember).Value);
|
|
}
|
|
else if (memberName.Equals(StringSerializationSource, StringComparison.OrdinalIgnoreCase))
|
|
{
|
|
var aliasProperty = standardMember as PSAliasProperty;
|
|
if (aliasProperty != null)
|
|
{
|
|
typeData.StringSerializationSource = aliasProperty.ReferencedMemberName;
|
|
}
|
|
else
|
|
{
|
|
typeData.StringSerializationSourceProperty = GetTypeMemberDataFromPSMemberInfo(standardMember);
|
|
}
|
|
}
|
|
else if (memberName.Equals(DefaultDisplayPropertySet, StringComparison.OrdinalIgnoreCase))
|
|
{
|
|
typeData.DefaultDisplayPropertySet =
|
|
new PropertySetData(((PSPropertySet)standardMember).ReferencedPropertyNames);
|
|
}
|
|
else if (memberName.Equals(DefaultKeyPropertySet, StringComparison.OrdinalIgnoreCase))
|
|
{
|
|
typeData.DefaultKeyPropertySet =
|
|
new PropertySetData(((PSPropertySet)standardMember).ReferencedPropertyNames);
|
|
}
|
|
else if (memberName.Equals(PropertySerializationSet, StringComparison.OrdinalIgnoreCase))
|
|
{
|
|
typeData.PropertySerializationSet =
|
|
new PropertySetData(((PSPropertySet)standardMember).ReferencedPropertyNames);
|
|
}
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Get all Type configurations, return a Dictionary with typeName as the key, TypeData as the value.
|
|
/// </summary>
|
|
/// <returns></returns>
|
|
internal Dictionary<string, TypeData> GetAllTypeData()
|
|
{
|
|
Dictionary<string, TypeData> allTypes = new Dictionary<string, TypeData>();
|
|
foreach (string typeName in _extendedMembers.Keys)
|
|
{
|
|
if (allTypes.ContainsKey(typeName))
|
|
{
|
|
continue;
|
|
}
|
|
|
|
TypeData typeData = new TypeData(typeName);
|
|
bool isTypeDataLoaded = false;
|
|
|
|
isTypeDataLoaded |= RetrieveMembersToTypeData(typeData);
|
|
isTypeDataLoaded |= RetrieveConverterToTypeData(typeData);
|
|
isTypeDataLoaded |= RetrieveAdapterToTypeData(typeData);
|
|
|
|
if (isTypeDataLoaded)
|
|
{
|
|
allTypes.Add(typeName, typeData);
|
|
}
|
|
}
|
|
|
|
foreach (string typeName in _typeConverters.Keys)
|
|
{
|
|
if (allTypes.ContainsKey(typeName))
|
|
{
|
|
continue;
|
|
}
|
|
|
|
TypeData typeData = new TypeData(typeName);
|
|
bool isTypeDataLoaded = false;
|
|
|
|
isTypeDataLoaded |= RetrieveConverterToTypeData(typeData);
|
|
isTypeDataLoaded |= RetrieveAdapterToTypeData(typeData);
|
|
|
|
if (isTypeDataLoaded)
|
|
{
|
|
allTypes.Add(typeName, typeData);
|
|
}
|
|
}
|
|
|
|
foreach (string typeName in _typeAdapters.Keys)
|
|
{
|
|
if (allTypes.ContainsKey(typeName))
|
|
{
|
|
continue;
|
|
}
|
|
|
|
TypeData typeData = new TypeData(typeName);
|
|
|
|
if (RetrieveAdapterToTypeData(typeData))
|
|
{
|
|
allTypes.Add(typeName, typeData);
|
|
}
|
|
}
|
|
|
|
return allTypes;
|
|
}
|
|
|
|
private bool RetrieveMembersToTypeData(TypeData typeData)
|
|
{
|
|
Dbg.Assert(typeData != null && typeData.TypeName != null, "The caller needs to make sure typeData != null");
|
|
string typeName = typeData.TypeName;
|
|
|
|
PSMemberInfoInternalCollection<PSMemberInfo> typeMembers;
|
|
if (_extendedMembers.TryGetValue(typeName, out typeMembers))
|
|
{
|
|
Dbg.Assert(typeMembers != null, "members should not be null");
|
|
foreach (PSMemberInfo member in typeMembers)
|
|
{
|
|
PSMemberInfo newMember = member.Copy();
|
|
LoadMembersToTypeData(newMember, typeData);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
private bool RetrieveConverterToTypeData(TypeData typeData)
|
|
{
|
|
Dbg.Assert(typeData != null && typeData.TypeName != null, "The caller needs to make sure typeData != null");
|
|
string typeName = typeData.TypeName;
|
|
|
|
object converterResult;
|
|
if (_typeConverters.TryGetValue(typeName, out converterResult))
|
|
{
|
|
Dbg.Assert(converterResult != null, "converter should not be null");
|
|
typeData.TypeConverter = converterResult.GetType();
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
private bool RetrieveAdapterToTypeData(TypeData typeData)
|
|
{
|
|
Dbg.Assert(typeData != null && typeData.TypeName != null, "The caller needs to make sure typeData != null");
|
|
string typeName = typeData.TypeName;
|
|
|
|
PSObject.AdapterSet adapterResult;
|
|
if (_typeAdapters.TryGetValue(typeName, out adapterResult))
|
|
{
|
|
Dbg.Assert(adapterResult != null, "adapter should not be null");
|
|
typeData.TypeAdapter = ((ThirdPartyAdapter)(adapterResult.OriginalAdapter)).ExternalAdapterType;
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Clone the TypeTable by doing a shallow copy of all the members.
|
|
/// </summary>
|
|
/// <param name="unshared">
|
|
/// Indicate that the clone of this TypeTable instance should not be marked as "Shared", even if
|
|
/// this TypeTable instance itself is a shared TypeTable.
|
|
/// </param>
|
|
/// <returns>
|
|
/// If <para>unshared</para> is True, return an unshared clone of this TypeTable instance
|
|
/// If <para>unshared</para> is False, return a clone that is exactly the same as this TypeTable instance.
|
|
/// </returns>
|
|
public TypeTable Clone(bool unshared)
|
|
{
|
|
TypeTable result = unshared ? new TypeTable() : new TypeTable(this.isShared);
|
|
|
|
// 1. Update the result typeTable with the current typeTable
|
|
// 2. Shallow copy the 'typesInfo'
|
|
|
|
// copy the type members
|
|
foreach (var pair in _extendedMembers)
|
|
{
|
|
var resultMembers = new PSMemberInfoInternalCollection<PSMemberInfo>();
|
|
foreach (var member in pair.Value)
|
|
{
|
|
resultMembers.Add(member.Copy());
|
|
}
|
|
|
|
result._extendedMembers.TryAdd(pair.Key, resultMembers);
|
|
}
|
|
|
|
// copy the type adapter members
|
|
foreach (var pair in _typeAdapters)
|
|
{
|
|
result._typeAdapters.TryAdd(pair.Key, pair.Value);
|
|
}
|
|
|
|
// copy the type converters
|
|
foreach (var pair in _typeConverters)
|
|
{
|
|
result._typeConverters.TryAdd(pair.Key, pair.Value);
|
|
}
|
|
|
|
result.typesInfo.Add(this.typesInfo);
|
|
|
|
return result;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Clear the TypeTable.
|
|
/// </summary>
|
|
internal void Clear()
|
|
{
|
|
foreach (var conv in _typeConverters.Keys)
|
|
{
|
|
LanguagePrimitives.UpdateTypeConvertFromTypeTable(conv);
|
|
}
|
|
|
|
_typeConverters.Clear();
|
|
|
|
foreach (var ml in _extendedMembers.Values)
|
|
{
|
|
foreach (var m in ml)
|
|
{
|
|
PSGetMemberBinder.TypeTableMemberPossiblyUpdated(m.Name);
|
|
}
|
|
}
|
|
|
|
_extendedMembers.Clear();
|
|
|
|
StandardMembersUpdated();
|
|
|
|
_typeAdapters.Clear();
|
|
typesInfo.Clear();
|
|
|
|
ClearConsolidatedMembers();
|
|
}
|
|
|
|
internal void ClearConsolidatedMembers()
|
|
{
|
|
_consolidatedMembers.Clear();
|
|
_consolidatedSpecificProperties.Clear();
|
|
}
|
|
|
|
private static void StandardMembersUpdated()
|
|
{
|
|
foreach (var sm in s_standardMembers)
|
|
{
|
|
PSGetMemberBinder.TypeTableMemberPossiblyUpdated(sm);
|
|
}
|
|
|
|
PSGetMemberBinder.TypeTableMemberPossiblyUpdated(PSStandardMembers);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Load the specified file and report the errors in <paramref name="errors."/>
|
|
/// </summary>
|
|
/// <param name="snapinName"></param>
|
|
/// <param name="fileToLoad">
|
|
/// Type file to load. File should be a fully-qualified path.
|
|
/// </param>
|
|
/// <param name="errors"></param>
|
|
/// <param name="authorizationManager">
|
|
/// Authorization manager to perform signature checks before reading ps1xml files (or null of no checks are needed)
|
|
/// </param>
|
|
/// <param name="host">
|
|
/// Host passed to <paramref name="authorizationManager"/>. Can be null if no interactive questions should be asked.
|
|
/// </param>
|
|
/// <param name="failToLoadFile">Indicate if the file failed to be loaded.</param>
|
|
internal void Initialize(
|
|
string snapinName,
|
|
string fileToLoad,
|
|
ConcurrentBag<string> errors,
|
|
AuthorizationManager authorizationManager,
|
|
PSHost host,
|
|
out bool failToLoadFile)
|
|
{
|
|
if (ProcessIsBuiltIn(fileToLoad, errors, out failToLoadFile))
|
|
{
|
|
return;
|
|
}
|
|
|
|
bool isFullyTrusted;
|
|
bool isProductCode;
|
|
string fileContents = GetModuleContents(snapinName, fileToLoad, errors, authorizationManager, host, out isFullyTrusted, out failToLoadFile, out isProductCode);
|
|
|
|
if (fileContents == null)
|
|
{
|
|
return;
|
|
}
|
|
|
|
UpdateWithModuleContents(fileContents, snapinName, fileToLoad, isFullyTrusted, isProductCode, errors);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Helper method to load content for a module.
|
|
/// </summary>
|
|
private static string GetModuleContents(
|
|
string moduleName,
|
|
string fileToLoad,
|
|
ConcurrentBag<string> errors,
|
|
AuthorizationManager authorizationManager,
|
|
PSHost host,
|
|
out bool isFullyTrusted,
|
|
out bool failToLoadFile,
|
|
out bool isProductCode)
|
|
{
|
|
Dbg.Assert(Path.IsPathRooted(fileToLoad), "fileToLoad should be a fully-qualified path.");
|
|
|
|
ExternalScriptInfo ps1xmlInfo;
|
|
string fileContents;
|
|
isFullyTrusted = false;
|
|
isProductCode = false;
|
|
|
|
try
|
|
{
|
|
ps1xmlInfo = new ExternalScriptInfo(fileToLoad, fileToLoad);
|
|
fileContents = ps1xmlInfo.ScriptContents;
|
|
|
|
if (ps1xmlInfo.DefiningLanguageMode == PSLanguageMode.FullLanguage)
|
|
{
|
|
isFullyTrusted = true;
|
|
}
|
|
|
|
if (SecuritySupport.IsProductBinary(fileToLoad))
|
|
{
|
|
isProductCode = true;
|
|
}
|
|
}
|
|
catch (SecurityException e)
|
|
{
|
|
string errorMsg = StringUtil.Format(TypesXmlStrings.Exception, e.Message);
|
|
string errorLine = StringUtil.Format(TypesXmlStrings.FileError, moduleName, fileToLoad, errorMsg);
|
|
errors.Add(errorLine);
|
|
|
|
failToLoadFile = true;
|
|
return null;
|
|
}
|
|
|
|
// Check authorization.
|
|
if (authorizationManager != null)
|
|
{
|
|
try
|
|
{
|
|
authorizationManager.ShouldRunInternal(ps1xmlInfo, CommandOrigin.Internal, host);
|
|
}
|
|
catch (PSSecurityException reason)
|
|
{
|
|
string errorMessage = StringUtil.Format(TypesXmlStrings.ValidationException, moduleName, fileToLoad, reason.Message);
|
|
errors.Add(errorMessage);
|
|
|
|
failToLoadFile = true;
|
|
return null;
|
|
}
|
|
}
|
|
|
|
failToLoadFile = false;
|
|
return fileContents;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Helper method to update with module file contents.
|
|
/// </summary>
|
|
/// <param name="fileContents">Module contents.</param>
|
|
/// <param name="moduleName">Module name.</param>
|
|
/// <param name="fileToLoad">Module file path.</param>
|
|
/// <param name="isFullyTrusted">Whether the module contents are fully trusted.</param>
|
|
/// <param name="isProductCode">Whether the module contents are considered part of Windows (e.g. catalog signed).</param>
|
|
/// <param name="errors">Errors.</param>
|
|
private void UpdateWithModuleContents(
|
|
string fileContents,
|
|
string moduleName,
|
|
string fileToLoad,
|
|
bool isFullyTrusted,
|
|
bool isProductCode,
|
|
ConcurrentBag<string> errors)
|
|
{
|
|
LoadContext loadContext = new LoadContext(moduleName, fileToLoad, errors)
|
|
{
|
|
IsFullyTrusted = isFullyTrusted,
|
|
IsProductCode = isProductCode,
|
|
};
|
|
|
|
using (StringReader xmlStream = new StringReader(fileContents))
|
|
{
|
|
XmlReader reader = new XmlTextReader(xmlStream) { WhitespaceHandling = WhitespaceHandling.Significant };
|
|
loadContext.reader = reader;
|
|
Update(loadContext);
|
|
reader.Dispose();
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Update the TypeTable by adding a TypeData instance.
|
|
/// </summary>
|
|
/// <exception cref="PSArgumentNullException">Throw when the argument is null.</exception>
|
|
/// <exception cref="RuntimeException">Throw when there were failures during the update.</exception>
|
|
/// <param name="typeData">A TypeData instance to update the TypeTable.</param>
|
|
public void AddType(TypeData typeData)
|
|
{
|
|
if (typeData == null)
|
|
throw PSTraceSource.NewArgumentNullException(nameof(typeData));
|
|
|
|
Dbg.Assert(isShared, "This method should only be called by the developer user. It should not be used internally.");
|
|
|
|
ConcurrentBag<string> errors = new ConcurrentBag<string>();
|
|
|
|
// Always clear the consolidate members - they need to be recalculated
|
|
// anytime the types are updated...
|
|
ClearConsolidatedMembers();
|
|
|
|
Update(errors, typeData, false);
|
|
StandardMembersUpdated();
|
|
|
|
// Throw exception if there are any errors
|
|
FormatAndTypeDataHelper.ThrowExceptionOnError("ErrorsUpdatingTypes", errors, FormatAndTypeDataHelper.Category.Types);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Remove all type information related to the type name.
|
|
/// </summary>
|
|
/// <exception cref="PSArgumentNullException">Throw when the argument is null or empty.</exception>
|
|
/// <exception cref="RuntimeException">Throw if there were failures when remove the type.</exception>
|
|
/// <param name="typeName">The name of the type to remove from TypeTable.</param>
|
|
public void RemoveType(string typeName)
|
|
{
|
|
if (string.IsNullOrEmpty(typeName))
|
|
{
|
|
throw PSTraceSource.NewArgumentNullException(nameof(typeName));
|
|
}
|
|
|
|
Dbg.Assert(isShared, "This method should only be called by the developer user. It should not be used internally.");
|
|
|
|
TypeData typeData = new TypeData(typeName);
|
|
ConcurrentBag<string> errors = new ConcurrentBag<string>();
|
|
|
|
// Always clear the consolidate members - they need to be recalculated
|
|
// anytime the types are updated...
|
|
ClearConsolidatedMembers();
|
|
|
|
Update(errors, typeData, true);
|
|
StandardMembersUpdated();
|
|
|
|
// Throw exception if there are any errors
|
|
FormatAndTypeDataHelper.ThrowExceptionOnError("ErrorsUpdatingTypes", errors, FormatAndTypeDataHelper.Category.Types);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Update type data from a specific file...
|
|
/// </summary>
|
|
/// <param name="moduleName">The name of the module or snapin that this file is associated with.</param>
|
|
/// <param name="filePath">The path to the file to load.</param>
|
|
/// <param name="errors">A place to put the errors...</param>
|
|
/// <param name="authorizationManager">
|
|
/// Authorization manager to perform signature checks before reading ps1xml files (or null of no checks are needed)
|
|
/// </param>
|
|
/// <param name="host">
|
|
/// Host passed to <paramref name="authorizationManager"/>. Can be null if no interactive questions should be asked.
|
|
/// </param>
|
|
/// <param name="failToLoadFile">Indicate if the file cannot be loaded due to security reason.</param>
|
|
/// <exception cref="InvalidOperationException">
|
|
/// 1. The TypeTable cannot be updated because the TypeTable might have
|
|
/// been created outside of the Runspace.
|
|
/// </exception>
|
|
internal void Update(
|
|
string moduleName,
|
|
string filePath,
|
|
ConcurrentBag<string> errors,
|
|
AuthorizationManager authorizationManager,
|
|
PSHost host,
|
|
out bool failToLoadFile)
|
|
{
|
|
if (filePath == null)
|
|
{
|
|
throw new ArgumentNullException(nameof(filePath));
|
|
}
|
|
|
|
if (errors == null)
|
|
{
|
|
throw new ArgumentNullException(nameof(errors));
|
|
}
|
|
|
|
if (isShared)
|
|
{
|
|
throw PSTraceSource.NewInvalidOperationException(TypesXmlStrings.SharedTypeTableCannotBeUpdated);
|
|
}
|
|
|
|
var etwEnabled = RunspaceEventSource.Log.IsEnabled();
|
|
if (etwEnabled)
|
|
{
|
|
RunspaceEventSource.Log.ProcessTypeFileStart(filePath);
|
|
}
|
|
|
|
if (!ProcessIsBuiltIn(filePath, errors, out failToLoadFile))
|
|
{
|
|
// Get file contents and perform authorization check
|
|
// (including possible user prompt) outside of the lock.
|
|
bool isFullyTrusted;
|
|
bool isProductCode;
|
|
|
|
var fileContents = GetModuleContents(moduleName, filePath, errors, authorizationManager, host, out isFullyTrusted, out failToLoadFile, out isProductCode);
|
|
|
|
if (fileContents != null)
|
|
{
|
|
UpdateWithModuleContents(fileContents, moduleName, filePath, isFullyTrusted, isProductCode, errors);
|
|
}
|
|
}
|
|
|
|
if (etwEnabled)
|
|
{
|
|
RunspaceEventSource.Log.ProcessTypeFileStop(filePath);
|
|
}
|
|
}
|
|
|
|
private bool ProcessIsBuiltIn(string filePath, ConcurrentBag<string> errors, out bool failToLoadFile)
|
|
{
|
|
var result = false;
|
|
var errorCount = errors.Count;
|
|
|
|
if (string.Equals(TypesFilePath, filePath, StringComparison.OrdinalIgnoreCase))
|
|
{
|
|
Process_Types_Ps1Xml(filePath, errors);
|
|
result = true;
|
|
}
|
|
else if (string.Equals(TypesV3FilePath, filePath, StringComparison.OrdinalIgnoreCase))
|
|
{
|
|
Process_TypesV3_Ps1Xml(filePath, errors);
|
|
result = true;
|
|
}
|
|
else if (string.Equals(GetEventTypesFilePath, filePath, StringComparison.OrdinalIgnoreCase))
|
|
{
|
|
Process_GetEvent_Types_Ps1Xml(filePath, errors);
|
|
result = true;
|
|
}
|
|
|
|
failToLoadFile = errorCount < errors.Count;
|
|
return result;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Update typetable from a specific strong type data.
|
|
/// </summary>
|
|
/// <param name="type"></param>
|
|
/// <param name="errors"></param>
|
|
/// <param name="isRemove"></param>
|
|
internal void Update(
|
|
TypeData type,
|
|
ConcurrentBag<string> errors,
|
|
bool isRemove)
|
|
{
|
|
if (type == null)
|
|
throw new ArgumentNullException(nameof(type));
|
|
if (errors == null)
|
|
throw new ArgumentNullException(nameof(errors));
|
|
|
|
if (isShared)
|
|
{
|
|
throw PSTraceSource.NewInvalidOperationException(TypesXmlStrings.SharedTypeTableCannotBeUpdated);
|
|
}
|
|
|
|
Update(errors, type, isRemove);
|
|
StandardMembersUpdated();
|
|
}
|
|
|
|
#endregion internal methods
|
|
}
|
|
}
|