PowerShell/src/System.Management.Automation/engine/CoreAdapter.cs
xtqqczze 883ca98dd7
Seal private classes (#15725)
* Seal private classes

* Fix CS0509

* Fix CS0628
2021-07-19 14:09:12 +05:00

6244 lines
257 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.Data;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Dynamic;
using System.Globalization;
using System.Linq;
using System.Linq.Expressions;
using System.Management.Automation.Language;
using System.Management.Automation.Runspaces;
using System.Reflection;
using System.Reflection.Emit;
using System.Text;
using System.Threading;
using System.Xml;
using System.Management.Automation.Internal;
using Microsoft.PowerShell;
using Dbg = System.Management.Automation.Diagnostics;
#pragma warning disable 1634, 1691 // Stops compiler from warning about unknown warnings
namespace System.Management.Automation
{
/// <summary>
/// Base class for all Adapters
/// This is the place to look every time you create a new Adapter. Consider if you
/// should implement each of the virtual methods here.
/// The base class deals with errors and performs additional operations before and after
/// calling the derived virtual methods.
/// </summary>
internal abstract class Adapter
{
/// <summary>
/// Tracer for this and derivate classes.
/// </summary>
[TraceSource("ETS", "Extended Type System")]
protected static PSTraceSource tracer = PSTraceSource.GetTracer("ETS", "Extended Type System");
#region virtual
#region member
internal virtual bool CanSiteBinderOptimize(MemberTypes typeToOperateOn) { return false; }
protected static IEnumerable<string> GetDotNetTypeNameHierarchy(Type type)
{
for (; type != null; type = type.BaseType)
{
yield return type.FullName;
}
}
protected static IEnumerable<string> GetDotNetTypeNameHierarchy(object obj)
{
return GetDotNetTypeNameHierarchy(obj.GetType());
}
/// <summary>
/// Returns the TypeNameHierarchy out of an object.
/// </summary>
/// <param name="obj">Object to get the TypeNameHierarchy from.</param>
protected virtual IEnumerable<string> GetTypeNameHierarchy(object obj)
{
return GetDotNetTypeNameHierarchy(obj);
}
/// <summary>
/// Returns the cached typename, if it can be cached, otherwise constructs a new typename.
/// By default, we don't return interned values, adapters can override if they choose.
/// </summary>
/// <param name="obj">Object to get the TypeNameHierarchy from.</param>
protected virtual ConsolidatedString GetInternedTypeNameHierarchy(object obj)
{
return new ConsolidatedString(GetTypeNameHierarchy(obj));
}
/// <summary>
/// Returns null if memberName is not a member in the adapter or
/// the corresponding PSMemberInfo.
/// </summary>
/// <param name="obj">Object to retrieve the PSMemberInfo from.</param>
/// <param name="memberName">Name of the member to be retrieved.</param>
/// <returns>The PSMemberInfo corresponding to memberName from obj.</returns>
protected abstract T GetMember<T>(object obj, string memberName) where T : PSMemberInfo;
/// <summary>
/// Returns the first PSMemberInfo whose name matches the specified <see cref="MemberNamePredicate"/>.
/// Otherwise, return null.
/// </summary>
/// <param name="obj">Object to retrieve the PSMemberInfo from.</param>
/// <param name="predicate">The predicate to find the matching member.</param>
/// <returns>The PSMemberInfo corresponding to the predicate match.</returns>
protected abstract T GetFirstMemberOrDefault<T>(object obj, MemberNamePredicate predicate) where T : PSMemberInfo;
/// <summary>
/// Retrieves all the members available in the object.
/// The adapter implementation is encouraged to cache all properties/methods available
/// in the first call to GetMember and GetMembers so that subsequent
/// calls can use the cache.
/// In the case of the .NET adapter that would be a cache from the .NET type to
/// the public properties and fields available in that type.
/// In the case of the DirectoryEntry adapter, this could be a cache of the objectClass
/// to the properties available in it.
/// </summary>
/// <param name="obj">Object to get all the member information from.</param>
/// <returns>All members in obj.</returns>
protected abstract PSMemberInfoInternalCollection<T> GetMembers<T>(object obj) where T : PSMemberInfo;
#endregion member
#region property
/// <summary>
/// Returns the value from a property coming from a previous call to GetMember.
/// </summary>
/// <param name="property">PSProperty coming from a previous call to GetMember.</param>
/// <returns>The value of the property.</returns>
protected abstract object PropertyGet(PSProperty property);
/// <summary>
/// Sets the value of a property coming from a previous call to GetMember.
/// </summary>
/// <param name="property">PSProperty coming from a previous call to GetMember.</param>
/// <param name="setValue">Value to set the property with.</param>
/// <param name="convertIfPossible">Instructs the adapter to convert before setting, if the adapter supports conversion.</param>
protected abstract void PropertySet(PSProperty property, object setValue, bool convertIfPossible);
/// <summary>
/// Returns true if the property is settable.
/// </summary>
/// <param name="property">Property to check.</param>
/// <returns>True if the property is settable.</returns>
protected abstract bool PropertyIsSettable(PSProperty property);
/// <summary>
/// Returns true if the property is gettable.
/// </summary>
/// <param name="property">Property to check.</param>
/// <returns>True if the property is gettable.</returns>
protected abstract bool PropertyIsGettable(PSProperty property);
/// <summary>
/// Returns the name of the type corresponding to the property's value.
/// </summary>
/// <param name="property">PSProperty obtained in a previous GetMember.</param>
/// <param name="forDisplay">True if the result is for display purposes only.</param>
/// <returns>The name of the type corresponding to the member.</returns>
protected abstract string PropertyType(PSProperty property, bool forDisplay);
/// <summary>
/// Returns the string representation of the property in the object.
/// </summary>
/// <param name="property">Property obtained in a previous GetMember.</param>
/// <returns>The string representation of the property in the object.</returns>
protected abstract string PropertyToString(PSProperty property);
/// <summary>
/// Returns an array with the property attributes.
/// </summary>
/// <param name="property">Property we want the attributes from.</param>
/// <returns>An array with the property attributes.</returns>
protected abstract AttributeCollection PropertyAttributes(PSProperty property);
#endregion property
#region method
/// <summary>
/// Called after a non null return from GetMember to try to call
/// the method with the arguments.
/// </summary>
/// <param name="method">The non empty return from GetMethods.</param>
/// <param name="invocationConstraints">Invocation constraints.</param>
/// <param name="arguments">The arguments to use.</param>
/// <returns>The return value for the method.</returns>
protected virtual object MethodInvoke(PSMethod method, PSMethodInvocationConstraints invocationConstraints, object[] arguments)
{
return this.MethodInvoke(method, arguments);
}
/// <summary>
/// Called after a non null return from GetMember to try to call
/// the method with the arguments.
/// </summary>
/// <param name="method">The non empty return from GetMethods.</param>
/// <param name="arguments">The arguments to use.</param>
/// <returns>The return value for the method.</returns>
protected abstract object MethodInvoke(PSMethod method, object[] arguments);
/// <summary>
/// Called after a non null return from GetMember to return the overloads.
/// </summary>
/// <param name="method">The return of GetMember.</param>
/// <returns></returns>
protected abstract Collection<string> MethodDefinitions(PSMethod method);
/// <summary>
/// Returns the string representation of the method in the object.
/// </summary>
/// <returns>The string representation of the method in the object.</returns>
protected virtual string MethodToString(PSMethod method)
{
StringBuilder returnValue = new StringBuilder();
Collection<string> definitions = MethodDefinitions(method);
for (int i = 0; i < definitions.Count; i++)
{
returnValue.Append(definitions[i]);
returnValue.Append(", ");
}
returnValue.Remove(returnValue.Length - 2, 2);
return returnValue.ToString();
}
#endregion method
#region parameterized property
/// <summary>
/// Returns the name of the type corresponding to the property's value.
/// </summary>
/// <param name="property">Property obtained in a previous GetMember.</param>
/// <returns>The name of the type corresponding to the member.</returns>
/// <remarks>
/// It is not necessary for derived methods to override this.
/// This method is called only if ParameterizedProperties are present.
/// </remarks>
protected virtual string ParameterizedPropertyType(PSParameterizedProperty property)
{
Diagnostics.Assert(false, "adapter is not called for parameterized properties");
throw PSTraceSource.NewNotSupportedException();
}
/// <summary>
/// Returns true if the property is settable.
/// </summary>
/// <param name="property">Property to check.</param>
/// <returns>True if the property is settable.</returns>
/// <remarks>
/// It is not necessary for derived methods to override this.
/// This method is called only if ParameterizedProperties are present.
/// </remarks>
protected virtual bool ParameterizedPropertyIsSettable(PSParameterizedProperty property)
{
Diagnostics.Assert(false, "adapter is not called for parameterized properties");
throw PSTraceSource.NewNotSupportedException();
}
/// <summary>
/// Returns true if the property is gettable.
/// </summary>
/// <param name="property">Property to check.</param>
/// <returns>True if the property is gettable.</returns>
/// <remarks>
/// It is not necessary for derived methods to override this.
/// This method is called only if ParameterizedProperties are present.
/// </remarks>
protected virtual bool ParameterizedPropertyIsGettable(PSParameterizedProperty property)
{
Diagnostics.Assert(false, "adapter is not called for parameterized properties");
throw PSTraceSource.NewNotSupportedException();
}
/// <summary>
/// Called after a non null return from GetMember to return the overloads.
/// </summary>
/// <param name="property">The return of GetMember.</param>
/// <remarks>
/// It is not necessary for derived methods to override this.
/// This method is called only if ParameterizedProperties are present.
/// </remarks>
protected virtual Collection<string> ParameterizedPropertyDefinitions(PSParameterizedProperty property)
{
Diagnostics.Assert(false, "adapter is not called for parameterized properties");
throw PSTraceSource.NewNotSupportedException();
}
/// <summary>
/// Called after a non null return from GetMember to get the property value.
/// </summary>
/// <param name="property">The non empty return from GetMember.</param>
/// <param name="arguments">The arguments to use.</param>
/// <returns>The return value for the property.</returns>
/// <remarks>
/// It is not necessary for derived methods to override this.
/// This method is called only if ParameterizedProperties are present.
/// </remarks>
protected virtual object ParameterizedPropertyGet(PSParameterizedProperty property, object[] arguments)
{
Diagnostics.Assert(false, "adapter is not called for parameterized properties");
throw PSTraceSource.NewNotSupportedException();
}
/// <summary>
/// Called after a non null return from GetMember to set the property value.
/// </summary>
/// <param name="property">The non empty return from GetMember.</param>
/// <param name="setValue">The value to set property with.</param>
/// <param name="arguments">The arguments to use.</param>
/// <remarks>
/// It is not necessary for derived methods to override this.
/// This method is called only if ParameterizedProperties are present.
/// </remarks>
protected virtual void ParameterizedPropertySet(PSParameterizedProperty property, object setValue, object[] arguments)
{
Diagnostics.Assert(false, "adapter is not called for parameterized properties");
throw PSTraceSource.NewNotSupportedException();
}
/// <summary>
/// Returns the string representation of the property in the object.
/// </summary>
/// <param name="property">Property obtained in a previous GetMember.</param>
/// <returns>The string representation of the property in the object.</returns>
/// <remarks>
/// It is not necessary for derived methods to override this.
/// This method is called only if ParameterizedProperties are present.
/// </remarks>
protected virtual string ParameterizedPropertyToString(PSParameterizedProperty property)
{
Diagnostics.Assert(false, "adapter is not called for parameterized properties");
throw PSTraceSource.NewNotSupportedException();
}
#endregion parameterized property
#endregion virtual
#region base
#region private
private static Exception NewException(
Exception e,
string errorId,
string targetErrorId,
string resourceString,
params object[] parameters)
{
object[] newParameters = new object[parameters.Length + 1];
for (int i = 0; i < parameters.Length; i++)
{
newParameters[i + 1] = parameters[i];
}
Exception ex = e as TargetInvocationException;
if (ex != null)
{
Exception inner = ex.InnerException ?? ex;
newParameters[0] = inner.Message;
return new ExtendedTypeSystemException(
targetErrorId,
inner,
resourceString,
newParameters);
}
newParameters[0] = e.Message;
return new ExtendedTypeSystemException(
errorId,
e,
resourceString,
newParameters);
}
#endregion private
#region member
internal ConsolidatedString BaseGetTypeNameHierarchy(object obj)
{
try
{
return GetInternedTypeNameHierarchy(obj);
}
catch (ExtendedTypeSystemException) { throw; }
catch (Exception e)
{
throw NewException(
e,
"CatchFromBaseGetTypeNameHierarchy",
"CatchFromBaseGetTypeNameHierarchyTI",
ExtendedTypeSystem.ExceptionRetrievingTypeNameHierarchy);
}
}
internal T BaseGetMember<T>(object obj, string memberName) where T : PSMemberInfo
{
try
{
return this.GetMember<T>(obj, memberName);
}
catch (ExtendedTypeSystemException) { throw; }
catch (Exception e)
{
throw NewException(
e,
"CatchFromBaseGetMember",
"CatchFromBaseGetMemberTI",
ExtendedTypeSystem.ExceptionGettingMember,
memberName);
}
}
internal T BaseGetFirstMemberOrDefault<T>(object obj, MemberNamePredicate predicate) where T : PSMemberInfo
{
try
{
return this.GetFirstMemberOrDefault<T>(obj, predicate);
}
catch (ExtendedTypeSystemException) { throw; }
catch (Exception e)
{
throw NewException(
e,
"CatchFromBaseGetMember",
"CatchFromBaseGetMemberTI",
ExtendedTypeSystem.ExceptionGettingMember,
nameof(predicate));
}
}
internal PSMemberInfoInternalCollection<T> BaseGetMembers<T>(object obj) where T : PSMemberInfo
{
try
{
return this.GetMembers<T>(obj);
}
catch (ExtendedTypeSystemException) { throw; }
catch (Exception e)
{
throw NewException(
e,
"CatchFromBaseGetMembers",
"CatchFromBaseGetMembersTI",
ExtendedTypeSystem.ExceptionGettingMembers);
}
}
#endregion member
#region property
internal object BasePropertyGet(PSProperty property)
{
try
{
return PropertyGet(property);
}
catch (TargetInvocationException ex)
{
Exception inner = ex.InnerException ?? ex;
throw new GetValueInvocationException(
"CatchFromBaseAdapterGetValueTI",
inner,
ExtendedTypeSystem.ExceptionWhenGetting,
property.Name, inner.Message);
}
catch (GetValueException) { throw; }
catch (Exception e)
{
throw new GetValueInvocationException(
"CatchFromBaseAdapterGetValue",
e,
ExtendedTypeSystem.ExceptionWhenGetting,
property.Name, e.Message);
}
}
internal void BasePropertySet(PSProperty property, object setValue, bool convert)
{
try
{
PropertySet(property, setValue, convert);
}
catch (TargetInvocationException ex)
{
Exception inner = ex.InnerException ?? ex;
throw new SetValueInvocationException(
"CatchFromBaseAdapterSetValueTI",
inner,
ExtendedTypeSystem.ExceptionWhenSetting,
property.Name, inner.Message);
}
catch (SetValueException) { throw; }
catch (Exception e)
{
throw new SetValueInvocationException(
"CatchFromBaseAdapterSetValue",
e,
ExtendedTypeSystem.ExceptionWhenSetting,
property.Name, e.Message);
}
}
internal bool BasePropertyIsSettable(PSProperty property)
{
try
{
return this.PropertyIsSettable(property);
}
catch (ExtendedTypeSystemException) { throw; }
catch (Exception e)
{
throw NewException(
e,
"CatchFromBasePropertyIsSettable",
"CatchFromBasePropertyIsSettableTI",
ExtendedTypeSystem.ExceptionRetrievingPropertyWriteState,
property.Name);
}
}
internal bool BasePropertyIsGettable(PSProperty property)
{
try
{
return this.PropertyIsGettable(property);
}
catch (ExtendedTypeSystemException) { throw; }
catch (Exception e)
{
throw NewException(
e,
"CatchFromBasePropertyIsGettable",
"CatchFromBasePropertyIsGettableTI",
ExtendedTypeSystem.ExceptionRetrievingPropertyReadState,
property.Name);
}
}
internal string BasePropertyType(PSProperty property)
{
try
{
return this.PropertyType(property, forDisplay: false);
}
catch (ExtendedTypeSystemException) { throw; }
catch (Exception e)
{
throw NewException(
e,
"CatchFromBasePropertyType",
"CatchFromBasePropertyTypeTI",
ExtendedTypeSystem.ExceptionRetrievingPropertyType,
property.Name);
}
}
internal string BasePropertyToString(PSProperty property)
{
try
{
return this.PropertyToString(property);
}
catch (ExtendedTypeSystemException) { throw; }
catch (Exception e)
{
throw NewException(
e,
"CatchFromBasePropertyToString",
"CatchFromBasePropertyToStringTI",
ExtendedTypeSystem.ExceptionRetrievingPropertyString,
property.Name);
}
}
internal AttributeCollection BasePropertyAttributes(PSProperty property)
{
try
{
return this.PropertyAttributes(property);
}
catch (ExtendedTypeSystemException) { throw; }
catch (Exception e)
{
throw NewException(
e,
"CatchFromBasePropertyAttributes",
"CatchFromBasePropertyAttributesTI",
ExtendedTypeSystem.ExceptionRetrievingPropertyAttributes,
property.Name);
}
}
#endregion property
#region method
internal object BaseMethodInvoke(PSMethod method, PSMethodInvocationConstraints invocationConstraints, params object[] arguments)
{
try
{
return this.MethodInvoke(method, invocationConstraints, arguments);
}
catch (TargetInvocationException ex)
{
Exception inner = ex.InnerException ?? ex;
throw new MethodInvocationException(
"CatchFromBaseAdapterMethodInvokeTI",
inner,
ExtendedTypeSystem.MethodInvocationException,
method.Name,
arguments.Length,
inner.Message);
}
catch (FlowControlException) { throw; }
catch (ScriptCallDepthException) { throw; }
catch (PipelineStoppedException) { throw; }
catch (MethodException) { throw; }
catch (Exception e)
{
if (method.baseObject is SteppablePipeline
&& (method.Name.Equals("Begin", StringComparison.OrdinalIgnoreCase) ||
method.Name.Equals("Process", StringComparison.OrdinalIgnoreCase) ||
method.Name.Equals("End", StringComparison.OrdinalIgnoreCase)))
{
throw;
}
throw new MethodInvocationException(
"CatchFromBaseAdapterMethodInvoke",
e,
ExtendedTypeSystem.MethodInvocationException,
method.Name,
arguments.Length,
e.Message);
}
}
internal Collection<string> BaseMethodDefinitions(PSMethod method)
{
try
{
return this.MethodDefinitions(method);
}
catch (ExtendedTypeSystemException) { throw; }
catch (Exception e)
{
throw NewException(
e,
"CatchFromBaseMethodDefinitions",
"CatchFromBaseMethodDefinitionsTI",
ExtendedTypeSystem.ExceptionRetrievingMethodDefinitions,
method.Name);
}
}
internal string BaseMethodToString(PSMethod method)
{
try
{
return this.MethodToString(method);
}
catch (ExtendedTypeSystemException) { throw; }
catch (Exception e)
{
throw NewException(
e,
"CatchFromBaseMethodToString",
"CatchFromBaseMethodToStringTI",
ExtendedTypeSystem.ExceptionRetrievingMethodString,
method.Name);
}
}
#endregion method
#region parameterized property
internal string BaseParameterizedPropertyType(PSParameterizedProperty property)
{
try
{
return this.ParameterizedPropertyType(property);
}
catch (ExtendedTypeSystemException) { throw; }
catch (Exception e)
{
throw NewException(
e,
"CatchFromBaseParameterizedPropertyType",
"CatchFromBaseParameterizedPropertyTypeTI",
ExtendedTypeSystem.ExceptionRetrievingParameterizedPropertytype,
property.Name);
}
}
internal bool BaseParameterizedPropertyIsSettable(PSParameterizedProperty property)
{
try
{
return this.ParameterizedPropertyIsSettable(property);
}
catch (ExtendedTypeSystemException) { throw; }
catch (Exception e)
{
throw NewException(
e,
"CatchFromBaseParameterizedPropertyIsSettable",
"CatchFromBaseParameterizedPropertyIsSettableTI",
ExtendedTypeSystem.ExceptionRetrievingParameterizedPropertyWriteState,
property.Name);
}
}
internal bool BaseParameterizedPropertyIsGettable(PSParameterizedProperty property)
{
try
{
return this.ParameterizedPropertyIsGettable(property);
}
catch (ExtendedTypeSystemException) { throw; }
catch (Exception e)
{
throw NewException(
e,
"CatchFromBaseParameterizedPropertyIsGettable",
"CatchFromBaseParameterizedPropertyIsGettableTI",
ExtendedTypeSystem.ExceptionRetrievingParameterizedPropertyReadState,
property.Name);
}
}
internal Collection<string> BaseParameterizedPropertyDefinitions(PSParameterizedProperty property)
{
try
{
return this.ParameterizedPropertyDefinitions(property);
}
catch (ExtendedTypeSystemException) { throw; }
catch (Exception e)
{
throw NewException(
e,
"CatchFromBaseParameterizedPropertyDefinitions",
"CatchFromBaseParameterizedPropertyDefinitionsTI",
ExtendedTypeSystem.ExceptionRetrievingParameterizedPropertyDefinitions,
property.Name);
}
}
internal object BaseParameterizedPropertyGet(PSParameterizedProperty property, params object[] arguments)
{
try
{
return this.ParameterizedPropertyGet(property, arguments);
}
catch (TargetInvocationException ex)
{
Exception inner = ex.InnerException ?? ex;
throw new GetValueInvocationException(
"CatchFromBaseAdapterParameterizedPropertyGetValueTI",
inner,
ExtendedTypeSystem.ExceptionWhenGetting,
property.Name,
inner.Message);
}
catch (GetValueException) { throw; }
catch (Exception e)
{
throw new GetValueInvocationException(
"CatchFromBaseParameterizedPropertyAdapterGetValue",
e,
ExtendedTypeSystem.ExceptionWhenGetting,
property.Name,
e.Message);
}
}
internal void BaseParameterizedPropertySet(PSParameterizedProperty property, object setValue, params object[] arguments)
{
try
{
this.ParameterizedPropertySet(property, setValue, arguments);
}
catch (TargetInvocationException ex)
{
Exception inner = ex.InnerException ?? ex;
throw new SetValueInvocationException(
"CatchFromBaseAdapterParameterizedPropertySetValueTI",
inner,
ExtendedTypeSystem.ExceptionWhenSetting,
property.Name,
inner.Message);
}
catch (SetValueException) { throw; }
catch (Exception e)
{
throw new SetValueInvocationException(
"CatchFromBaseAdapterParameterizedPropertySetValue",
e,
ExtendedTypeSystem.ExceptionWhenSetting,
property.Name,
e.Message);
}
}
internal string BaseParameterizedPropertyToString(PSParameterizedProperty property)
{
try
{
return this.ParameterizedPropertyToString(property);
}
catch (ExtendedTypeSystemException) { throw; }
catch (Exception e)
{
throw NewException(
e,
"CatchFromBaseParameterizedPropertyToString",
"CatchFromBaseParameterizedPropertyToStringTI",
ExtendedTypeSystem.ExceptionRetrievingParameterizedPropertyString,
property.Name);
}
}
#endregion parameterized property
#region Internal Helper Methods
private static Type GetArgumentType(object argument, bool isByRefParameter)
{
if (argument == null)
{
return typeof(LanguagePrimitives.Null);
}
if (isByRefParameter && argument is PSReference psref)
{
return GetArgumentType(PSObject.Base(psref.Value), isByRefParameter: false);
}
return argument.GetType();
}
internal static ConversionRank GetArgumentConversionRank(object argument, Type parameterType, bool isByRef, bool allowCastingToByRefLikeType)
{
Type fromType = null;
ConversionRank rank = ConversionRank.None;
if (allowCastingToByRefLikeType && parameterType.IsByRefLike)
{
// When resolving best method for use in binders, we can accept implicit/explicit casting conversions to
// a ByRef-like target type, because when generating IL from a call site with the binder, the IL includes
// the casting operation. However, we don't accept such conversions when it's for invoking the method via
// reflection, because reflection just doesn't support ByRef-like type.
fromType = GetArgumentType(PSObject.Base(argument), isByRefParameter: false);
if (fromType != typeof(LanguagePrimitives.Null))
{
LanguagePrimitives.FigureCastConversion(fromType, parameterType, ref rank);
}
return rank;
}
fromType = GetArgumentType(argument, isByRef);
rank = LanguagePrimitives.GetConversionRank(fromType, parameterType);
if (rank == ConversionRank.None)
{
fromType = GetArgumentType(PSObject.Base(argument), isByRef);
rank = LanguagePrimitives.GetConversionRank(fromType, parameterType);
}
return rank;
}
private static ParameterInformation[] ExpandParameters(int argCount, ParameterInformation[] parameters, Type elementType)
{
Diagnostics.Assert(parameters[parameters.Length - 1].isParamArray, "ExpandParameters shouldn't be called on non-param method");
ParameterInformation[] newParameters = new ParameterInformation[argCount];
Array.Copy(parameters, newParameters, parameters.Length - 1);
for (int i = parameters.Length - 1; i < argCount; ++i)
{
newParameters[i] = new ParameterInformation(elementType, false, null, false);
}
return newParameters;
}
/// <summary>
/// Compare the 2 methods, determining which method is better.
/// </summary>
/// <returns>1 if method1 is better, -1 if method2 is better, 0 otherwise.</returns>
private static int CompareOverloadCandidates(OverloadCandidate candidate1, OverloadCandidate candidate2, object[] arguments)
{
Diagnostics.Assert(candidate1.conversionRanks.Length == candidate2.conversionRanks.Length,
"should have same number of conversions regardless of the number of parameters - default arguments are not included here");
ParameterInformation[] params1 = candidate1.expandedParameters ?? candidate1.parameters;
ParameterInformation[] params2 = candidate2.expandedParameters ?? candidate2.parameters;
int betterCount = 0;
int multiplier = candidate1.conversionRanks.Length;
for (int i = 0; i < candidate1.conversionRanks.Length; ++i, --multiplier)
{
if (candidate1.conversionRanks[i] < candidate2.conversionRanks[i])
{
betterCount -= multiplier;
}
else if (candidate1.conversionRanks[i] > candidate2.conversionRanks[i])
{
betterCount += multiplier;
}
else if (candidate1.conversionRanks[i] == ConversionRank.UnrelatedArrays)
{
// If both are unrelated arrays, then use the element type conversions instead.
Type argElemType = EffectiveArgumentType(arguments[i]).GetElementType();
ConversionRank rank1 = LanguagePrimitives.GetConversionRank(argElemType, params1[i].parameterType.GetElementType());
ConversionRank rank2 = LanguagePrimitives.GetConversionRank(argElemType, params2[i].parameterType.GetElementType());
if (rank1 < rank2)
{
betterCount -= multiplier;
}
else if (rank1 > rank2)
{
betterCount += multiplier;
}
}
}
if (betterCount == 0)
{
multiplier = candidate1.conversionRanks.Length;
for (int i = 0; i < candidate1.conversionRanks.Length; ++i, multiplier = Math.Abs(multiplier) - 1)
{
// The following rather tricky logic tries to pick the best method in 2 very different cases -
// - Pick the most specific method when conversions aren't losing information
// - Pick the most general method when conversions will lose information.
// Consider:
// f(uint32), f(decimal), call with f([byte]$i)
// in this case, we want to call f(uint32) because it is more specific
// while not losing information
// f(byte), f(int16), call with f([int]$i)
// in this case, we want to call f(int16) because it is more general,
// we know we could lose information with either call, but we will lose
// less information calling f(int16).
ConversionRank rank1 = candidate1.conversionRanks[i];
ConversionRank rank2 = candidate2.conversionRanks[i];
if (rank1 < ConversionRank.NullToValue || rank2 < ConversionRank.NullToValue)
{
// The tie breaking rules here do not apply to conversions that are not
// numeric or inheritance related.
continue;
}
if ((rank1 >= ConversionRank.NumericImplicit) != (rank2 >= ConversionRank.NumericImplicit))
{
// Skip trying to break ties when argument conversions are not both implicit or both
// explicit. If we have that situation, there are multiple arguments and an
// ambiguity is probably the best choice.
continue;
}
// We will now compare the parameter types, ignoring the actual argument type. To choose
// the right method, we need to know if we want the "most specific" or the "most general".
// If we have implicit argument conversions, we'll want the most specific, so invert the multiplier.
if (rank1 >= ConversionRank.NumericImplicit)
{
multiplier = -multiplier;
}
// With a positive multiplier, we'll choose the "most general" type, and a negative
// multiplier will choose the "most specific".
rank1 = LanguagePrimitives.GetConversionRank(params1[i].parameterType, params2[i].parameterType);
rank2 = LanguagePrimitives.GetConversionRank(params2[i].parameterType, params1[i].parameterType);
if (rank1 < rank2)
{
betterCount += multiplier;
}
else if (rank1 > rank2)
{
betterCount -= multiplier;
}
}
}
if (betterCount == 0)
{
// Check if parameters are the same. If so, we have a few tiebreakering rules down below.
for (int i = 0; i < candidate1.conversionRanks.Length; ++i)
{
if (params1[i].parameterType != params2[i].parameterType)
{
return 0;
}
}
// Apply tie breaking rules, related to expanded parameters
if (candidate1.expandedParameters != null && candidate2.expandedParameters != null)
{
// Both are using expanded parameters. The one with more parameters is better
return (candidate1.parameters.Length > candidate2.parameters.Length) ? 1 : -1;
}
else if (candidate1.expandedParameters != null)
{
return -1;
}
else if (candidate2.expandedParameters != null)
{
return 1;
}
// Apply tie breaking rules, related to specificity of parameters
betterCount = CompareTypeSpecificity(candidate1, candidate2);
}
// The methods with fewer parameter wins
// Need to revisit this if we support named arguments
if (betterCount == 0)
{
if (candidate1.parameters.Length < candidate2.parameters.Length)
{
return 1;
}
else if (candidate1.parameters.Length > candidate2.parameters.Length)
{
return -1;
}
}
return betterCount;
}
private static OverloadCandidate FindBestCandidate(List<OverloadCandidate> candidates, object[] arguments)
{
Dbg.Assert(candidates != null, "Caller should verify candidates != null");
OverloadCandidate bestCandidateSoFar = null;
bool multipleBestCandidates = false;
for (int i = 0; i < candidates.Count; i++)
{
OverloadCandidate currentCandidate = candidates[i];
if (bestCandidateSoFar == null) // first iteration
{
bestCandidateSoFar = currentCandidate;
continue;
}
int comparisonResult = CompareOverloadCandidates(bestCandidateSoFar, currentCandidate, arguments);
if (comparisonResult == 0)
{
multipleBestCandidates = true;
}
else if (comparisonResult < 0)
{
bestCandidateSoFar = currentCandidate;
multipleBestCandidates = false;
}
}
Dbg.Assert(
!candidates.Any(otherCandidate => otherCandidate != bestCandidateSoFar && CompareOverloadCandidates(otherCandidate, bestCandidateSoFar, arguments) > 0),
"No other candidates are better than bestCandidateSoFar");
return multipleBestCandidates ? null : bestCandidateSoFar;
}
private static OverloadCandidate FindBestCandidate(List<OverloadCandidate> candidates, object[] arguments, PSMethodInvocationConstraints invocationConstraints)
{
List<OverloadCandidate> filteredCandidates = candidates.Where(candidate => IsInvocationConstraintSatisfied(candidate, invocationConstraints)).ToList();
if (filteredCandidates.Count > 0)
{
candidates = filteredCandidates;
}
OverloadCandidate bestCandidate = FindBestCandidate(candidates, arguments);
return bestCandidate;
}
private static int CompareTypeSpecificity(Type type1, Type type2)
{
if (type1.IsGenericParameter || type2.IsGenericParameter)
{
int result = 0;
if (type1.IsGenericParameter)
{
result -= 1;
}
if (type2.IsGenericParameter)
{
result += 1;
}
return result;
}
if (type1.IsArray)
{
Dbg.Assert(type2.IsArray, "Caller should verify that both overload candidates have the same parameter types");
Dbg.Assert(type1.GetArrayRank() == type2.GetArrayRank(), "Caller should verify that both overload candidates have the same parameter types");
return CompareTypeSpecificity(type1.GetElementType(), type2.GetElementType());
}
if (type1.IsGenericType)
{
Dbg.Assert(type2.IsGenericType, "Caller should verify that both overload candidates have the same parameter types");
Dbg.Assert(type1.GetGenericTypeDefinition() == type2.GetGenericTypeDefinition(), "Caller should verify that both overload candidates have the same parameter types");
return CompareTypeSpecificity(type1.GetGenericArguments(), type2.GetGenericArguments());
}
return 0;
}
private static int CompareTypeSpecificity(Type[] params1, Type[] params2)
{
Dbg.Assert(params1.Length == params2.Length, "Caller should verify that both overload candidates have the same number of parameters");
bool candidate1hasAtLeastOneMoreSpecificParameter = false;
bool candidate2hasAtLeastOneMoreSpecificParameter = false;
for (int i = 0; i < params1.Length; ++i)
{
int specificityComparison = CompareTypeSpecificity(params1[i], params2[i]);
if (specificityComparison > 0)
{
candidate1hasAtLeastOneMoreSpecificParameter = true;
}
else if (specificityComparison < 0)
{
candidate2hasAtLeastOneMoreSpecificParameter = true;
}
if (candidate1hasAtLeastOneMoreSpecificParameter && candidate2hasAtLeastOneMoreSpecificParameter)
{
break;
}
}
if (candidate1hasAtLeastOneMoreSpecificParameter && !candidate2hasAtLeastOneMoreSpecificParameter)
{
return 1;
}
else if (candidate2hasAtLeastOneMoreSpecificParameter && !candidate1hasAtLeastOneMoreSpecificParameter)
{
return -1;
}
else
{
return 0;
}
}
/// <summary>
/// Returns -1 if <paramref name="candidate1"/> is less specific than <paramref name="candidate2"/>
/// (1 otherwise, or 0 if both are equally specific or non-comparable)
/// </summary>
private static int CompareTypeSpecificity(OverloadCandidate candidate1, OverloadCandidate candidate2)
{
if (!(candidate1.method.isGeneric || candidate2.method.isGeneric))
{
return 0;
}
Type[] params1 = GetGenericMethodDefinitionIfPossible(candidate1.method.method).GetParameters().Select(static p => p.ParameterType).ToArray();
Type[] params2 = GetGenericMethodDefinitionIfPossible(candidate2.method.method).GetParameters().Select(static p => p.ParameterType).ToArray();
return CompareTypeSpecificity(params1, params2);
}
private static MethodBase GetGenericMethodDefinitionIfPossible(MethodBase method)
{
if (method.IsGenericMethod && !method.IsGenericMethodDefinition)
{
MethodInfo methodInfo = method as MethodInfo;
if (methodInfo != null)
{
return methodInfo.GetGenericMethodDefinition();
}
}
return method;
}
[DebuggerDisplay("OverloadCandidate: {method.methodDefinition}")]
private sealed class OverloadCandidate
{
internal MethodInformation method;
internal ParameterInformation[] parameters;
internal ParameterInformation[] expandedParameters;
internal ConversionRank[] conversionRanks;
internal OverloadCandidate(MethodInformation method, int argCount)
{
this.method = method;
this.parameters = method.parameters;
conversionRanks = new ConversionRank[argCount];
}
}
private static bool IsInvocationTargetConstraintSatisfied(MethodInformation method, PSMethodInvocationConstraints invocationConstraints)
{
Dbg.Assert(method != null, "Caller should verify method != null");
if (method.method == null)
{
return true; // do not apply methodTargetType constraint to non-.NET types (i.e. to COM or WMI types)
}
// An invocation constraint is only specified when there is an explicit cast on the target expression, so:
//
// [IFoo]$x.Bar()
//
// will have [IFoo] as the method target type, but
//
// $hash = @{}; $hash.Add(1,2)
//
// will have no method target type.
var methodDeclaringType = method.method.DeclaringType;
if (invocationConstraints == null || invocationConstraints.MethodTargetType == null)
{
// If no method target type is specified, we say the constraint is matched as long as the method is not an interface.
// This behavior matches V2 - our candidate sets never included methods with declaring type as an interface in V2.
return !methodDeclaringType.IsInterface;
}
var targetType = invocationConstraints.MethodTargetType;
if (targetType.IsInterface)
{
// If targetType is an interface, types must match exactly. This is how we can call method impls.
// We also allow the method declaring type to be in a base interface.
return methodDeclaringType == targetType || (methodDeclaringType.IsInterface && targetType.IsSubclassOf(methodDeclaringType));
}
if (methodDeclaringType.IsInterface)
{
// targetType is a class. We don't try comparing with targetType because we'll end up with
// an ambiguous set because what is effectively the same method may appear in our set multiple
// times (once with the declaring type as the interface, and once as the actual class type.)
return false;
}
// Dual-purpose of ([type]<expression>).method() syntax makes this code a little bit tricky to understand.
// First purpose of this syntax is cast.
// Second is a non-virtual super-class method call.
//
// Consider this code:
//
// ```
// class B {
// [string]foo() {return 'B.foo'}
// [string]foo($a) {return 'B.foo'}
// }
//
// class Q : B {
// [string]$Name
// Q([string]$name) {$this.name = $name}
// }
//
// ([Q]'t').foo()
// ```
//
// Here we are using [Q] just for the cast and we are expecting foo() to be resolved to a super-class method.
// So methodDeclaringType is [B] and targetType is [Q]
//
// Now consider another code
//
// ```
// ([object]"abc").ToString()
// ```
//
// Here we are using [object] to specify that we want a super-class implementation of ToString(), so it should
// return "System.String"
// Here methodDeclaringType is [string] and targetType is [object]
//
// Notice: in one case targetType is a subclass of methodDeclaringType,
// in another case it's the reverse.
// Both of them are valid.
//
// Array is a special case.
return targetType.IsAssignableFrom(methodDeclaringType)
|| methodDeclaringType.IsAssignableFrom(targetType)
|| (targetType.IsArray && methodDeclaringType == typeof(Array));
}
private static bool IsInvocationConstraintSatisfied(OverloadCandidate overloadCandidate, PSMethodInvocationConstraints invocationConstraints)
{
Dbg.Assert(overloadCandidate != null, "Caller should verify overloadCandidate != null");
if (invocationConstraints == null)
{
return true;
}
if (invocationConstraints.ParameterTypes != null)
{
int parameterIndex = 0;
foreach (Type parameterTypeConstraint in invocationConstraints.ParameterTypes)
{
if (parameterTypeConstraint != null)
{
if (parameterIndex >= overloadCandidate.parameters.Length)
{
return false;
}
Type parameterType = overloadCandidate.parameters[parameterIndex].parameterType;
if (parameterType != parameterTypeConstraint)
{
return false;
}
}
parameterIndex++;
}
}
return true;
}
/// <summary>
/// Return the best method out of overloaded methods.
/// The best has the smallest type distance between the method's parameters and the given arguments.
/// </summary>
/// <param name="methods">Different overloads for a method.</param>
/// <param name="invocationConstraints">Invocation constraints.</param>
/// <param name="allowCastingToByRefLikeType">True if we accept implicit/explicit casting conversion to a ByRef-like parameter type for method resolution.</param>
/// <param name="arguments">Arguments to check against the overloads.</param>
/// <param name="errorId">If no best method, the error id to use in the error message.</param>
/// <param name="errorMsg">If no best method, the error message (format string) to use in the error message.</param>
/// <param name="expandParamsOnBest">True if the best method's last parameter is a params method.</param>
/// <param name="callNonVirtually">True if best method should be called as non-virtual.</param>
internal static MethodInformation FindBestMethod(
MethodInformation[] methods,
PSMethodInvocationConstraints invocationConstraints,
bool allowCastingToByRefLikeType,
object[] arguments,
ref string errorId,
ref string errorMsg,
out bool expandParamsOnBest,
out bool callNonVirtually)
{
callNonVirtually = false;
var methodInfo = FindBestMethodImpl(methods, invocationConstraints, allowCastingToByRefLikeType, arguments, ref errorId, ref errorMsg, out expandParamsOnBest);
if (methodInfo == null)
{
return null;
}
// For PS classes we need to support base method call syntax:
//
// class BaseClass
// {
// [int] foo() { return 1}
// }
// class DerivedClass : BaseClass
// {
// [int] foo() { return 2 * ([BaseClass]$this).foo() }
// }
//
// If we have such information in invocationConstraints then we should call method on the baseClass.
if (invocationConstraints != null &&
invocationConstraints.MethodTargetType != null &&
methodInfo.method != null &&
methodInfo.method.DeclaringType != null)
{
Type targetType = methodInfo.method.DeclaringType;
if (targetType != invocationConstraints.MethodTargetType && targetType.IsSubclassOf(invocationConstraints.MethodTargetType))
{
var parameterTypes = methodInfo.method.GetParameters().Select(static parameter => parameter.ParameterType).ToArray();
var targetTypeMethod = invocationConstraints.MethodTargetType.GetMethod(methodInfo.method.Name, BindingFlags.IgnoreCase | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance, null, parameterTypes, null);
if (targetTypeMethod != null && (targetTypeMethod.IsPublic || targetTypeMethod.IsFamily || targetTypeMethod.IsFamilyOrAssembly))
{
methodInfo = new MethodInformation(targetTypeMethod, 0);
callNonVirtually = true;
}
}
}
return methodInfo;
}
private static MethodInformation FindBestMethodImpl(
MethodInformation[] methods,
PSMethodInvocationConstraints invocationConstraints,
bool allowCastingToByRefLikeType,
object[] arguments,
ref string errorId,
ref string errorMsg,
out bool expandParamsOnBest)
{
expandParamsOnBest = false;
// Small optimization so we don't calculate type distances when there is only one method
// We skip the optimization, if the method hasVarArgs, since in the case where arguments
// and parameters are of the same size, we want to know if the last argument should
// be turned into an array.
// We also skip the optimization if the number of arguments and parameters is different
// so we let the loop deal with possible optional parameters.
if ((methods.Length == 1) &&
(!methods[0].hasVarArgs) &&
(!methods[0].isGeneric) &&
(methods[0].method == null || !(methods[0].method.DeclaringType.IsGenericTypeDefinition)) &&
// generic methods need to be double checked in a loop below - generic methods can be rejected if type inference fails
(methods[0].parameters.Length == arguments.Length))
{
return methods[0];
}
Type[] argumentTypes = arguments.Select(EffectiveArgumentType).ToArray();
List<OverloadCandidate> candidates = new List<OverloadCandidate>();
for (int i = 0; i < methods.Length; i++)
{
MethodInformation method = methods[i];
if (method.method != null && method.method.DeclaringType.IsGenericTypeDefinition)
{
continue; // skip methods defined by an *open* generic type
}
if (method.isGeneric)
{
Type[] argumentTypesForTypeInference = new Type[argumentTypes.Length];
Array.Copy(argumentTypes, argumentTypesForTypeInference, argumentTypes.Length);
if (invocationConstraints != null && invocationConstraints.ParameterTypes != null)
{
int parameterIndex = 0;
foreach (Type typeConstraintFromCallSite in invocationConstraints.ParameterTypes)
{
if (typeConstraintFromCallSite != null)
{
argumentTypesForTypeInference[parameterIndex] = typeConstraintFromCallSite;
}
parameterIndex++;
}
}
method = TypeInference.Infer(method, argumentTypesForTypeInference);
if (method == null)
{
// Skip generic methods for which we cannot infer type arguments
continue;
}
}
if (!IsInvocationTargetConstraintSatisfied(method, invocationConstraints))
{
continue;
}
ParameterInformation[] parameters = method.parameters;
if (arguments.Length != parameters.Length)
{
// Skip methods w/ an incorrect # of arguments.
if (arguments.Length > parameters.Length)
{
// If too many args,it's only OK if the method is varargs.
if (!method.hasVarArgs)
{
continue;
}
}
else
{
// Too few args, OK if there are optionals, or varargs with the param array omitted
if (!method.hasOptional && (!method.hasVarArgs || (arguments.Length + 1) != parameters.Length))
{
continue;
}
if (method.hasOptional)
{
// Count optionals. This code is rarely hit, mainly when calling code in the
// assembly Microsoft.VisualBasic. If it were more frequent, the optional count
// should be stored in MethodInformation.
int optionals = 0;
for (int j = 0; j < parameters.Length; j++)
{
if (parameters[j].isOptional)
{
optionals += 1;
}
}
if (arguments.Length + optionals < parameters.Length)
{
// Even with optionals, there are too few.
continue;
}
}
}
}
OverloadCandidate candidate = new OverloadCandidate(method, arguments.Length);
for (int j = 0; candidate != null && j < parameters.Length; j++)
{
ParameterInformation parameter = parameters[j];
if (parameter.isOptional && arguments.Length <= j)
{
break; // All the other parameters are optional and it is ok not to have more arguments
}
else if (parameter.isParamArray)
{
Type elementType = parameter.parameterType.GetElementType();
if (parameters.Length == arguments.Length)
{
ConversionRank arrayConv = GetArgumentConversionRank(
arguments[j],
parameter.parameterType,
isByRef: false,
allowCastingToByRefLikeType: false);
ConversionRank elemConv = GetArgumentConversionRank(
arguments[j],
elementType,
isByRef: false,
allowCastingToByRefLikeType: false);
if (elemConv > arrayConv)
{
candidate.expandedParameters = ExpandParameters(arguments.Length, parameters, elementType);
candidate.conversionRanks[j] = elemConv;
}
else
{
candidate.conversionRanks[j] = arrayConv;
}
if (candidate.conversionRanks[j] == ConversionRank.None)
{
candidate = null;
}
}
else
{
// All remaining arguments will be added to one array to be passed as this params
// argument.
// Note that we go through here when the param array parameter has no argument.
for (int k = j; k < arguments.Length; k++)
{
candidate.conversionRanks[k] = GetArgumentConversionRank(
arguments[k],
elementType,
isByRef: false,
allowCastingToByRefLikeType: false);
if (candidate.conversionRanks[k] == ConversionRank.None)
{
// No longer a candidate
candidate = null;
break;
}
}
if (candidate != null)
{
candidate.expandedParameters = ExpandParameters(arguments.Length, parameters, elementType);
}
}
}
else
{
candidate.conversionRanks[j] = GetArgumentConversionRank(
arguments[j],
parameter.parameterType,
parameter.isByRef,
allowCastingToByRefLikeType);
if (candidate.conversionRanks[j] == ConversionRank.None)
{
// No longer a candidate
candidate = null;
}
}
}
if (candidate != null)
{
candidates.Add(candidate);
}
}
if (candidates.Count == 0)
{
if ((methods.Length > 0) && (methods.All(static m => m.method != null && m.method.DeclaringType.IsGenericTypeDefinition && m.method.IsStatic)))
{
errorId = "CannotInvokeStaticMethodOnUninstantiatedGenericType";
errorMsg = string.Format(
CultureInfo.InvariantCulture,
ExtendedTypeSystem.CannotInvokeStaticMethodOnUninstantiatedGenericType,
methods[0].method.DeclaringType.FullName);
return null;
}
else
{
errorId = "MethodCountCouldNotFindBest";
errorMsg = ExtendedTypeSystem.MethodArgumentCountException;
return null;
}
}
OverloadCandidate bestCandidate = candidates.Count == 1
? candidates[0]
: FindBestCandidate(candidates, arguments, invocationConstraints);
if (bestCandidate != null)
{
expandParamsOnBest = bestCandidate.expandedParameters != null;
return bestCandidate.method;
}
errorId = "MethodCountCouldNotFindBest";
errorMsg = ExtendedTypeSystem.MethodAmbiguousException;
return null;
}
internal static Type EffectiveArgumentType(object arg)
{
if (arg != null)
{
arg = PSObject.Base(arg);
object[] argAsArray = arg as object[];
if (argAsArray != null && argAsArray.Length > 0 && PSObject.Base(argAsArray[0]) != null)
{
Type firstType = PSObject.Base(argAsArray[0]).GetType();
bool allSameType = true;
for (int j = 1; j < argAsArray.Length; ++j)
{
if (argAsArray[j] == null || firstType != PSObject.Base(argAsArray[j]).GetType())
{
allSameType = false;
break;
}
}
if (allSameType)
{
return firstType.MakeArrayType();
}
}
return arg.GetType();
}
else
{
return typeof(LanguagePrimitives.Null);
}
}
internal static void SetReferences(object[] arguments, MethodInformation methodInformation, object[] originalArguments)
{
using (PSObject.MemberResolution.TraceScope("Checking for possible references."))
{
ParameterInformation[] parameters = methodInformation.parameters;
for (int i = 0; (i < originalArguments.Length) && (i < parameters.Length) && (i < arguments.Length); i++)
{
object originalArgument = originalArguments[i];
PSReference originalArgumentReference = originalArgument as PSReference;
// It still might be an PSObject wrapping an PSReference
if (originalArgumentReference == null)
{
if (!(originalArgument is PSObject originalArgumentObj))
{
continue;
}
originalArgumentReference = originalArgumentObj.BaseObject as PSReference;
if (originalArgumentReference == null)
{
continue;
}
}
ParameterInformation parameter = parameters[i];
if (!parameter.isByRef)
{
continue;
}
object argument = arguments[i];
PSObject.MemberResolution.WriteLine("Argument '{0}' was a reference so it will be set to \"{1}\".", i + 1, argument);
originalArgumentReference.Value = argument;
}
}
}
internal static MethodInformation GetBestMethodAndArguments(
string methodName,
MethodInformation[] methods,
object[] arguments,
out object[] newArguments)
{
return GetBestMethodAndArguments(methodName, methods, null, arguments, out newArguments);
}
internal static MethodInformation GetBestMethodAndArguments(
string methodName,
MethodInformation[] methods,
PSMethodInvocationConstraints invocationConstraints,
object[] arguments,
out object[] newArguments)
{
bool expandParamsOnBest;
bool callNonVirtually;
string errorId = null;
string errorMsg = null;
MethodInformation bestMethod = FindBestMethod(
methods,
invocationConstraints,
allowCastingToByRefLikeType: false,
arguments,
ref errorId,
ref errorMsg,
out expandParamsOnBest,
out callNonVirtually);
if (bestMethod == null)
{
throw new MethodException(errorId, null, errorMsg, methodName, arguments.Length);
}
newArguments = GetMethodArgumentsBase(methodName, bestMethod.parameters, arguments, expandParamsOnBest);
return bestMethod;
}
/// <summary>
/// Called in GetBestMethodAndArguments after a call to FindBestMethod to perform the
/// type conversion, copying(varArg) and optional value setting of the final arguments.
/// </summary>
internal static object[] GetMethodArgumentsBase(string methodName,
ParameterInformation[] parameters, object[] arguments,
bool expandParamsOnBest)
{
int parametersLength = parameters.Length;
if (parametersLength == 0)
{
return Array.Empty<object>();
}
object[] retValue = new object[parametersLength];
for (int i = 0; i < parametersLength - 1; i++)
{
ParameterInformation parameter = parameters[i];
SetNewArgument(methodName, arguments, retValue, parameter, i);
}
ParameterInformation lastParameter = parameters[parametersLength - 1];
if (!expandParamsOnBest)
{
SetNewArgument(methodName, arguments, retValue, lastParameter, parametersLength - 1);
return retValue;
}
// From this point on, we are dealing with VarArgs (Params)
// If we have no arguments left, we use an appropriate empty array for the last parameter
if (arguments.Length < parametersLength)
{
retValue[parametersLength - 1] = Array.CreateInstance(lastParameter.parameterType.GetElementType(), new int[] { 0 });
return retValue;
}
// We are going to put all the remaining arguments into an array
// and convert them to the propper type, if necessary to be the
// one argument for this last parameter
int remainingArgumentCount = arguments.Length - parametersLength + 1;
if (remainingArgumentCount == 1 && arguments[arguments.Length - 1] == null)
{
// Don't turn a single null argument into an array of 1 element, just pass null.
retValue[parametersLength - 1] = null;
}
else
{
object[] remainingArguments = new object[remainingArgumentCount];
Type paramsElementType = lastParameter.parameterType.GetElementType();
for (int j = 0; j < remainingArgumentCount; j++)
{
int argumentIndex = j + parametersLength - 1;
try
{
remainingArguments[j] = MethodArgumentConvertTo(arguments[argumentIndex], false, argumentIndex,
paramsElementType, CultureInfo.InvariantCulture);
}
catch (InvalidCastException e)
{
// NTRAID#Windows Out Of Band Releases-924162-2005/11/17-JonN
throw new MethodException(
"MethodArgumentConversionInvalidCastArgument",
e,
ExtendedTypeSystem.MethodArgumentConversionException,
argumentIndex, arguments[argumentIndex], methodName, paramsElementType, e.Message);
}
}
try
{
retValue[parametersLength - 1] = MethodArgumentConvertTo(remainingArguments,
lastParameter.isByRef, parametersLength - 1, lastParameter.parameterType,
CultureInfo.InvariantCulture);
}
catch (InvalidCastException e)
{
// NTRAID#Windows Out Of Band Releases-924162-2005/11/17-JonN
throw new MethodException(
"MethodArgumentConversionParamsConversion",
e,
ExtendedTypeSystem.MethodArgumentConversionException,
parametersLength - 1, remainingArguments, methodName, lastParameter.parameterType, e.Message);
}
}
return retValue;
}
/// <summary>
/// Auxiliary method in MethodInvoke to set newArguments[index] with the propper value.
/// </summary>
/// <param name="methodName">Used for the MethodException that might be thrown.</param>
/// <param name="arguments">The complete array of arguments.</param>
/// <param name="newArguments">The complete array of new arguments.</param>
/// <param name="parameter">The parameter to use.</param>
/// <param name="index">The index in newArguments to set.</param>
internal static void SetNewArgument(string methodName, object[] arguments,
object[] newArguments, ParameterInformation parameter, int index)
{
if (arguments.Length > index)
{
try
{
newArguments[index] = MethodArgumentConvertTo(arguments[index], parameter.isByRef, index,
parameter.parameterType, CultureInfo.InvariantCulture);
}
catch (InvalidCastException e)
{
// NTRAID#Windows Out Of Band Releases-924162-2005/11/17-JonN
throw new MethodException(
"MethodArgumentConversionInvalidCastArgument",
e,
ExtendedTypeSystem.MethodArgumentConversionException,
index, arguments[index], methodName, parameter.parameterType, e.Message);
}
}
else
{
Diagnostics.Assert(parameter.isOptional, "FindBestMethod would not return this method if there is no corresponding argument for a non optional parameter");
newArguments[index] = parameter.defaultValue;
}
}
internal static object MethodArgumentConvertTo(object valueToConvert,
bool isParameterByRef, int parameterIndex, Type resultType,
IFormatProvider formatProvider)
{
using (PSObject.MemberResolution.TraceScope("Method argument conversion."))
{
if (resultType == null)
{
throw PSTraceSource.NewArgumentNullException(nameof(resultType));
}
bool isArgumentByRef;
valueToConvert = UnReference(valueToConvert, out isArgumentByRef);
if (isParameterByRef && !isArgumentByRef)
{
throw new MethodException("NonRefArgumentToRefParameterMsg", null,
ExtendedTypeSystem.NonRefArgumentToRefParameter, parameterIndex + 1, typeof(PSReference).FullName, "[ref]");
}
if (isArgumentByRef && !isParameterByRef)
{
throw new MethodException("RefArgumentToNonRefParameterMsg", null,
ExtendedTypeSystem.RefArgumentToNonRefParameter, parameterIndex + 1, typeof(PSReference).FullName, "[ref]");
}
return PropertySetAndMethodArgumentConvertTo(valueToConvert, resultType, formatProvider);
}
}
internal static object UnReference(object obj, out bool isArgumentByRef)
{
isArgumentByRef = false;
PSReference reference = obj as PSReference;
if (reference != null)
{
PSObject.MemberResolution.WriteLine("Parameter was a reference.");
isArgumentByRef = true;
return reference.Value;
}
PSObject mshObj = obj as PSObject;
if (mshObj != null)
{
reference = mshObj.BaseObject as PSReference;
}
if (reference != null)
{
PSObject.MemberResolution.WriteLine("Parameter was an PSObject containing a reference.");
isArgumentByRef = true;
return reference.Value;
}
return obj;
}
internal static object PropertySetAndMethodArgumentConvertTo(object valueToConvert,
Type resultType, IFormatProvider formatProvider)
{
using (PSObject.MemberResolution.TraceScope("Converting parameter \"{0}\" to \"{1}\".", valueToConvert, resultType))
{
if (resultType == null)
{
throw PSTraceSource.NewArgumentNullException(nameof(resultType));
}
PSObject mshObj = valueToConvert as PSObject;
if (mshObj != null)
{
if (resultType == typeof(object))
{
PSObject.MemberResolution.WriteLine("Parameter was an PSObject and will be converted to System.Object.");
// we use PSObject.Base so we don't return
// PSCustomObject
return PSObject.Base(mshObj);
}
}
return LanguagePrimitives.ConvertTo(valueToConvert, resultType, formatProvider);
}
}
internal static void DoBoxingIfNecessary(ILGenerator generator, Type type)
{
if (type.IsByRef)
{
// We can't use a byref like we would use System.Object (the CLR will
// crash if we attempt to do so.) There isn't much anyone could do
// with a byref in PowerShell anyway, so just load the object and
// return that instead.
type = type.GetElementType();
if (type.IsPrimitive)
{
if (type == typeof(byte)) { generator.Emit(OpCodes.Ldind_U1); }
else if (type == typeof(ushort)) { generator.Emit(OpCodes.Ldind_U2); }
else if (type == typeof(uint)) { generator.Emit(OpCodes.Ldind_U4); }
else if (type == typeof(sbyte)) { generator.Emit(OpCodes.Ldind_I8); }
else if (type == typeof(short)) { generator.Emit(OpCodes.Ldind_I2); }
else if (type == typeof(int)) { generator.Emit(OpCodes.Ldind_I4); }
else if (type == typeof(long)) { generator.Emit(OpCodes.Ldind_I8); }
else if (type == typeof(float)) { generator.Emit(OpCodes.Ldind_R4); }
else if (type == typeof(double)) { generator.Emit(OpCodes.Ldind_R8); }
}
else if (type.IsValueType)
{
generator.Emit(OpCodes.Ldobj, type);
}
else
{
generator.Emit(OpCodes.Ldind_Ref);
}
}
else if (type.IsPointer)
{
// Pointers are similar to a byref. Here we mimic what C# would do
// when assigning a pointer to an object. This might not be useful
// to PowerShell script, but if we did nothing, the CLR would crash
// our process.
MethodInfo boxMethod = typeof(Pointer).GetMethod("Box");
MethodInfo typeFromHandle = typeof(Type).GetMethod("GetTypeFromHandle");
generator.Emit(OpCodes.Ldtoken, type);
generator.Emit(OpCodes.Call, typeFromHandle);
generator.Emit(OpCodes.Call, boxMethod);
}
if (type.IsValueType)
{
generator.Emit(OpCodes.Box, type);
}
}
#endregion
#endregion base
}
/// <summary>
/// The abstract cache entry type.
/// All specific cache entry types should derive from it.
/// </summary>
internal abstract class CacheEntry
{
/// <summary>
/// Gets the boolean value to indicate if the member is hidden.
/// </summary>
/// <remarks>
/// Currently, we only check the 'HiddenAttribute' declared for properties and methods,
/// because it can be done for them through the 'hidden' keyword in PowerShell Class.
///
/// We can't currently write a parameterized property in a PowerShell class so it's not too important
/// to check for the 'HiddenAttribute' for parameterized properties. But if someone added the attribute
/// to their C#, it'd be good to set this property correctly.
/// </remarks>
internal virtual bool IsHidden => false;
}
/// <summary>
/// Ordered and case insensitive hashtable.
/// </summary>
internal class CacheTable
{
/// <summary>
/// An object collection is used to help make populating method cache table more efficient
/// <see cref="DotNetAdapter.PopulateMethodReflectionTable(Type, CacheTable, BindingFlags)"/>.
/// </summary>
internal Collection<object> memberCollection;
private readonly Dictionary<string, int> _indexes;
internal CacheTable()
{
memberCollection = new Collection<object>();
_indexes = new Dictionary<string, int>(StringComparer.OrdinalIgnoreCase);
}
internal void Add(string name, object member)
{
_indexes[name] = memberCollection.Count;
memberCollection.Add(member);
}
internal object this[string name]
{
get
{
int indexObj;
if (!_indexes.TryGetValue(name, out indexObj))
{
return null;
}
return memberCollection[indexObj];
}
}
/// <summary>
/// Get the first non-hidden member that satisfies the predicate.
/// </summary>
/// <remarks>
/// Hidden members are not returned for any fuzzy searches (searching by 'match' or enumerating a collection).
/// A hidden member is returned only if the member name is explicitly looked for.
/// </remarks>
internal object GetFirstOrDefault(MemberNamePredicate predicate)
{
foreach (var entry in _indexes)
{
if (predicate(entry.Key))
{
object member = memberCollection[entry.Value];
if (member is CacheEntry cacheEntry && cacheEntry.IsHidden)
{
continue;
}
return member;
}
}
return null;
}
}
/// <summary>
/// Stores method related information.
/// This structure should be used whenever a new type is adapted.
/// For example, ManagementObjectAdapter uses this structure to store
/// WMI method information.
/// </summary>
[DebuggerDisplay("MethodInformation: {methodDefinition}")]
internal class MethodInformation
{
internal MethodBase method;
private string _cachedMethodDefinition;
internal string methodDefinition
{
get
{
if (_cachedMethodDefinition == null)
{
var name = method is ConstructorInfo ? "new" : method.Name;
var methodDefn = DotNetAdapter.GetMethodInfoOverloadDefinition(name, method, method.GetParameters().Length - parameters.Length);
Interlocked.CompareExchange(ref _cachedMethodDefinition, methodDefn, null);
}
return _cachedMethodDefinition;
}
}
internal ParameterInformation[] parameters;
internal bool hasVarArgs;
internal bool hasOptional;
internal bool isGeneric;
private bool _useReflection;
private delegate object MethodInvoker(object target, object[] arguments);
private MethodInvoker _methodInvoker;
/// <summary>
/// This constructor supports .net methods.
/// </summary>
internal MethodInformation(MethodBase method, int parametersToIgnore)
{
this.method = method;
this.isGeneric = method.IsGenericMethod;
ParameterInfo[] methodParameters = method.GetParameters();
int parametersLength = methodParameters.Length - parametersToIgnore;
this.parameters = new ParameterInformation[parametersLength];
for (int i = 0; i < parametersLength; i++)
{
this.parameters[i] = new ParameterInformation(methodParameters[i]);
if (methodParameters[i].IsOptional)
{
hasOptional = true;
}
}
this.hasVarArgs = false;
if (parametersLength > 0)
{
ParameterInfo lastParameter = methodParameters[parametersLength - 1];
// Optional and params together are forbidden in VB and so we only check for params
// if !hasOptional
if (!hasOptional && lastParameter.ParameterType.IsArray)
{
// The extension method 'CustomAttributeExtensions.GetCustomAttributes(ParameterInfo, Type, Boolean)' has inconsistent
// behavior on its return value in both FullCLR and CoreCLR. According to MSDN, if the attribute cannot be found, it
// should return an empty collection. However, it returns null in some rare cases [when the parameter isn't backed by
// actual metadata].
// This inconsistent behavior affects OneCore powershell because we are using the extension method here when compiling
// against CoreCLR. So we need to add a null check until this is fixed in CLR.
var paramArrayAttrs = lastParameter.GetCustomAttributes(typeof(ParamArrayAttribute), false);
if (paramArrayAttrs != null && paramArrayAttrs.Length > 0)
{
this.hasVarArgs = true;
this.parameters[parametersLength - 1].isParamArray = true;
}
}
}
}
internal MethodInformation(bool hasvarargs, bool hasoptional, ParameterInformation[] arguments)
{
hasVarArgs = hasvarargs;
hasOptional = hasoptional;
parameters = arguments;
}
internal object Invoke(object target, object[] arguments)
{
// There may be parameters of ByRef-like types, but they will be taken care of
// when we resolve overloads to find the best methods -- proper exception will
// be thrown when converting arguments to the ByRef-like parameter types.
//
// So when reaching here, we only care about (1) if the method return type is
// BeRef-like; (2) if it's a constrcutor of a ByRef-like type.
if (method is ConstructorInfo ctor)
{
if (ctor.DeclaringType.IsByRefLike)
{
throw new MethodException(
nameof(ExtendedTypeSystem.CannotInstantiateBoxedByRefLikeType),
innerException: null,
ExtendedTypeSystem.CannotInstantiateBoxedByRefLikeType,
ctor.DeclaringType);
}
return ctor.Invoke(arguments);
}
var methodInfo = (MethodInfo)method;
if (methodInfo.ReturnType.IsByRefLike)
{
throw new MethodException(
nameof(ExtendedTypeSystem.CannotCallMethodWithByRefLikeReturnType),
innerException: null,
ExtendedTypeSystem.CannotCallMethodWithByRefLikeReturnType,
methodInfo.Name,
methodInfo.ReturnType);
}
if (target is PSObject)
{
if (!method.DeclaringType.IsAssignableFrom(target.GetType()))
{
target = PSObject.Base(target);
}
}
if (!_useReflection)
{
if (_methodInvoker == null)
{
_methodInvoker = GetMethodInvoker(methodInfo);
}
if (_methodInvoker != null)
{
return _methodInvoker(target, arguments);
}
}
return method.Invoke(target, arguments);
}
private static readonly OpCode[] s_ldc = new OpCode[] {
OpCodes.Ldc_I4_0, OpCodes.Ldc_I4_1, OpCodes.Ldc_I4_2, OpCodes.Ldc_I4_3, OpCodes.Ldc_I4_4,
OpCodes.Ldc_I4_5, OpCodes.Ldc_I4_6, OpCodes.Ldc_I4_7, OpCodes.Ldc_I4_8
};
private static void EmitLdc(ILGenerator emitter, int c)
{
if (c < s_ldc.Length)
{
emitter.Emit(s_ldc[c]);
}
else
{
emitter.Emit(OpCodes.Ldc_I4, c);
}
}
private static bool CompareMethodParameters(MethodBase method1, MethodBase method2)
{
ParameterInfo[] params1 = method1.GetParameters();
ParameterInfo[] params2 = method2.GetParameters();
if (params1.Length != params2.Length)
{
return false;
}
for (int i = 0; i < params1.Length; ++i)
{
if (params1[i].ParameterType != params2[i].ParameterType)
{
return false;
}
}
return true;
}
private static Type FindInterfaceForMethod(MethodInfo method, out MethodInfo methodToCall)
{
methodToCall = null;
Type valuetype = method.DeclaringType;
Diagnostics.Assert(valuetype.IsValueType, "This code only works with valuetypes");
Type[] interfaces = valuetype.GetInterfaces();
for (int i = 0; i < interfaces.Length; i++)
{
Type type = interfaces[i];
MethodInfo methodInfo = type.GetMethod(method.Name, BindingFlags.Instance);
if (methodInfo != null && CompareMethodParameters(methodInfo, method))
{
methodToCall = methodInfo;
return type;
}
}
// TODO: method impls (not especially important because I don't think they can be called in script.
return null;
}
[SuppressMessage("NullPtr", "#pw26500", Justification = "This is a false positive. Original warning was on the deference of 'locals' on line 1863: emitter.Emit(OpCodes.Ldloca, locals[cLocal])")]
private MethodInvoker GetMethodInvoker(MethodInfo method)
{
Type type;
bool valueTypeInstanceMethod = false;
bool anyOutOrRefParameters = false;
bool mustStoreRetVal = false;
MethodInfo methodToCall = method;
int cLocal = 0;
int c;
DynamicMethod dynamicMethod = new DynamicMethod(method.Name, typeof(object),
new Type[] { typeof(object), typeof(object[]) }, typeof(Adapter).Module, true);
ILGenerator emitter = dynamicMethod.GetILGenerator();
ParameterInfo[] parameters = method.GetParameters();
int localCount = 0;
if (!method.IsStatic && method.DeclaringType.IsValueType)
{
if (!method.IsVirtual)
{
// We need a local to unbox the instance argument into
valueTypeInstanceMethod = true;
localCount += 1;
}
}
for (int i = 0; i < parameters.Length; i++)
{
// We need locals for any out/ref parameters. We could get
// away with avoiding a local if the parameter was 'object',
// but that optimization is not implemented.
if (parameters[i].IsOut || parameters[i].ParameterType.IsByRef)
{
anyOutOrRefParameters = true;
localCount += 1;
}
}
LocalBuilder[] locals = null;
Type returnType = method.ReturnType;
if (localCount > 0)
{
if (anyOutOrRefParameters && returnType != typeof(void))
{
// If there are any ref/out parameters, we set them after the
// call. We can't leave the return value on the stack (it fails
// verification), so we must create a local to hold the return value.
localCount += 1;
mustStoreRetVal = true;
}
locals = new LocalBuilder[localCount];
cLocal = 0;
if (valueTypeInstanceMethod)
{
// Unbox the instance parameter into a local.
type = method.DeclaringType;
locals[cLocal] = emitter.DeclareLocal(type);
emitter.Emit(OpCodes.Ldarg_0);
emitter.Emit(OpCodes.Unbox_Any, type);
emitter.Emit(OpCodes.Stloc, locals[cLocal]);
cLocal += 1;
}
// Copy all arguments that are being passed as out/ref parameters into
// locals.
for (c = 0; c < parameters.Length; ++c)
{
type = parameters[c].ParameterType;
if (parameters[c].IsOut || type.IsByRef)
{
if (type.IsByRef)
{
type = type.GetElementType();
}
locals[cLocal] = emitter.DeclareLocal(type);
emitter.Emit(OpCodes.Ldarg_1);
EmitLdc(emitter, c);
emitter.Emit(OpCodes.Ldelem_Ref);
if (type.IsValueType)
{
emitter.Emit(OpCodes.Unbox_Any, type);
}
else if (type != typeof(object))
{
emitter.Emit(OpCodes.Castclass, type);
}
emitter.Emit(OpCodes.Stloc, locals[cLocal]);
cLocal += 1;
}
}
if (mustStoreRetVal)
{
locals[cLocal] = emitter.DeclareLocal(returnType);
}
}
cLocal = 0;
if (!method.IsStatic)
{
// Load the "instance" argument.
if (method.DeclaringType.IsValueType)
{
if (method.IsVirtual)
{
type = FindInterfaceForMethod(method, out methodToCall);
if (type == null)
{
_useReflection = true;
return null;
}
emitter.Emit(OpCodes.Ldarg_0);
emitter.Emit(OpCodes.Castclass, type);
}
else
{
emitter.Emit(OpCodes.Ldloca, locals[cLocal]);
cLocal += 1;
}
}
else
{
emitter.Emit(OpCodes.Ldarg_0);
}
}
for (c = 0; c < parameters.Length; c++)
{
type = parameters[c].ParameterType;
if (type.IsByRef)
{
emitter.Emit(OpCodes.Ldloca, locals[cLocal]);
cLocal += 1;
}
else if (parameters[c].IsOut)
{
emitter.Emit(OpCodes.Ldloc, locals[cLocal]);
cLocal += 1;
}
else
{
emitter.Emit(OpCodes.Ldarg_1);
EmitLdc(emitter, c);
emitter.Emit(OpCodes.Ldelem_Ref);
// Unbox value types since our args array is full of objects
if (type.IsValueType)
{
emitter.Emit(OpCodes.Unbox_Any, type);
}
// For reference types, cast from object
else if (type != typeof(object))
{
emitter.Emit(OpCodes.Castclass, type);
}
}
}
emitter.Emit(method.IsStatic ? OpCodes.Call : OpCodes.Callvirt, methodToCall);
if (mustStoreRetVal)
{
emitter.Emit(OpCodes.Stloc, locals[locals.Length - 1]);
}
// Handle the ref/out arguments by copying the locals
// back into the original args array
if (anyOutOrRefParameters)
{
cLocal = valueTypeInstanceMethod ? 1 : 0;
for (c = 0; c < parameters.Length; c++)
{
type = parameters[c].ParameterType;
if (!parameters[c].IsOut && !type.IsByRef)
{
continue;
}
if (type.IsByRef)
{
type = type.GetElementType();
}
emitter.Emit(OpCodes.Ldarg_1);
EmitLdc(emitter, c);
emitter.Emit(OpCodes.Ldloc, locals[cLocal]);
// Again, box value types since the args array holds objects
if (type.IsValueType)
{
emitter.Emit(OpCodes.Box, type);
}
emitter.Emit(OpCodes.Stelem_Ref);
cLocal += 1;
}
}
// We must return something, so return null for void methods
if (returnType == typeof(void))
{
emitter.Emit(OpCodes.Ldnull);
}
else
{
if (mustStoreRetVal)
{
// Return value was stored in a local, load it before return
emitter.Emit(OpCodes.Ldloc, locals[locals.Length - 1]);
}
Adapter.DoBoxingIfNecessary(emitter, returnType);
}
emitter.Emit(OpCodes.Ret);
return (MethodInvoker)dynamicMethod.CreateDelegate(typeof(MethodInvoker));
}
}
/// <summary>
/// Stores parameter related information.
/// This structure should be used whenever a new type is adapted.
/// For example, ManagementObjectAdapter uses this structure to store
/// method parameter information.
/// </summary>
internal class ParameterInformation
{
internal Type parameterType;
internal object defaultValue;
internal bool isOptional;
internal bool isByRef;
internal bool isParamArray;
internal ParameterInformation(System.Reflection.ParameterInfo parameter)
{
this.isOptional = parameter.IsOptional;
this.defaultValue = parameter.DefaultValue;
this.parameterType = parameter.ParameterType;
if (this.parameterType.IsByRef)
{
this.isByRef = true;
this.parameterType = this.parameterType.GetElementType();
}
else
{
this.isByRef = false;
}
}
internal ParameterInformation(Type parameterType, bool isOptional, object defaultValue, bool isByRef)
{
this.parameterType = parameterType;
this.isOptional = isOptional;
this.defaultValue = defaultValue;
this.isByRef = isByRef;
}
}
/// <summary>
/// This is the adapter used for all objects that don't match the appropriate types for other adapters.
/// It uses reflection to retrieve property information.
/// </summary>
internal class DotNetAdapter : Adapter
{
#region auxiliary methods and classes
private const BindingFlags instanceBindingFlags = (BindingFlags.FlattenHierarchy | BindingFlags.Public |
BindingFlags.IgnoreCase | BindingFlags.Instance);
private const BindingFlags staticBindingFlags = (BindingFlags.FlattenHierarchy | BindingFlags.Public |
BindingFlags.IgnoreCase | BindingFlags.Static);
private readonly bool _isStatic;
internal DotNetAdapter() { }
internal DotNetAdapter(bool isStatic)
{
_isStatic = isStatic;
}
// This static is thread safe based on the lock in GetInstancePropertyReflectionTable
/// <summary>
/// CLR reflection property cache for instance properties.
/// </summary>
private static readonly Dictionary<Type, CacheTable> s_instancePropertyCacheTable = new Dictionary<Type, CacheTable>();
// This static is thread safe based on the lock in GetStaticPropertyReflectionTable
/// <summary>
/// CLR reflection property cache for static properties.
/// </summary>
private static readonly Dictionary<Type, CacheTable> s_staticPropertyCacheTable = new Dictionary<Type, CacheTable>();
// This static is thread safe based on the lock in GetInstanceMethodReflectionTable
/// <summary>
/// CLR reflection method cache for instance methods.
/// </summary>
private static readonly Dictionary<Type, CacheTable> s_instanceMethodCacheTable = new Dictionary<Type, CacheTable>();
// This static is thread safe based on the lock in GetStaticMethodReflectionTable
/// <summary>
/// CLR reflection method cache for static methods.
/// </summary>
private static readonly Dictionary<Type, CacheTable> s_staticMethodCacheTable = new Dictionary<Type, CacheTable>();
// This static is thread safe based on the lock in GetInstanceMethodReflectionTable
/// <summary>
/// CLR reflection method cache for instance events.
/// </summary>
private static readonly Dictionary<Type, Dictionary<string, EventCacheEntry>> s_instanceEventCacheTable
= new Dictionary<Type, Dictionary<string, EventCacheEntry>>();
// This static is thread safe based on the lock in GetStaticMethodReflectionTable
/// <summary>
/// CLR reflection method cache for static events.
/// </summary>
private static readonly Dictionary<Type, Dictionary<string, EventCacheEntry>> s_staticEventCacheTable
= new Dictionary<Type, Dictionary<string, EventCacheEntry>>();
internal class MethodCacheEntry : CacheEntry
{
internal readonly MethodInformation[] methodInformationStructures;
/// <summary>
/// Cache delegate to the ctor of PSMethod&lt;&gt; with a template parameter derived from the methodInformationStructures.
/// </summary>
internal Func<string, DotNetAdapter, object, DotNetAdapter.MethodCacheEntry, bool, bool, PSMethod> PSMethodCtor;
internal MethodCacheEntry(MethodBase[] methods)
{
methodInformationStructures = DotNetAdapter.GetMethodInformationArray(methods);
}
internal MethodInformation this[int i]
{
get
{
return methodInformationStructures[i];
}
}
private bool? _isHidden;
internal override bool IsHidden
{
get
{
if (_isHidden == null)
{
bool hasHiddenAttribute = false;
foreach (var method in methodInformationStructures)
{
if (method.method.GetCustomAttributes(typeof(HiddenAttribute), inherit: false).Length != 0)
{
hasHiddenAttribute = true;
break;
}
}
_isHidden = hasHiddenAttribute;
}
return _isHidden.Value;
}
}
}
internal class EventCacheEntry : CacheEntry
{
internal EventInfo[] events;
internal EventCacheEntry(EventInfo[] events)
{
this.events = events;
}
}
internal class ParameterizedPropertyCacheEntry : CacheEntry
{
internal MethodInformation[] getterInformation;
internal MethodInformation[] setterInformation;
internal string propertyName;
internal bool readOnly;
internal bool writeOnly;
internal Type propertyType;
// propertyDefinition is used as a string representation of the property
internal string[] propertyDefinition;
internal ParameterizedPropertyCacheEntry(List<PropertyInfo> properties)
{
PropertyInfo firstProperty = properties[0];
this.propertyName = firstProperty.Name;
this.propertyType = firstProperty.PropertyType;
var getterList = new List<MethodInfo>();
var setterList = new List<MethodInfo>();
var definitionArray = new List<string>();
for (int i = 0; i < properties.Count; i++)
{
PropertyInfo property = properties[i];
// Properties can have different return types. If they do
// we pretend it is System.Object
if (property.PropertyType != this.propertyType)
{
this.propertyType = typeof(object);
}
// Get the public getter
MethodInfo propertyGetter = property.GetGetMethod();
StringBuilder definition = new StringBuilder();
StringBuilder extraDefinition = new StringBuilder();
if (propertyGetter != null)
{
extraDefinition.Append("get;");
definition.Append(DotNetAdapter.GetMethodInfoOverloadDefinition(this.propertyName, propertyGetter, 0));
getterList.Add(propertyGetter);
}
// Get the public setter
MethodInfo propertySetter = property.GetSetMethod();
if (propertySetter != null)
{
extraDefinition.Append("set;");
if (definition.Length == 0)
{
definition.Append(DotNetAdapter.GetMethodInfoOverloadDefinition(this.propertyName, propertySetter, 1));
}
setterList.Add(propertySetter);
}
definition.Append(" {");
definition.Append(extraDefinition);
definition.Append('}');
definitionArray.Add(definition.ToString());
}
propertyDefinition = definitionArray.ToArray();
this.writeOnly = getterList.Count == 0;
this.readOnly = setterList.Count == 0;
this.getterInformation = new MethodInformation[getterList.Count];
for (int i = 0; i < getterList.Count; i++)
{
this.getterInformation[i] = new MethodInformation(getterList[i], 0);
}
this.setterInformation = new MethodInformation[setterList.Count];
for (int i = 0; i < setterList.Count; i++)
{
this.setterInformation[i] = new MethodInformation(setterList[i], 1);
}
}
}
internal class PropertyCacheEntry : CacheEntry
{
internal delegate object GetterDelegate(object instance);
internal delegate void SetterDelegate(object instance, object setValue);
internal PropertyCacheEntry(PropertyInfo property)
{
this.member = property;
this.propertyType = property.PropertyType;
// Generating code for fields/properties in ValueTypes is complex and will probably
// require different delegates
// The same is true for generics, COM Types.
Type declaringType = property.DeclaringType;
if (declaringType.IsValueType ||
propertyType.IsGenericType ||
declaringType.IsGenericType ||
declaringType.IsCOMObject ||
propertyType.IsCOMObject)
{
this.readOnly = property.GetSetMethod() == null;
this.writeOnly = property.GetGetMethod() == null;
this.useReflection = true;
return;
}
// Get the public or protected getter
MethodInfo propertyGetter = property.GetGetMethod(true);
if (propertyGetter != null && (propertyGetter.IsPublic || propertyGetter.IsFamily))
{
this.isStatic = propertyGetter.IsStatic;
// Delegate is initialized later to avoid jit if it's not called
}
else
{
this.writeOnly = true;
}
// Get the public or protected setter
MethodInfo propertySetter = property.GetSetMethod(true);
if (propertySetter != null && (propertySetter.IsPublic || propertySetter.IsFamily))
{
this.isStatic = propertySetter.IsStatic;
}
else
{
this.readOnly = true;
}
}
internal PropertyCacheEntry(FieldInfo field)
{
this.member = field;
this.isStatic = field.IsStatic;
this.propertyType = field.FieldType;
// const fields have no setter and we are getting them with GetValue instead of
// using generated code. Init fields are only settable during initialization
// then cannot be set afterwards..
if (field.IsLiteral || field.IsInitOnly)
{
this.readOnly = true;
}
}
private void InitGetter()
{
if (writeOnly || useReflection)
{
return;
}
var parameter = Expression.Parameter(typeof(object));
Expression instance = null;
var field = member as FieldInfo;
if (field != null)
{
var declaringType = field.DeclaringType;
if (!field.IsStatic)
{
if (declaringType.IsValueType)
{
// I'm not sure we can get here with a Nullable, but if so,
// we must use the Value property, see PSGetMemberBinder.GetTargetValue.
instance = Nullable.GetUnderlyingType(declaringType) != null
? (Expression)Expression.Property(parameter, "Value")
: Expression.Unbox(parameter, declaringType);
}
else
{
instance = parameter.Cast(declaringType);
}
}
Expression getterExpr;
if (declaringType.IsGenericTypeDefinition)
{
Expression innerException = Expression.New(CachedReflectionInfo.GetValueException_ctor,
Expression.Constant("PropertyGetException"),
Expression.Constant(null, typeof(Exception)),
Expression.Constant(ParserStrings.PropertyInGenericType),
Expression.NewArrayInit(typeof(object), Expression.Constant(field.Name)));
getterExpr = Compiler.ThrowRuntimeErrorWithInnerException("PropertyGetException",
Expression.Constant(ParserStrings.PropertyInGenericType),
innerException, typeof(object), Expression.Constant(field.Name));
}
else
{
getterExpr = Expression.Field(instance, field).Cast(typeof(object));
}
_getterDelegate = Expression.Lambda<GetterDelegate>(getterExpr, parameter).Compile();
return;
}
var property = (PropertyInfo)member;
var propertyGetter = property.GetGetMethod(true);
instance = this.isStatic ? null : parameter.Cast(propertyGetter.DeclaringType);
_getterDelegate = Expression.Lambda<GetterDelegate>(
Expression.Property(instance, property).Cast(typeof(object)), parameter).Compile();
}
private void InitSetter()
{
if (readOnly || useReflection)
{
return;
}
var parameter = Expression.Parameter(typeof(object));
var value = Expression.Parameter(typeof(object));
Expression instance = null;
var field = member as FieldInfo;
if (field != null)
{
var declaringType = field.DeclaringType;
if (!field.IsStatic)
{
if (declaringType.IsValueType)
{
// I'm not sure we can get here with a Nullable, but if so,
// we must use the Value property, see PSGetMemberBinder.GetTargetValue.
instance = Nullable.GetUnderlyingType(declaringType) != null
? (Expression)Expression.Property(parameter, "Value")
: Expression.Unbox(parameter, declaringType);
}
else
{
instance = parameter.Cast(declaringType);
}
}
Expression setterExpr;
string errMessage = null;
Type errType = field.FieldType;
if (declaringType.IsGenericTypeDefinition)
{
errMessage = ParserStrings.PropertyInGenericType;
if (errType.ContainsGenericParameters)
{
errType = typeof(object);
}
}
else if (readOnly)
{
errMessage = ParserStrings.PropertyIsReadOnly;
}
if (errMessage != null)
{
Expression innerException = Expression.New(CachedReflectionInfo.SetValueException_ctor,
Expression.Constant("PropertyAssignmentException"),
Expression.Constant(null, typeof(Exception)),
Expression.Constant(errMessage),
Expression.NewArrayInit(typeof(object), Expression.Constant(field.Name)));
setterExpr = Compiler.ThrowRuntimeErrorWithInnerException("PropertyAssignmentException",
Expression.Constant(errMessage),
innerException, errType, Expression.Constant(field.Name));
}
else
{
setterExpr = Expression.Assign(Expression.Field(instance, field), Expression.Convert(value, field.FieldType));
}
_setterDelegate = Expression.Lambda<SetterDelegate>(setterExpr, parameter, value).Compile();
return;
}
var property = (PropertyInfo)member;
MethodInfo propertySetter = property.GetSetMethod(true);
instance = this.isStatic ? null : parameter.Cast(propertySetter.DeclaringType);
_setterDelegate =
Expression.Lambda<SetterDelegate>(
Expression.Assign(Expression.Property(instance, property),
Expression.Convert(value, property.PropertyType)), parameter, value).Compile();
}
internal MemberInfo member;
internal GetterDelegate getterDelegate
{
get
{
if (_getterDelegate == null)
{
InitGetter();
}
return _getterDelegate;
}
}
private GetterDelegate _getterDelegate;
internal SetterDelegate setterDelegate
{
get
{
if (_setterDelegate == null)
{
InitSetter();
}
return _setterDelegate;
}
}
private SetterDelegate _setterDelegate;
internal bool useReflection;
internal bool readOnly;
internal bool writeOnly;
internal bool isStatic;
internal Type propertyType;
private bool? _isHidden;
internal override bool IsHidden
{
get
{
if (_isHidden == null)
{
_isHidden = member.GetCustomAttributes(typeof(HiddenAttribute), inherit: false).Length != 0;
}
return _isHidden.Value;
}
}
private AttributeCollection _attributes;
internal AttributeCollection Attributes
{
get
{
if (_attributes == null)
{
// Since AttributeCollection can only be constructed with an Attribute[], one is built.
var objAttributes = this.member.GetCustomAttributes(true);
_attributes = new AttributeCollection(objAttributes.Cast<Attribute>().ToArray());
}
return _attributes;
}
}
}
/// <summary>
/// Compare the signatures of the methods, returning true if the methods have
/// the same signature.
/// </summary>
private static bool SameSignature(MethodBase method1, MethodBase method2)
{
if (method1.GetGenericArguments().Length != method2.GetGenericArguments().Length)
{
return false;
}
ParameterInfo[] parameters1 = method1.GetParameters();
ParameterInfo[] parameters2 = method2.GetParameters();
if (parameters1.Length != parameters2.Length)
{
return false;
}
for (int i = 0; i < parameters1.Length; ++i)
{
if (parameters1[i].ParameterType != parameters2[i].ParameterType
|| parameters1[i].IsOut != parameters2[i].IsOut
|| parameters1[i].IsOptional != parameters2[i].IsOptional)
{
return false;
}
}
return true;
}
/// <summary>
/// Adds an overload to a list of MethodInfo. Before adding to the list, the
/// list is searched to make sure we don't end up with 2 functions with the
/// same signature. This can happen when there is a newslot method.
/// </summary>
private static void AddOverload(List<MethodBase> previousMethodEntry, MethodInfo method)
{
bool add = true;
for (int i = 0; i < previousMethodEntry.Count; i++)
{
if (SameSignature(previousMethodEntry[i], method))
{
add = false;
break;
}
}
if (add)
{
previousMethodEntry.Add(method);
}
}
private static void PopulateMethodReflectionTable(Type type, MethodInfo[] methods, CacheTable typeMethods)
{
for (int i = 0; i < methods.Length; i++)
{
MethodInfo method = methods[i];
if (method.DeclaringType == type)
{
string methodName = method.Name;
var previousMethodEntry = (List<MethodBase>)typeMethods[methodName];
if (previousMethodEntry == null)
{
var methodEntry = new List<MethodBase> { method };
typeMethods.Add(methodName, methodEntry);
}
else
{
AddOverload(previousMethodEntry, method);
}
}
}
if (type.BaseType != null)
{
PopulateMethodReflectionTable(type.BaseType, methods, typeMethods);
}
}
private static void PopulateMethodReflectionTable(ConstructorInfo[] ctors, CacheTable typeMethods)
{
foreach (var ctor in ctors)
{
var previousMethodEntry = (List<MethodBase>)typeMethods["new"];
if (previousMethodEntry == null)
{
var methodEntry = new List<MethodBase>();
methodEntry.Add(ctor);
typeMethods.Add("new", methodEntry);
}
else
{
previousMethodEntry.Add(ctor);
}
}
}
/// <summary>
/// Called from GetMethodReflectionTable within a lock to fill the
/// method cache table.
/// </summary>
/// <param name="type">Type to get methods from.</param>
/// <param name="typeMethods">Table to be filled.</param>
/// <param name="bindingFlags">BindingFlags to use.</param>
private static void PopulateMethodReflectionTable(Type type, CacheTable typeMethods, BindingFlags bindingFlags)
{
Type typeToGetMethod = type;
// Assemblies in CoreCLR might not allow reflection execution on their internal types. In such case, we walk up
// the derivation chain to find the first public parent, and use reflection methods on the public parent.
if (!TypeResolver.IsPublic(type) && DisallowPrivateReflection(type))
{
typeToGetMethod = GetFirstPublicParentType(type);
}
// In CoreCLR, "GetFirstPublicParentType" may return null if 'type' is an interface
if (typeToGetMethod != null)
{
MethodInfo[] methods = typeToGetMethod.GetMethods(bindingFlags);
PopulateMethodReflectionTable(typeToGetMethod, methods, typeMethods);
}
Type[] interfaces = type.GetInterfaces();
for (int interfaceIndex = 0; interfaceIndex < interfaces.Length; interfaceIndex++)
{
var interfaceType = interfaces[interfaceIndex];
if (!TypeResolver.IsPublic(interfaceType))
{
continue;
}
if (interfaceType.IsGenericType && type.IsArray)
{
continue; // GetInterfaceMap is not supported in this scenario... not sure if we need to do something special here...
}
MethodInfo[] methods;
if (type.IsInterface)
{
methods = interfaceType.GetMethods(bindingFlags);
}
else
{
InterfaceMapping interfaceMapping = type.GetInterfaceMap(interfaceType);
methods = interfaceMapping.InterfaceMethods;
}
for (int methodIndex = 0; methodIndex < methods.Length; methodIndex++)
{
MethodInfo interfaceMethodDefinition = methods[methodIndex];
if ((!interfaceMethodDefinition.IsPublic) ||
(interfaceMethodDefinition.IsStatic != ((BindingFlags.Static & bindingFlags) != 0)))
{
continue;
}
var previousMethodEntry = (List<MethodBase>)typeMethods[interfaceMethodDefinition.Name];
if (previousMethodEntry == null)
{
var methodEntry = new List<MethodBase> { interfaceMethodDefinition };
typeMethods.Add(interfaceMethodDefinition.Name, methodEntry);
}
else
{
if (!previousMethodEntry.Contains(interfaceMethodDefinition))
{
previousMethodEntry.Add(interfaceMethodDefinition);
}
}
}
}
if ((bindingFlags & BindingFlags.Static) != 0 && TypeResolver.IsPublic(type))
{
// We don't add constructors if there was a static method named new.
// We don't add constructors if the target type is not public, because it's useless to an internal type.
var previousMethodEntry = (List<MethodBase>)typeMethods["new"];
if (previousMethodEntry == null)
{
var ctorBindingFlags = bindingFlags & ~(BindingFlags.FlattenHierarchy | BindingFlags.Static);
ctorBindingFlags |= BindingFlags.Instance;
var ctorInfos = type.GetConstructors(ctorBindingFlags);
PopulateMethodReflectionTable(ctorInfos, typeMethods);
}
}
for (int i = 0; i < typeMethods.memberCollection.Count; i++)
{
typeMethods.memberCollection[i] =
new MethodCacheEntry(((List<MethodBase>)typeMethods.memberCollection[i]).ToArray());
}
}
/// <summary>
/// Called from GetEventReflectionTable within a lock to fill the
/// event cache table.
/// </summary>
/// <param name="type">Type to get events from.</param>
/// <param name="typeEvents">Table to be filled.</param>
/// <param name="bindingFlags">BindingFlags to use.</param>
private static void PopulateEventReflectionTable(Type type, Dictionary<string, EventCacheEntry> typeEvents, BindingFlags bindingFlags)
{
// Assemblies in CoreCLR might not allow reflection execution on their internal types. In such case, we walk up
// the derivation chain to find the first public parent, and use reflection events on the public parent.
if (!TypeResolver.IsPublic(type) && DisallowPrivateReflection(type))
{
type = GetFirstPublicParentType(type);
}
// In CoreCLR, "GetFirstPublicParentType" may return null if 'type' is an interface
if (type != null)
{
EventInfo[] events = type.GetEvents(bindingFlags);
var tempTable = new Dictionary<string, List<EventInfo>>(StringComparer.OrdinalIgnoreCase);
for (int i = 0; i < events.Length; i++)
{
var typeEvent = events[i];
string eventName = typeEvent.Name;
List<EventInfo> previousEntry;
if (!tempTable.TryGetValue(eventName, out previousEntry))
{
var eventEntry = new List<EventInfo> { typeEvent };
tempTable.Add(eventName, eventEntry);
}
else
{
previousEntry.Add(typeEvent);
}
}
foreach (var entry in tempTable)
{
typeEvents.Add(entry.Key, new EventCacheEntry(entry.Value.ToArray()));
}
}
}
/// <summary>
/// This method is necessary because an overridden property in a specific class derived from a generic one will
/// appear twice. The second time, it should be ignored.
/// </summary>
private static bool PropertyAlreadyPresent(List<PropertyInfo> previousProperties, PropertyInfo property)
{
// The loop below
bool returnValue = false;
ParameterInfo[] propertyParameters = property.GetIndexParameters();
int propertyIndexLength = propertyParameters.Length;
for (int propertyIndex = 0; propertyIndex < previousProperties.Count; propertyIndex++)
{
var previousProperty = previousProperties[propertyIndex];
ParameterInfo[] previousParameters = previousProperty.GetIndexParameters();
if (previousParameters.Length == propertyIndexLength)
{
bool parametersAreSame = true;
for (int parameterIndex = 0; parameterIndex < previousParameters.Length; parameterIndex++)
{
ParameterInfo previousParameter = previousParameters[parameterIndex];
ParameterInfo propertyParameter = propertyParameters[parameterIndex];
if (previousParameter.ParameterType != propertyParameter.ParameterType)
{
parametersAreSame = false;
break;
}
}
if (parametersAreSame)
{
returnValue = true;
break;
}
}
}
return returnValue;
}
/// <summary>
/// Called from GetPropertyReflectionTable within a lock to fill the
/// property cache table.
/// </summary>
/// <param name="type">Type to get properties from.</param>
/// <param name="typeProperties">Table to be filled.</param>
/// <param name="bindingFlags">BindingFlags to use.</param>
private static void PopulatePropertyReflectionTable(Type type, CacheTable typeProperties, BindingFlags bindingFlags)
{
var tempTable = new Dictionary<string, List<PropertyInfo>>(StringComparer.OrdinalIgnoreCase);
Type typeToGetPropertyAndField = type;
// Assemblies in CoreCLR might not allow reflection execution on their internal types. In such case, we walk up the
// derivation chain to find the first public parent, and use reflection properties/fields on the public parent.
if (!TypeResolver.IsPublic(type) && DisallowPrivateReflection(type))
{
typeToGetPropertyAndField = GetFirstPublicParentType(type);
}
// In CoreCLR, "GetFirstPublicParentType" may return null if 'type' is an interface
PropertyInfo[] properties;
if (typeToGetPropertyAndField != null)
{
properties = typeToGetPropertyAndField.GetProperties(bindingFlags);
for (int i = 0; i < properties.Length; i++)
{
PopulateSingleProperty(type, properties[i], tempTable, properties[i].Name);
}
}
Type[] interfaces = type.GetInterfaces();
for (int interfaceIndex = 0; interfaceIndex < interfaces.Length; interfaceIndex++)
{
Type interfaceType = interfaces[interfaceIndex];
if (!TypeResolver.IsPublic(interfaceType))
{
continue;
}
properties = interfaceType.GetProperties(bindingFlags);
for (int propertyIndex = 0; propertyIndex < properties.Length; propertyIndex++)
{
PopulateSingleProperty(type, properties[propertyIndex], tempTable, properties[propertyIndex].Name);
}
}
foreach (var pairs in tempTable)
{
var propertiesList = pairs.Value;
PropertyInfo firstProperty = propertiesList[0];
if ((propertiesList.Count > 1) || (firstProperty.GetIndexParameters().Length != 0))
{
typeProperties.Add(pairs.Key, new ParameterizedPropertyCacheEntry(propertiesList));
}
else
{
typeProperties.Add(pairs.Key, new PropertyCacheEntry(firstProperty));
}
}
// In CoreCLR, "GetFirstPublicParentType" may return null if 'type' is an interface
if (typeToGetPropertyAndField != null)
{
FieldInfo[] fields = typeToGetPropertyAndField.GetFields(bindingFlags);
for (int i = 0; i < fields.Length; i++)
{
FieldInfo field = fields[i];
string fieldName = field.Name;
var previousMember = (PropertyCacheEntry)typeProperties[fieldName];
if (previousMember == null)
{
typeProperties.Add(fieldName, new PropertyCacheEntry(field));
}
else
{
// A property/field declared with new in a derived class might appear twice
if (!string.Equals(previousMember.member.Name, fieldName))
{
throw new ExtendedTypeSystemException("NotACLSComplaintField", null,
ExtendedTypeSystem.NotAClsCompliantFieldProperty, fieldName, type.FullName, previousMember.member.Name);
}
}
}
}
}
private static void PopulateSingleProperty(Type type, PropertyInfo property, Dictionary<string, List<PropertyInfo>> tempTable, string propertyName)
{
List<PropertyInfo> previousPropertyEntry;
if (!tempTable.TryGetValue(propertyName, out previousPropertyEntry))
{
previousPropertyEntry = new List<PropertyInfo> { property };
tempTable.Add(propertyName, previousPropertyEntry);
}
else
{
var firstProperty = previousPropertyEntry[0];
if (!string.Equals(property.Name, firstProperty.Name, StringComparison.Ordinal))
{
throw new ExtendedTypeSystemException("NotACLSComplaintProperty", null,
ExtendedTypeSystem.NotAClsCompliantFieldProperty, property.Name, type.FullName, firstProperty.Name);
}
if (PropertyAlreadyPresent(previousPropertyEntry, property))
{
return;
}
previousPropertyEntry.Add(property);
}
}
#region Handle_Internal_Type_Reflection_In_CoreCLR
/// <summary>
/// The dictionary cache about if an assembly supports reflection execution on its internal types.
/// </summary>
private static readonly ConcurrentDictionary<string, bool> s_disallowReflectionCache =
new ConcurrentDictionary<string, bool>(StringComparer.OrdinalIgnoreCase);
/// <summary>
/// Check if the type is defined in an assembly that disallows reflection execution on internal types.
/// - .NET Framework assemblies don't support reflection execution on their internal types.
/// </summary>
internal static bool DisallowPrivateReflection(Type type)
{
bool disallowReflection = false;
Assembly assembly = type.Assembly;
if (s_disallowReflectionCache.TryGetValue(assembly.FullName, out disallowReflection))
{
return disallowReflection;
}
var productAttribute = assembly.GetCustomAttribute<AssemblyProductAttribute>();
if (productAttribute != null && string.Equals(productAttribute.Product, "Microsoft® .NET Framework", StringComparison.OrdinalIgnoreCase))
{
disallowReflection = true;
}
else
{
#pragma warning disable SYSLIB0015
// Check for 'DisablePrivateReflectionAttribute'. It's applied at the assembly level, and allow an assembly to opt-out of private/internal reflection.
var disablePrivateReflectionAttribute = assembly.GetCustomAttribute<System.Runtime.CompilerServices.DisablePrivateReflectionAttribute>();
disallowReflection = disablePrivateReflectionAttribute != null;
#pragma warning restore SYSLIB0015
}
s_disallowReflectionCache.TryAdd(assembly.FullName, disallowReflection);
return disallowReflection;
}
/// <summary>
/// Walk up the derivation chain to find the first public parent type.
/// </summary>
internal static Type GetFirstPublicParentType(Type type)
{
Dbg.Assert(!TypeResolver.IsPublic(type), "type should not be public.");
Type parent = type.BaseType;
while (parent != null)
{
if (parent.IsPublic)
{
return parent;
}
parent = parent.BaseType;
}
// Return null when type is an interface
return null;
}
#endregion Handle_Internal_Type_Reflection_In_CoreCLR
/// <summary>
/// Called from GetProperty and GetProperties to populate the
/// typeTable with all public properties and fields
/// of type.
/// </summary>
/// <param name="type">Type to load properties for.</param>
private static CacheTable GetStaticPropertyReflectionTable(Type type)
{
lock (s_staticPropertyCacheTable)
{
CacheTable typeTable = null;
if (s_staticPropertyCacheTable.TryGetValue(type, out typeTable))
{
return typeTable;
}
typeTable = new CacheTable();
PopulatePropertyReflectionTable(type, typeTable, staticBindingFlags);
s_staticPropertyCacheTable[type] = typeTable;
return typeTable;
}
}
/// <summary>
/// Retrieves the table for static methods.
/// </summary>
/// <param name="type">Type to load methods for.</param>
private static CacheTable GetStaticMethodReflectionTable(Type type)
{
lock (s_staticMethodCacheTable)
{
CacheTable typeTable = null;
if (s_staticMethodCacheTable.TryGetValue(type, out typeTable))
{
return typeTable;
}
typeTable = new CacheTable();
PopulateMethodReflectionTable(type, typeTable, staticBindingFlags);
s_staticMethodCacheTable[type] = typeTable;
return typeTable;
}
}
/// <summary>
/// Retrieves the table for static events.
/// </summary>
/// <param name="type">Type containing properties to load in typeTable.</param>
private static Dictionary<string, EventCacheEntry> GetStaticEventReflectionTable(Type type)
{
lock (s_staticEventCacheTable)
{
Dictionary<string, EventCacheEntry> typeTable;
if (s_staticEventCacheTable.TryGetValue(type, out typeTable))
{
return typeTable;
}
typeTable = new Dictionary<string, EventCacheEntry>();
PopulateEventReflectionTable(type, typeTable, staticBindingFlags);
s_staticEventCacheTable[type] = typeTable;
return typeTable;
}
}
/// <summary>
/// Called from GetProperty and GetProperties to populate the
/// typeTable with all public properties and fields
/// of type.
/// </summary>
/// <param name="type">Type with properties to load in typeTable.</param>
private static CacheTable GetInstancePropertyReflectionTable(Type type)
{
lock (s_instancePropertyCacheTable)
{
CacheTable typeTable = null;
if (s_instancePropertyCacheTable.TryGetValue(type, out typeTable))
{
return typeTable;
}
typeTable = new CacheTable();
PopulatePropertyReflectionTable(type, typeTable, instanceBindingFlags);
s_instancePropertyCacheTable[type] = typeTable;
return typeTable;
}
}
/// <summary>
/// Retrieves the table for instance methods.
/// </summary>
/// <param name="type">Type with methods to load in typeTable.</param>
private static CacheTable GetInstanceMethodReflectionTable(Type type)
{
lock (s_instanceMethodCacheTable)
{
CacheTable typeTable = null;
if (s_instanceMethodCacheTable.TryGetValue(type, out typeTable))
{
return typeTable;
}
typeTable = new CacheTable();
PopulateMethodReflectionTable(type, typeTable, instanceBindingFlags);
s_instanceMethodCacheTable[type] = typeTable;
return typeTable;
}
}
internal IEnumerable<object> GetPropertiesAndMethods(Type type, bool @static)
{
CacheTable propertyTable = @static
? GetStaticPropertyReflectionTable(type)
: GetInstancePropertyReflectionTable(type);
for (int i = 0; i < propertyTable.memberCollection.Count; i++)
{
var propertyCacheEntry = propertyTable.memberCollection[i] as PropertyCacheEntry;
if (propertyCacheEntry != null)
yield return propertyCacheEntry.member;
}
CacheTable methodTable = @static
? GetStaticMethodReflectionTable(type)
: GetInstanceMethodReflectionTable(type);
for (int i = 0; i < methodTable.memberCollection.Count; i++)
{
var method = methodTable.memberCollection[i] as MethodCacheEntry;
if (method != null && !method[0].method.IsSpecialName)
{
yield return method;
}
}
}
/// <summary>
/// Retrieves the table for instance events.
/// </summary>
/// <param name="type">Type containing methods to load in typeTable.</param>
private static Dictionary<string, EventCacheEntry> GetInstanceEventReflectionTable(Type type)
{
lock (s_instanceEventCacheTable)
{
Dictionary<string, EventCacheEntry> typeTable;
if (s_instanceEventCacheTable.TryGetValue(type, out typeTable))
{
return typeTable;
}
typeTable = new Dictionary<string, EventCacheEntry>(StringComparer.OrdinalIgnoreCase);
PopulateEventReflectionTable(type, typeTable, instanceBindingFlags);
s_instanceEventCacheTable[type] = typeTable;
return typeTable;
}
}
/// <summary>
/// Returns true if a parameterized property should be in a PSMemberInfoCollection of type t.
/// </summary>
/// <param name="t">Type of a PSMemberInfoCollection like the type of T in PSMemberInfoCollection of T.</param>
/// <returns>True if a parameterized property should be in a collection.</returns>
/// <remarks>
/// Usually typeof(T).IsAssignableFrom(typeof(PSParameterizedProperty)) would work like it does
/// for PSMethod and PSProperty, but since PSParameterizedProperty derives from PSMethodInfo and
/// since we don't want to have ParameterizedProperties in PSMemberInfoCollection of PSMethodInfo
/// we need this method.
/// </remarks>
internal static bool IsTypeParameterizedProperty(Type t)
{
return t == typeof(PSMemberInfo) || t == typeof(PSParameterizedProperty);
}
private T GetDotNetPropertyImpl<T>(object obj, string propertyName, MemberNamePredicate predicate) where T : PSMemberInfo
{
bool lookingForProperties = typeof(T).IsAssignableFrom(typeof(PSProperty));
bool lookingForParameterizedProperties = IsTypeParameterizedProperty(typeof(T));
if (!lookingForProperties && !lookingForParameterizedProperties)
{
return null;
}
CacheTable typeTable = _isStatic
? GetStaticPropertyReflectionTable((Type)obj)
: GetInstancePropertyReflectionTable(obj.GetType());
object entry = predicate != null
? typeTable.GetFirstOrDefault(predicate)
: typeTable[propertyName];
switch (entry)
{
case null:
return null;
case PropertyCacheEntry cacheEntry when lookingForProperties:
return new PSProperty(cacheEntry.member.Name, this, obj, cacheEntry) { IsHidden = cacheEntry.IsHidden } as T;
case ParameterizedPropertyCacheEntry paramCacheEntry when lookingForParameterizedProperties:
// TODO: check for HiddenAttribute
// We can't currently write a parameterized property in a PowerShell class so this isn't too important,
// but if someone added the attribute to their C#, it'd be good to set isHidden correctly here.
return new PSParameterizedProperty(paramCacheEntry.propertyName, this, obj, paramCacheEntry) as T;
default: return null;
}
}
private T GetDotNetMethodImpl<T>(object obj, string methodName, MemberNamePredicate predicate) where T : PSMemberInfo
{
if (!typeof(T).IsAssignableFrom(typeof(PSMethod)))
{
return null;
}
CacheTable typeTable = _isStatic
? GetStaticMethodReflectionTable((Type)obj)
: GetInstanceMethodReflectionTable(obj.GetType());
var methods = predicate != null
? (MethodCacheEntry)typeTable.GetFirstOrDefault(predicate)
: (MethodCacheEntry)typeTable[methodName];
if (methods == null)
{
return null;
}
var isCtor = methods[0].method is ConstructorInfo;
bool isSpecial = !isCtor && methods[0].method.IsSpecialName;
return PSMethod.Create(methods[0].method.Name, this, obj, methods, isSpecial, methods.IsHidden) as T;
}
internal T GetDotNetProperty<T>(object obj, string propertyName) where T : PSMemberInfo
{
return GetDotNetPropertyImpl<T>(obj, propertyName, predicate: null);
}
internal T GetDotNetMethod<T>(object obj, string methodName) where T : PSMemberInfo
{
return GetDotNetMethodImpl<T>(obj, methodName, predicate: null);
}
protected T GetFirstDotNetPropertyOrDefault<T>(object obj, MemberNamePredicate predicate) where T : PSMemberInfo
{
return GetDotNetPropertyImpl<T>(obj, propertyName: null, predicate);
}
protected T GetFirstDotNetMethodOrDefault<T>(object obj, MemberNamePredicate predicate) where T : PSMemberInfo
{
return GetDotNetMethodImpl<T>(obj, methodName: null, predicate);
}
protected T GetFirstDotNetEventOrDefault<T>(object obj, MemberNamePredicate predicate) where T : PSMemberInfo
{
if (!typeof(T).IsAssignableFrom(typeof(PSEvent)))
{
return null;
}
var table = _isStatic
? GetStaticEventReflectionTable((Type)obj)
: GetInstanceEventReflectionTable(obj.GetType());
foreach (var psEvent in table.Values)
{
if (predicate(psEvent.events[0].Name))
{
return new PSEvent(psEvent.events[0]) as T;
}
}
return null;
}
protected T GetFirstDynamicMemberOrDefault<T>(object obj, MemberNamePredicate predicate) where T : PSMemberInfo
{
var idmop = obj as IDynamicMetaObjectProvider;
if (idmop == null || obj is PSObject)
{
return null;
}
if (!typeof(T).IsAssignableFrom(typeof(PSDynamicMember)))
{
return null;
}
foreach (var name in idmop.GetMetaObject(Expression.Variable(idmop.GetType())).GetDynamicMemberNames())
{
if (predicate(name))
{
return new PSDynamicMember(name) as T;
}
}
return null;
}
internal void AddAllProperties<T>(object obj, PSMemberInfoInternalCollection<T> members, bool ignoreDuplicates) where T : PSMemberInfo
{
bool lookingForProperties = typeof(T).IsAssignableFrom(typeof(PSProperty));
bool lookingForParameterizedProperties = IsTypeParameterizedProperty(typeof(T));
if (!lookingForProperties && !lookingForParameterizedProperties)
{
return;
}
CacheTable table = _isStatic
? GetStaticPropertyReflectionTable((Type)obj)
: GetInstancePropertyReflectionTable(obj.GetType());
for (int i = 0; i < table.memberCollection.Count; i++)
{
if (table.memberCollection[i] is PropertyCacheEntry propertyEntry)
{
if (lookingForProperties)
{
if (!ignoreDuplicates || (members[propertyEntry.member.Name] == null))
{
members.Add(
new PSProperty(
name: propertyEntry.member.Name,
adapter: this,
baseObject: obj,
adapterData: propertyEntry)
{ IsHidden = propertyEntry.IsHidden } as T);
}
}
}
else if (lookingForParameterizedProperties)
{
var parameterizedPropertyEntry = (ParameterizedPropertyCacheEntry)table.memberCollection[i];
if (!ignoreDuplicates || (members[parameterizedPropertyEntry.propertyName] == null))
{
// TODO: check for HiddenAttribute
// We can't currently write a parameterized property in a PowerShell class so this isn't too important,
// but if someone added the attribute to their C#, it'd be good to set isHidden correctly here.
members.Add(new PSParameterizedProperty(parameterizedPropertyEntry.propertyName,
this, obj, parameterizedPropertyEntry) as T);
}
}
}
}
internal void AddAllMethods<T>(object obj, PSMemberInfoInternalCollection<T> members, bool ignoreDuplicates) where T : PSMemberInfo
{
if (!typeof(T).IsAssignableFrom(typeof(PSMethod)))
{
return;
}
CacheTable table = _isStatic
? GetStaticMethodReflectionTable((Type)obj)
: GetInstanceMethodReflectionTable(obj.GetType());
for (int i = 0; i < table.memberCollection.Count; i++)
{
var method = (MethodCacheEntry)table.memberCollection[i];
var isCtor = method[0].method is ConstructorInfo;
var name = isCtor ? "new" : method[0].method.Name;
if (!ignoreDuplicates || (members[name] == null))
{
bool isSpecial = !isCtor && method[0].method.IsSpecialName;
members.Add(PSMethod.Create(name, this, obj, method, isSpecial, method.IsHidden) as T);
}
}
}
internal void AddAllEvents<T>(object obj, PSMemberInfoInternalCollection<T> members, bool ignoreDuplicates) where T : PSMemberInfo
{
if (!typeof(T).IsAssignableFrom(typeof(PSEvent)))
{
return;
}
var table = _isStatic
? GetStaticEventReflectionTable((Type)obj)
: GetInstanceEventReflectionTable(obj.GetType());
foreach (var psEvent in table.Values)
{
if (!ignoreDuplicates || (members[psEvent.events[0].Name] == null))
{
members.Add(new PSEvent(psEvent.events[0]) as T);
}
}
}
internal void AddAllDynamicMembers<T>(object obj, PSMemberInfoInternalCollection<T> members, bool ignoreDuplicates) where T : PSMemberInfo
{
var idmop = obj as IDynamicMetaObjectProvider;
if (idmop == null || obj is PSObject)
{
return;
}
if (!typeof(T).IsAssignableFrom(typeof(PSDynamicMember)))
{
return;
}
foreach (var name in idmop.GetMetaObject(Expression.Variable(idmop.GetType())).GetDynamicMemberNames())
{
members.Add(new PSDynamicMember(name) as T);
}
}
private static bool PropertyIsStatic(PSProperty property)
{
if (!(property.adapterData is PropertyCacheEntry entry))
{
return false;
}
return entry.isStatic;
}
#endregion auxiliary methods and classes
#region virtual
#region member
internal override bool CanSiteBinderOptimize(MemberTypes typeToOperateOn) { return true; }
private static readonly ConcurrentDictionary<Type, ConsolidatedString> s_typeToTypeNameDictionary =
new ConcurrentDictionary<Type, ConsolidatedString>();
internal static ConsolidatedString GetInternedTypeNameHierarchy(Type type)
{
return s_typeToTypeNameDictionary.GetOrAdd(type,
t => new ConsolidatedString(GetDotNetTypeNameHierarchy(t), interned: true));
}
protected override ConsolidatedString GetInternedTypeNameHierarchy(object obj)
{
return GetInternedTypeNameHierarchy(obj.GetType());
}
/// <summary>
/// Get the .NET member based on the given member name.
/// </summary>
/// <remark>
/// Dynamic members of an object that implements IDynamicMetaObjectProvider are not included because
/// 1. Dynamic members cannot be invoked via reflection;
/// 2. Access to dynamic members is handled by the DLR for free.
/// </remark>
/// <param name="obj">Object to retrieve the PSMemberInfo from.</param>
/// <param name="memberName">Name of the member to be retrieved.</param>
/// <returns>
/// The PSMemberInfo corresponding to memberName from obj,
/// or null if the given member name is not a member in the adapter.
/// </returns>
protected override T GetMember<T>(object obj, string memberName)
{
T returnValue = GetDotNetProperty<T>(obj, memberName);
if (returnValue != null) return returnValue;
return GetDotNetMethod<T>(obj, memberName);
}
/// <summary>
/// Get the first .NET member whose name matches the specified <see cref="MemberNamePredicate"/>.
/// </summary>
protected override T GetFirstMemberOrDefault<T>(object obj, MemberNamePredicate predicate)
{
return GetFirstDotNetPropertyOrDefault<T>(obj, predicate) ?? GetFirstDotNetMethodOrDefault<T>(obj, predicate) ??
GetFirstDotNetEventOrDefault<T>(obj, predicate) ?? GetFirstDynamicMemberOrDefault<T>(obj, predicate);
}
/// <summary>
/// Retrieves all the members available in the object.
/// The adapter implementation is encouraged to cache all properties/methods available
/// in the first call to GetMember and GetMembers so that subsequent
/// calls can use the cache.
/// In the case of the .NET adapter that would be a cache from the .NET type to
/// the public properties and fields available in that type.
/// In the case of the DirectoryEntry adapter, this could be a cache of the objectClass
/// to the properties available in it.
/// </summary>
/// <remark>
/// Dynamic members of an object that implements IDynamicMetaObjectProvider are included because
/// we want to view the dynamic members via 'Get-Member' and be able to auto-complete those members.
/// </remark>
/// <param name="obj">Object to get all the member information from.</param>
/// <returns>All members in obj.</returns>
protected override PSMemberInfoInternalCollection<T> GetMembers<T>(object obj)
{
PSMemberInfoInternalCollection<T> returnValue = new PSMemberInfoInternalCollection<T>();
AddAllProperties<T>(obj, returnValue, false);
AddAllMethods<T>(obj, returnValue, false);
AddAllEvents<T>(obj, returnValue, false);
AddAllDynamicMembers(obj, returnValue, false);
return returnValue;
}
#endregion member
#region property
/// <summary>
/// Returns an array with the property attributes.
/// </summary>
/// <param name="property">Property we want the attributes from.</param>
/// <returns>An array with the property attributes.</returns>
protected override AttributeCollection PropertyAttributes(PSProperty property)
{
PropertyCacheEntry adapterData = (PropertyCacheEntry)property.adapterData;
return adapterData.Attributes;
}
/// <summary>
/// Returns the string representation of the property in the object.
/// </summary>
/// <param name="property">Property obtained in a previous GetMember.</param>
/// <returns>The string representation of the property in the object.</returns>
protected override string PropertyToString(PSProperty property)
{
StringBuilder returnValue = new StringBuilder();
if (PropertyIsStatic(property))
{
returnValue.Append("static ");
}
returnValue.Append(PropertyType(property, forDisplay: true));
returnValue.Append(' ');
returnValue.Append(property.Name);
returnValue.Append(" {");
if (PropertyIsGettable(property))
{
returnValue.Append("get;");
}
if (PropertyIsSettable(property))
{
returnValue.Append("set;");
}
returnValue.Append('}');
return returnValue.ToString();
}
/// <summary>
/// Returns the value from a property coming from a previous call to GetMember.
/// </summary>
/// <param name="property">PSProperty coming from a previous call to GetMember.</param>
/// <returns>The value of the property.</returns>
protected override object PropertyGet(PSProperty property)
{
PropertyCacheEntry adapterData = (PropertyCacheEntry)property.adapterData;
if (adapterData.propertyType.IsByRefLike)
{
throw new GetValueException(
nameof(ExtendedTypeSystem.CannotAccessByRefLikePropertyOrField),
innerException: null,
ExtendedTypeSystem.CannotAccessByRefLikePropertyOrField,
adapterData.member.Name,
adapterData.propertyType);
}
PropertyInfo propertyInfo = adapterData.member as PropertyInfo;
if (propertyInfo != null)
{
if (adapterData.writeOnly)
{
throw new GetValueException(
nameof(ExtendedTypeSystem.WriteOnlyProperty),
innerException: null,
ExtendedTypeSystem.WriteOnlyProperty,
propertyInfo.Name);
}
if (adapterData.useReflection)
{
return propertyInfo.GetValue(property.baseObject, null);
}
else
{
return adapterData.getterDelegate(property.baseObject);
}
}
FieldInfo field = adapterData.member as FieldInfo;
if (adapterData.useReflection)
{
return field?.GetValue(property.baseObject);
}
else
{
return adapterData.getterDelegate(property.baseObject);
}
}
/// <summary>
/// Sets the value of a property coming from a previous call to GetMember.
/// </summary>
/// <param name="property">PSProperty coming from a previous call to GetMember.</param>
/// <param name="setValue">Value to set the property with.</param>
/// <param name="convertIfPossible">Instructs the adapter to convert before setting, if the adapter supports conversion.</param>
protected override void PropertySet(PSProperty property, object setValue, bool convertIfPossible)
{
PropertyCacheEntry adapterData = (PropertyCacheEntry)property.adapterData;
if (adapterData.readOnly)
{
throw new SetValueException(
nameof(ExtendedTypeSystem.ReadOnlyProperty),
innerException: null,
ExtendedTypeSystem.ReadOnlyProperty,
adapterData.member.Name);
}
if (adapterData.propertyType.IsByRefLike)
{
throw new SetValueException(
nameof(ExtendedTypeSystem.CannotAccessByRefLikePropertyOrField),
innerException: null,
ExtendedTypeSystem.CannotAccessByRefLikePropertyOrField,
adapterData.member.Name,
adapterData.propertyType);
}
PropertyInfo propertyInfo = adapterData.member as PropertyInfo;
if (propertyInfo != null)
{
if (convertIfPossible)
{
setValue = PropertySetAndMethodArgumentConvertTo(setValue, propertyInfo.PropertyType, CultureInfo.InvariantCulture);
}
if (adapterData.useReflection)
{
propertyInfo.SetValue(property.baseObject, setValue, null);
}
else
{
adapterData.setterDelegate(property.baseObject, setValue);
}
return;
}
FieldInfo field = adapterData.member as FieldInfo;
if (convertIfPossible)
{
setValue = PropertySetAndMethodArgumentConvertTo(setValue, field.FieldType, CultureInfo.InvariantCulture);
}
if (adapterData.useReflection)
{
field.SetValue(property.baseObject, setValue);
}
else
{
adapterData.setterDelegate(property.baseObject, setValue);
}
}
/// <summary>
/// Returns true if the property is settable.
/// </summary>
/// <param name="property">Property to check.</param>
/// <returns>True if the property is settable.</returns>
protected override bool PropertyIsSettable(PSProperty property)
{
return !((PropertyCacheEntry)property.adapterData).readOnly;
}
/// <summary>
/// Returns true if the property is gettable.
/// </summary>
/// <param name="property">Property to check.</param>
/// <returns>True if the property is gettable.</returns>
protected override bool PropertyIsGettable(PSProperty property)
{
return !((PropertyCacheEntry)property.adapterData).writeOnly;
}
/// <summary>
/// Returns the name of the type corresponding to the property's value.
/// </summary>
/// <param name="property">PSProperty obtained in a previous GetMember.</param>
/// <param name="forDisplay">True if the result is for display purposes only.</param>
/// <returns>The name of the type corresponding to the member.</returns>
protected override string PropertyType(PSProperty property, bool forDisplay)
{
var propertyType = ((PropertyCacheEntry)property.adapterData).propertyType;
return forDisplay ? ToStringCodeMethods.Type(propertyType) : propertyType.FullName;
}
#endregion property
#region method
#region auxiliary to method calling
/// <summary>
/// Calls constructor using the arguments and catching the appropriate exception.
/// </summary>
/// <param name="arguments">Final arguments to the constructor.</param>
/// <returns>The return of the constructor.</returns>
/// <param name="methodInformation">Information about the method to call. Used for setting references.</param>
/// <param name="originalArguments">Original arguments in the method call. Used for setting references.</param>
/// <exception cref="MethodInvocationException">If the constructor throws an exception.</exception>
internal static object AuxiliaryConstructorInvoke(MethodInformation methodInformation, object[] arguments, object[] originalArguments)
{
object returnValue;
#pragma warning disable 56500
try
{
returnValue = methodInformation.Invoke(target: null, arguments);
}
catch (TargetInvocationException ex)
{
Exception inner = ex.InnerException ?? ex;
throw new MethodInvocationException(
"DotNetconstructorTargetInvocation",
inner,
ExtendedTypeSystem.MethodInvocationException,
".ctor", arguments.Length, inner.Message);
}
catch (Exception e)
{
throw new MethodInvocationException(
"DotNetconstructorException",
e,
ExtendedTypeSystem.MethodInvocationException,
".ctor", arguments.Length, e.Message);
}
SetReferences(arguments, methodInformation, originalArguments);
return returnValue;
#pragma warning restore 56500
}
/// <summary>
/// Calls method on target using the arguments and catching the appropriate exception.
/// </summary>
/// <param name="target">Object we want to call the method on.</param>
/// <param name="arguments">Final arguments to the method.</param>
/// <param name="methodInformation">Information about the method to call. Used for setting references.</param>
/// <param name="originalArguments">Original arguments in the method call. Used for setting references.</param>
/// <returns>The return of the method.</returns>
/// <exception cref="MethodInvocationException">If the method throws an exception.</exception>
internal static object AuxiliaryMethodInvoke(object target, object[] arguments, MethodInformation methodInformation, object[] originalArguments)
{
object result;
#pragma warning disable 56500
try
{
// call the method and return the result unless the return type is void in which
// case we'll return AutomationNull.Value
result = methodInformation.Invoke(target, arguments);
}
catch (TargetInvocationException ex)
{
// Special handling to allow methods to throw flowcontrol exceptions
// Needed for ExitNestedPrompt exception.
if (ex.InnerException is FlowControlException || ex.InnerException is ScriptCallDepthException)
throw ex.InnerException;
// Win7:138054 - When wrapping cmdlets, we want the original exception to be raised,
// not the wrapped exception that occurs from invoking a steppable pipeline method.
if (ex.InnerException is ParameterBindingException)
throw ex.InnerException;
Exception inner = ex.InnerException ?? ex;
throw new MethodInvocationException(
"DotNetMethodTargetInvocation",
inner,
ExtendedTypeSystem.MethodInvocationException,
methodInformation.method.Name, arguments.Length, inner.Message);
}
//
// Note that FlowControlException, ScriptCallDepthException and ParameterBindingException will be wrapped in
// a TargetInvocationException only when the invocation uses reflection so we need to bubble them up here as well.
//
catch (ParameterBindingException) { throw; }
catch (FlowControlException) { throw; }
catch (ScriptCallDepthException) { throw; }
catch (PipelineStoppedException) { throw; }
catch (Exception e)
{
if (methodInformation.method.DeclaringType == typeof(SteppablePipeline) &&
(methodInformation.method.Name.Equals("Begin") ||
methodInformation.method.Name.Equals("Process") ||
methodInformation.method.Name.Equals("End")))
{
// Don't wrap exceptions that happen when calling methods on SteppablePipeline
// that are only used for proxy commands.
throw;
}
throw new MethodInvocationException(
"DotNetMethodException",
e,
ExtendedTypeSystem.MethodInvocationException,
methodInformation.method.Name, arguments.Length, e.Message);
}
#pragma warning restore 56500
SetReferences(arguments, methodInformation, originalArguments);
MethodInfo methodInfo = methodInformation.method as MethodInfo;
if (methodInfo != null && methodInfo.ReturnType != typeof(void))
return result;
return AutomationNull.Value;
}
/// <summary>
/// Converts a MethodBase[] into a MethodInformation[]
/// </summary>
/// <param name="methods">The methods to be converted.</param>
/// <returns>The MethodInformation[] corresponding to methods.</returns>
internal static MethodInformation[] GetMethodInformationArray(MethodBase[] methods)
{
int methodCount = methods.Length;
MethodInformation[] returnValue = new MethodInformation[methodCount];
for (int i = 0; i < methods.Length; i++)
{
returnValue[i] = new MethodInformation(methods[i], 0);
}
return returnValue;
}
/// <summary>
/// Calls the method best suited to the arguments on target.
/// </summary>
/// <param name="methodName">Used for error messages.</param>
/// <param name="target">Object to call the method on.</param>
/// <param name="methodInformation">Method information corresponding to methods.</param>
/// <param name="invocationConstraints">Invocation constraints.</param>
/// <param name="arguments">Arguments of the call.</param>
/// <returns>The return of the method.</returns>
/// <exception cref="MethodInvocationException">If the method throws an exception.</exception>
/// <exception cref="MethodException">If we could not find a method for the given arguments.</exception>
internal static object MethodInvokeDotNet(
string methodName,
object target,
MethodInformation[] methodInformation,
PSMethodInvocationConstraints invocationConstraints,
object[] arguments)
{
object[] newArguments;
MethodInformation bestMethod = GetBestMethodAndArguments(methodName, methodInformation, invocationConstraints, arguments, out newArguments);
if (bestMethod.method is ConstructorInfo)
{
return InvokeResolvedConstructor(bestMethod, newArguments, arguments);
}
string methodDefinition = bestMethod.methodDefinition;
ScriptTrace.Trace(1, "TraceMethodCall", ParserStrings.TraceMethodCall, methodDefinition);
PSObject.MemberResolution.WriteLine("Calling Method: {0}", methodDefinition);
return AuxiliaryMethodInvoke(target, newArguments, bestMethod, arguments);
}
/// <summary>
/// Calls the method best suited to the arguments on target.
/// </summary>
/// <param name="type">The type being constructed, used for diagnostics and caching.</param>
/// <param name="constructors">All overloads for the constructors.</param>
/// <param name="arguments">Arguments of the call.</param>
/// <returns>The return of the method.</returns>
/// <exception cref="MethodInvocationException">If the method throws an exception.</exception>
/// <exception cref="MethodException">If we could not find a method for the given arguments.</exception>
internal static object ConstructorInvokeDotNet(Type type, ConstructorInfo[] constructors, object[] arguments)
{
var newConstructors = GetMethodInformationArray(constructors);
object[] newArguments;
MethodInformation bestMethod = GetBestMethodAndArguments(type.Name, newConstructors, arguments, out newArguments);
return InvokeResolvedConstructor(bestMethod, newArguments, arguments);
}
private static object InvokeResolvedConstructor(MethodInformation bestMethod, object[] newArguments, object[] arguments)
{
if ((PSObject.MemberResolution.Options & PSTraceSourceOptions.WriteLine) != 0)
{
PSObject.MemberResolution.WriteLine("Calling Constructor: {0}", DotNetAdapter.GetMethodInfoOverloadDefinition(null,
bestMethod.method, 0));
}
return AuxiliaryConstructorInvoke(bestMethod, newArguments, arguments);
}
/// <summary>
/// This is a flavor of MethodInvokeDotNet to deal with a peculiarity of property setters:
/// Tthe setValue is always the last parameter. This enables a parameter after a varargs or optional
/// parameters and GetBestMethodAndArguments is not prepared for that.
/// This method disregards the last parameter in its call to GetBestMethodAndArguments used in this case
/// more for its "Arguments" side than for its "BestMethod" side, since there is only one method.
/// </summary>
internal static void ParameterizedPropertyInvokeSet(string propertyName, object target, object valuetoSet, MethodInformation[] methodInformation, object[] arguments)
{
// bestMethodIndex is ignored since we know we have only 1 method. GetBestMethodAndArguments
// is still useful to deal with optional and varargs parameters and to perform the type conversions
// of all parameters but the last one
object[] newArguments;
MethodInformation bestMethod = GetBestMethodAndArguments(propertyName, methodInformation, arguments, out newArguments);
PSObject.MemberResolution.WriteLine("Calling Set Method: {0}", bestMethod.methodDefinition);
ParameterInfo[] bestMethodParameters = bestMethod.method.GetParameters();
Type propertyType = bestMethodParameters[bestMethodParameters.Length - 1].ParameterType;
// we have to convert the last parameter (valuetoSet) manually since it has been
// disregarded in GetBestMethodAndArguments.
object lastArgument;
try
{
lastArgument = PropertySetAndMethodArgumentConvertTo(valuetoSet, propertyType, CultureInfo.InvariantCulture);
}
catch (InvalidCastException e)
{
// NTRAID#Windows Out Of Band Releases-924162-2005/11/17-JonN
throw new MethodException(
"PropertySetterConversionInvalidCastArgument",
e,
ExtendedTypeSystem.MethodArgumentConversionException,
arguments.Length - 1, valuetoSet, propertyName, propertyType, e.Message);
}
// and we also have to rebuild the argument array to include the last parameter
object[] finalArguments = new object[newArguments.Length + 1];
for (int i = 0; i < newArguments.Length; i++)
{
finalArguments[i] = newArguments[i];
}
finalArguments[newArguments.Length] = lastArgument;
AuxiliaryMethodInvoke(target, finalArguments, bestMethod, arguments);
}
internal static string GetMethodInfoOverloadDefinition(string memberName, MethodBase methodEntry, int parametersToIgnore)
{
StringBuilder builder = new StringBuilder();
if (methodEntry.IsStatic)
{
builder.Append("static ");
}
MethodInfo method = methodEntry as MethodInfo;
if (method != null)
{
builder.Append(ToStringCodeMethods.Type(method.ReturnType));
builder.Append(' ');
}
else
{
ConstructorInfo ctorInfo = methodEntry as ConstructorInfo;
if (ctorInfo != null)
{
builder.Append(ToStringCodeMethods.Type(ctorInfo.DeclaringType));
builder.Append(' ');
}
}
if (methodEntry.DeclaringType.IsInterface)
{
builder.Append(ToStringCodeMethods.Type(methodEntry.DeclaringType, dropNamespaces: true));
builder.Append('.');
}
builder.Append(memberName ?? methodEntry.Name);
if (methodEntry.IsGenericMethodDefinition)
{
builder.Append('[');
Type[] genericArgs = methodEntry.GetGenericArguments();
for (int i = 0; i < genericArgs.Length; i++)
{
if (i > 0) { builder.Append(", "); }
builder.Append(ToStringCodeMethods.Type(genericArgs[i]));
}
builder.Append(']');
}
builder.Append('(');
System.Reflection.ParameterInfo[] parameters = methodEntry.GetParameters();
int parametersLength = parameters.Length - parametersToIgnore;
if (parametersLength > 0)
{
for (int i = 0; i < parametersLength; i++)
{
System.Reflection.ParameterInfo parameter = parameters[i];
var parameterType = parameter.ParameterType;
if (parameterType.IsByRef)
{
builder.Append("[ref] ");
parameterType = parameterType.GetElementType();
}
if (parameterType.IsArray && (i == parametersLength - 1))
{
// The extension method 'CustomAttributeExtensions.GetCustomAttributes(ParameterInfo, Type, Boolean)' has inconsistent
// behavior on its return value in both FullCLR and CoreCLR. According to MSDN, if the attribute cannot be found, it
// should return an empty collection. However, it returns null in some rare cases [when the parameter isn't backed by
// actual metadata].
// This inconsistent behavior affects OneCore powershell because we are using the extension method here when compiling
// against CoreCLR. So we need to add a null check until this is fixed in CLR.
var paramArrayAttrs = parameter.GetCustomAttributes(typeof(ParamArrayAttribute), false);
if (paramArrayAttrs != null && paramArrayAttrs.Length > 0)
builder.Append("Params ");
}
builder.Append(ToStringCodeMethods.Type(parameterType));
builder.Append(' ');
builder.Append(parameter.Name);
builder.Append(", ");
}
builder.Remove(builder.Length - 2, 2);
}
builder.Append(')');
return builder.ToString();
}
#endregion auxiliary to method calling
/// <summary>
/// Called after a non null return from GetMember to try to call
/// the method with the arguments.
/// </summary>
/// <param name="method">The non empty return from GetMethods.</param>
/// <param name="arguments">The arguments to use.</param>
/// <returns>The return value for the method.</returns>
protected override object MethodInvoke(PSMethod method, object[] arguments)
{
return this.MethodInvoke(method, null, arguments);
}
/// <summary>
/// Called after a non null return from GetMember to try to call
/// the method with the arguments.
/// </summary>
/// <param name="method">The non empty return from GetMethods.</param>
/// <param name="invocationConstraints">Invocation constraints.</param>
/// <param name="arguments">The arguments to use.</param>
/// <returns>The return value for the method.</returns>
protected override object MethodInvoke(PSMethod method, PSMethodInvocationConstraints invocationConstraints, object[] arguments)
{
MethodCacheEntry methodEntry = (MethodCacheEntry)method.adapterData;
return DotNetAdapter.MethodInvokeDotNet(
method.Name,
method.baseObject,
methodEntry.methodInformationStructures,
invocationConstraints,
arguments);
}
/// <summary>
/// Called after a non null return from GetMember to return the overloads.
/// </summary>
/// <param name="method">The return of GetMember.</param>
/// <returns></returns>
protected override Collection<string> MethodDefinitions(PSMethod method)
{
MethodCacheEntry methodEntry = (MethodCacheEntry)method.adapterData;
IList<string> uniqueValues = methodEntry
.methodInformationStructures
.Select(static m => m.methodDefinition)
.Distinct(StringComparer.Ordinal)
.ToList();
return new Collection<string>(uniqueValues);
}
#endregion method
#region parameterized property
/// <summary>
/// Returns the name of the type corresponding to the property's value.
/// </summary>
/// <param name="property">Property obtained in a previous GetMember.</param>
/// <returns>The name of the type corresponding to the member.</returns>
protected override string ParameterizedPropertyType(PSParameterizedProperty property)
{
var adapterData = (ParameterizedPropertyCacheEntry)property.adapterData;
return adapterData.propertyType.FullName;
}
/// <summary>
/// Returns true if the property is settable.
/// </summary>
/// <param name="property">Property to check.</param>
/// <returns>True if the property is settable.</returns>
protected override bool ParameterizedPropertyIsSettable(PSParameterizedProperty property)
{
return !((ParameterizedPropertyCacheEntry)property.adapterData).readOnly;
}
/// <summary>
/// Returns true if the property is gettable.
/// </summary>
/// <param name="property">Property to check.</param>
/// <returns>True if the property is gettable.</returns>
protected override bool ParameterizedPropertyIsGettable(PSParameterizedProperty property)
{
return !((ParameterizedPropertyCacheEntry)property.adapterData).writeOnly;
}
/// <summary>
/// Called after a non null return from GetMember to get the property value.
/// </summary>
/// <param name="property">The non empty return from GetMember.</param>
/// <param name="arguments">The arguments to use.</param>
/// <returns>The return value for the property.</returns>
protected override object ParameterizedPropertyGet(PSParameterizedProperty property, object[] arguments)
{
var adapterData = (ParameterizedPropertyCacheEntry)property.adapterData;
return DotNetAdapter.MethodInvokeDotNet(property.Name, property.baseObject,
adapterData.getterInformation, null, arguments);
}
/// <summary>
/// Called after a non null return from GetMember to set the property value.
/// </summary>
/// <param name="property">The non empty return from GetMember.</param>
/// <param name="setValue">The value to set property with.</param>
/// <param name="arguments">The arguments to use.</param>
protected override void ParameterizedPropertySet(PSParameterizedProperty property, object setValue, object[] arguments)
{
var adapterData = (ParameterizedPropertyCacheEntry)property.adapterData;
ParameterizedPropertyInvokeSet(adapterData.propertyName, property.baseObject, setValue,
adapterData.setterInformation, arguments);
}
/// <summary>
/// Called after a non null return from GetMember to return the overloads.
/// </summary>
protected override Collection<string> ParameterizedPropertyDefinitions(PSParameterizedProperty property)
{
var adapterData = (ParameterizedPropertyCacheEntry)property.adapterData;
var returnValue = new Collection<string>();
for (int i = 0; i < adapterData.propertyDefinition.Length; i++)
{
returnValue.Add(adapterData.propertyDefinition[i]);
}
return returnValue;
}
/// <summary>
/// Returns the string representation of the property in the object.
/// </summary>
/// <param name="property">Property obtained in a previous GetMember.</param>
/// <returns>The string representation of the property in the object.</returns>
protected override string ParameterizedPropertyToString(PSParameterizedProperty property)
{
StringBuilder returnValue = new StringBuilder();
Collection<string> definitions = ParameterizedPropertyDefinitions(property);
for (int i = 0; i < definitions.Count; i++)
{
returnValue.Append(definitions[i]);
returnValue.Append(", ");
}
returnValue.Remove(returnValue.Length - 2, 2);
return returnValue.ToString();
}
#endregion parameterized property
#endregion virtual
}
#region DotNetAdapterWithOnlyPropertyLookup
/// <summary>
/// This is used by PSObject to support dotnet member lookup for the adapted
/// objects.
/// </summary>
/// <remarks>
/// This class is created to avoid cluttering DotNetAdapter with if () { } blocks .
/// </remarks>
internal class BaseDotNetAdapterForAdaptedObjects : DotNetAdapter
{
/// <summary>
/// Return a collection representing the <paramref name="obj"/> object's
/// members as returned by CLR reflection.
/// </summary>
protected override PSMemberInfoInternalCollection<T> GetMembers<T>(object obj)
{
PSMemberInfoInternalCollection<T> returnValue = new PSMemberInfoInternalCollection<T>();
AddAllProperties<T>(obj, returnValue, true);
AddAllMethods<T>(obj, returnValue, true);
AddAllEvents<T>(obj, returnValue, true);
return returnValue;
}
/// <summary>
/// Returns a member representing the <paramref name="obj"/> as given by CLR reflection.
/// </summary>
protected override T GetMember<T>(object obj, string memberName)
{
PSProperty property = base.GetDotNetProperty<PSProperty>(obj, memberName);
if (typeof(T).IsAssignableFrom(typeof(PSProperty)) && (property != null))
{
return property as T;
}
// In order to not break v1..base dotnet adapter should not return methods
// when accessed with T as PSMethod.. accessing method with PSMemberInfo
// is ok as property always gets precedence over methods and duplicates
// are ignored.
if (typeof(T) == typeof(PSMemberInfo))
{
T returnValue = base.GetDotNetMethod<T>(obj, memberName);
// We only return a method if there is no property by the same name
// to match the behavior we have in GetMembers
if (returnValue != null && property == null)
{
return returnValue;
}
}
if (IsTypeParameterizedProperty(typeof(T)))
{
PSParameterizedProperty parameterizedProperty = base.GetDotNetProperty<PSParameterizedProperty>(obj, memberName);
// We only return a parameterized property if there is no property by the same name
// to match the behavior we have in GetMembers
if (parameterizedProperty != null && property == null)
{
return parameterizedProperty as T;
}
}
return null;
}
/// <summary>
/// Returns the first reflection member whose name matches the specified <see cref="MemberNamePredicate"/>.
/// Otherwise, return null.
/// </summary>
protected override T GetFirstMemberOrDefault<T>(object obj, MemberNamePredicate predicate)
{
PSProperty property = base.GetFirstDotNetPropertyOrDefault<PSProperty>(obj, predicate);
if (typeof(T).IsAssignableFrom(typeof(PSProperty)) && property != null)
{
return property as T;
}
// In order to not break v1..base dotnet adapter should not return methods
// when accessed with T as PSMethod.. accessing method with PSMemberInfo
// is ok as property always gets precedence over methods and duplicates
// are ignored.
if (typeof(T) == typeof(PSMemberInfo))
{
T returnValue = base.GetFirstDotNetMethodOrDefault<T>(obj, predicate);
// We only return a method if there is no property by the same name
// to match the behavior we have in GetMembers
if (returnValue != null && property == null)
{
return returnValue;
}
}
if (IsTypeParameterizedProperty(typeof(T)))
{
var parameterizedProperty = base.GetFirstDotNetPropertyOrDefault<PSParameterizedProperty>(obj, predicate);
// We only return a parameterized property if there is no property by the same name
// to match the behavior we have in GetMembers
if (parameterizedProperty != null && property == null)
{
return parameterizedProperty as T;
}
}
return null;
}
}
#endregion
/// <summary>
/// Used only to add a COM style type name to a COM interop .NET type.
/// </summary>
internal class DotNetAdapterWithComTypeName : DotNetAdapter
{
private readonly ComTypeInfo _comTypeInfo;
internal DotNetAdapterWithComTypeName(ComTypeInfo comTypeInfo)
{
_comTypeInfo = comTypeInfo;
}
protected override IEnumerable<string> GetTypeNameHierarchy(object obj)
{
for (Type type = obj.GetType(); type != null; type = type.BaseType)
{
if (type.FullName.Equals("System.__ComObject"))
{
yield return ComAdapter.GetComTypeName(_comTypeInfo.Clsid);
}
yield return type.FullName;
}
}
protected override ConsolidatedString GetInternedTypeNameHierarchy(object obj)
{
return new ConsolidatedString(GetTypeNameHierarchy(obj), interned: true);
}
}
/// <summary>
/// Adapter used for GetMember and GetMembers only.
/// All other methods will not be called.
/// </summary>
internal abstract class MemberRedirectionAdapter : Adapter
{
#region virtual
#region property specific
/// <summary>
/// Returns an array with the property attributes.
/// </summary>
/// <param name="property">Property we want the attributes from.</param>
/// <returns>An array with the property attributes.</returns>
protected override AttributeCollection PropertyAttributes(PSProperty property)
{
return new AttributeCollection();
}
/// <summary>
/// Returns the value from a property coming from a previous call to GetMember.
/// </summary>
/// <param name="property">PSProperty coming from a previous call to GetMember.</param>
/// <returns>The value of the property.</returns>
protected override object PropertyGet(PSProperty property)
{
Diagnostics.Assert(false, "redirection adapter is not called for properties");
throw PSTraceSource.NewNotSupportedException();
}
/// <summary>
/// Sets the value of a property coming from a previous call to GetMember.
/// </summary>
/// <param name="property">PSProperty coming from a previous call to GetMember.</param>
/// <param name="setValue">Value to set the property with.</param>
/// <param name="convertIfPossible">Instructs the adapter to convert before setting, if the adapter supports conversion.</param>
protected override void PropertySet(PSProperty property, object setValue, bool convertIfPossible)
{
Diagnostics.Assert(false, "redirection adapter is not called for properties");
throw PSTraceSource.NewNotSupportedException();
}
/// <summary>
/// Returns true if the property is settable.
/// </summary>
/// <param name="property">Property to check.</param>
/// <returns>True if the property is settable.</returns>
protected override bool PropertyIsSettable(PSProperty property)
{
Diagnostics.Assert(false, "redirection adapter is not called for properties");
throw PSTraceSource.NewNotSupportedException();
}
/// <summary>
/// Returns true if the property is gettable.
/// </summary>
/// <param name="property">Property to check.</param>
/// <returns>True if the property is gettable.</returns>
protected override bool PropertyIsGettable(PSProperty property)
{
Diagnostics.Assert(false, "redirection adapter is not called for properties");
throw PSTraceSource.NewNotSupportedException();
}
/// <summary>
/// Returns the name of the type corresponding to the property's value.
/// </summary>
/// <param name="property">PSProperty obtained in a previous GetMember.</param>
/// <param name="forDisplay">True if the result is for display purposes only.</param>
/// <returns>The name of the type corresponding to the member.</returns>
protected override string PropertyType(PSProperty property, bool forDisplay)
{
Diagnostics.Assert(false, "redirection adapter is not called for properties");
throw PSTraceSource.NewNotSupportedException();
}
/// <summary>
/// Returns the string representation of the property in the object.
/// </summary>
/// <param name="property">Property obtained in a previous GetMember.</param>
/// <returns>The string representation of the property in the object.</returns>
protected override string PropertyToString(PSProperty property)
{
Diagnostics.Assert(false, "redirection adapter is not called for properties");
throw PSTraceSource.NewNotSupportedException();
}
#endregion property specific
#region method specific
/// <summary>
/// Called after a non null return from GetMember to try to call
/// the method with the arguments.
/// </summary>
/// <param name="method">The non empty return from GetMethods.</param>
/// <param name="arguments">The arguments to use.</param>
/// <returns>The return value for the method.</returns>
protected override object MethodInvoke(PSMethod method, object[] arguments)
{
Diagnostics.Assert(false, "redirection adapter is not called for methods");
throw PSTraceSource.NewNotSupportedException();
}
/// <summary>
/// Called after a non null return from GetMember to return the overloads.
/// </summary>
/// <param name="method">The return of GetMember.</param>
/// <returns></returns>
protected override Collection<string> MethodDefinitions(PSMethod method)
{
Diagnostics.Assert(false, "redirection adapter is not called for methods");
throw PSTraceSource.NewNotSupportedException();
}
#endregion method specific
#endregion virtual
}
/// <summary>
/// Adapter for properties in the inside PSObject if it has a null BaseObject.
/// </summary>
internal class PSObjectAdapter : MemberRedirectionAdapter
{
#region virtual
/// <summary>
/// Returns the TypeNameHierarchy out of an object.
/// </summary>
/// <param name="obj">Object to get the TypeNameHierarchy from.</param>
protected override IEnumerable<string> GetTypeNameHierarchy(object obj)
{
return ((PSObject)obj).InternalTypeNames;
}
/// <summary>
/// Returns null if memberName is not a member in the adapter or
/// the corresponding PSMemberInfo.
/// </summary>
/// <param name="obj">Object to retrieve the PSMemberInfo from.</param>
/// <param name="memberName">Name of the member to be retrieved.</param>
/// <returns>The PSMemberInfo corresponding to memberName from obj.</returns>
protected override T GetMember<T>(object obj, string memberName)
{
return ((PSObject)obj).Members[memberName] as T;
}
/// <summary>
/// Returns the first PSMemberInfo whose name matches the specified <see cref="MemberNamePredicate"/>.
/// Otherwise, return null.
/// </summary>
protected override T GetFirstMemberOrDefault<T>(object obj, MemberNamePredicate predicate)
{
return ((PSObject)obj).GetFirstPropertyOrDefault(predicate) as T;
}
/// <summary>
/// Retrieves all the members available in the object.
/// The adapter implementation is encouraged to cache all properties/methods available
/// in the first call to GetMember and GetMembers so that subsequent
/// calls can use the cache.
/// In the case of the .NET adapter that would be a cache from the .NET type to
/// the public properties and fields available in that type.
/// In the case of the DirectoryEntry adapter, this could be a cache of the objectClass
/// to the properties available in it.
/// </summary>
/// <param name="obj">Object to get all the member information from.</param>
/// <returns>All members in obj.</returns>
protected override PSMemberInfoInternalCollection<T> GetMembers<T>(object obj)
{
var returnValue = new PSMemberInfoInternalCollection<T>();
PSObject mshObj = (PSObject)obj;
foreach (PSMemberInfo member in mshObj.Members)
{
T memberAsT = member as T;
if (memberAsT != null)
{
returnValue.Add(memberAsT);
}
}
return returnValue;
}
#endregion virtual
}
/// <summary>
/// Adapter for properties inside a member set.
/// </summary>
internal class PSMemberSetAdapter : MemberRedirectionAdapter
{
#region virtual
protected override IEnumerable<string> GetTypeNameHierarchy(object obj)
{
// Make sure PSMemberSet adapter shows PSMemberSet as the typename.
// This is because PSInternalMemberSet internal class derives from
// PSMemberSet to support delay loading PSBase, PSObject etc. We
// should not show internal type members to the users. Also this
// might break type files shipped in v1.
yield return typeof(PSMemberSet).FullName;
}
/// <summary>
/// Returns null if memberName is not a member in the adapter or
/// the corresponding PSMemberInfo.
/// </summary>
/// <param name="obj">Object to retrieve the PSMemberInfo from.</param>
/// <param name="memberName">Name of the member to be retrieved.</param>
/// <returns>The PSMemberInfo corresponding to memberName from obj.</returns>
protected override T GetMember<T>(object obj, string memberName)
{
return ((PSMemberSet)obj).Members[memberName] as T;
}
/// <summary>
/// Returns the first PSMemberInfo whose name matches the specified <see cref="MemberNamePredicate"/>.
/// Otherwise, return null.
/// </summary>
protected override T GetFirstMemberOrDefault<T>(object obj, MemberNamePredicate predicate)
{
foreach (var member in ((PSMemberSet)obj).Members)
{
if (predicate(member.Name))
{
return member as T;
}
}
return null;
}
/// <summary>
/// Retrieves all the members available in the object.
/// The adapter implementation is encouraged to cache all properties/methods available
/// in the first call to GetMember and GetMembers so that subsequent
/// calls can use the cache.
/// In the case of the .NET adapter that would be a cache from the .NET type to
/// the public properties and fields available in that type.
/// In the case of the DirectoryEntry adapter, this could be a cache of the objectClass
/// to the properties available in it.
/// </summary>
/// <param name="obj">Object to get all the member information from.</param>
/// <returns>All members in obj.</returns>
protected override PSMemberInfoInternalCollection<T> GetMembers<T>(object obj)
{
var returnValue = new PSMemberInfoInternalCollection<T>();
foreach (PSMemberInfo member in ((PSMemberSet)obj).Members)
{
T memberAsT = member as T;
if (memberAsT != null)
{
returnValue.Add(memberAsT);
}
}
return returnValue;
}
#endregion virtual
}
/// <summary>
/// Base class for all adapters that adapt only properties and retain
/// .NET methods.
/// </summary>
internal abstract class PropertyOnlyAdapter : DotNetAdapter
{
/// <summary>
/// For a PropertyOnlyAdapter, the property may come from various sources,
/// but methods, including parameterized properties, still come from DotNetAdapter.
/// So, the binder can optimize on method calls for objects that map to a
/// custom PropertyOnlyAdapter.
/// </summary>
internal override bool CanSiteBinderOptimize(MemberTypes typeToOperateOn)
{
switch (typeToOperateOn)
{
case MemberTypes.Property:
return false;
case MemberTypes.Method:
return true;
default:
throw new InvalidOperationException("Should be unreachable. Update code if other member types need to be handled here.");
}
}
protected override ConsolidatedString GetInternedTypeNameHierarchy(object obj)
{
return new ConsolidatedString(GetTypeNameHierarchy(obj), interned: true);
}
/// <summary>
/// Returns null if propertyName is not a property in the adapter or
/// the corresponding PSProperty with its adapterData set to information
/// to be used when retrieving the property.
/// </summary>
/// <param name="obj">Object to retrieve the PSProperty from.</param>
/// <param name="propertyName">Name of the property to be retrieved.</param>
/// <returns>The PSProperty corresponding to propertyName from obj.</returns>
protected abstract PSProperty DoGetProperty(object obj, string propertyName);
/// <summary>
/// Returns the first PSProperty whose name matches the specified <see cref="MemberNamePredicate"/>.
/// Otherwise, return null.
/// </summary>
/// <param name="obj">Object to retrieve the PSProperty from.</param>
/// <param name="predicate">The predicate to find the matching member.</param>
/// <returns>The first PSProperty whose name matches the <paramref name="predicate"/>.</returns>
protected abstract PSProperty DoGetFirstPropertyOrDefault(object obj, MemberNamePredicate predicate);
/// <summary>
/// Retrieves all the properties available in the object.
/// </summary>
/// <param name="obj">Object to get all the property information from.</param>
/// <param name="members">Collection where the properties will be added.</param>
protected abstract void DoAddAllProperties<T>(object obj, PSMemberInfoInternalCollection<T> members) where T : PSMemberInfo;
/// <summary>
/// Returns null if memberName is not a member in the adapter or
/// the corresponding PSMemberInfo.
/// </summary>
/// <param name="obj">Object to retrieve the PSMemberInfo from.</param>
/// <param name="memberName">Name of the member to be retrieved.</param>
/// <returns>The PSMemberInfo corresponding to memberName from obj.</returns>
protected override T GetMember<T>(object obj, string memberName)
{
PSProperty property = DoGetProperty(obj, memberName);
if (typeof(T).IsAssignableFrom(typeof(PSProperty)) && property != null)
{
return property as T;
}
if (typeof(T).IsAssignableFrom(typeof(PSMethod)))
{
T returnValue = base.GetDotNetMethod<T>(obj, memberName);
// We only return a method if there is no property by the same name
// to match the behavior we have in GetMembers
if (returnValue != null && property == null)
{
return returnValue;
}
}
if (IsTypeParameterizedProperty(typeof(T)))
{
PSParameterizedProperty parameterizedProperty = base.GetDotNetProperty<PSParameterizedProperty>(obj, memberName);
// We only return a parameterized property if there is no property by the same name
// to match the behavior we have in GetMembers
if (parameterizedProperty != null && property == null)
{
return parameterizedProperty as T;
}
}
return null;
}
/// <summary>
/// Returns the first PSMemberInfo whose name matches the specified <see cref="MemberNamePredicate"/>.
/// Otherwise, return null.
/// </summary>
/// <typeparam name="T">A subtype of <see cref="PSMemberInfo"/>.</typeparam>
/// <param name="obj">Object to retrieve the PSMemberInfo from.</param>
/// <param name="predicate">A name matching predicate.</param>
/// <returns>The PSMemberInfo corresponding to the predicate match.</returns>
protected override T GetFirstMemberOrDefault<T>(object obj, MemberNamePredicate predicate)
{
PSProperty property = DoGetFirstPropertyOrDefault(obj, predicate);
if (typeof(T).IsAssignableFrom(typeof(PSProperty)))
{
return property as T;
}
if (typeof(T).IsAssignableFrom(typeof(PSMethod)))
{
T returnValue = base.GetFirstDotNetMethodOrDefault<T>(obj, predicate);
// We only return a method if there is no property by the same name
// to match the behavior we have in GetMembers
if (returnValue != null && property == null)
{
return returnValue;
}
}
if (IsTypeParameterizedProperty(typeof(T)))
{
var parameterizedProperty = base.GetFirstDotNetPropertyOrDefault<PSParameterizedProperty>(obj, predicate);
// We only return a parameterized property if there is no property by the same name
// to match the behavior we have in GetMembers
if (parameterizedProperty != null && property == null)
{
return parameterizedProperty as T;
}
}
return null;
}
/// <summary>
/// Retrieves all the members available in the object.
/// The adapter implementation is encouraged to cache all properties/methods available
/// in the first call to GetMember and GetMembers so that subsequent
/// calls can use the cache.
/// In the case of the .NET adapter that would be a cache from the .NET type to
/// the public properties and fields available in that type.
/// In the case of the DirectoryEntry adapter, this could be a cache of the objectClass
/// to the properties available in it.
/// </summary>
/// <param name="obj">Object to get all the member information from.</param>
/// <returns>All members in obj.</returns>
protected override PSMemberInfoInternalCollection<T> GetMembers<T>(object obj)
{
var returnValue = new PSMemberInfoInternalCollection<T>();
if (typeof(T).IsAssignableFrom(typeof(PSProperty)))
{
DoAddAllProperties<T>(obj, returnValue);
}
base.AddAllMethods(obj, returnValue, true);
if (IsTypeParameterizedProperty(typeof(T)))
{
var parameterizedProperties = new PSMemberInfoInternalCollection<PSParameterizedProperty>();
base.AddAllProperties(obj, parameterizedProperties, true);
foreach (PSParameterizedProperty parameterizedProperty in parameterizedProperties)
{
try
{
returnValue.Add(parameterizedProperty as T);
}
catch (ExtendedTypeSystemException)
{
// ignore duplicates: the adapted properties will take precedence
}
}
}
return returnValue;
}
}
/// <summary>
/// Deals with XmlNode objects.
/// </summary>
internal class XmlNodeAdapter : PropertyOnlyAdapter
{
#region virtual
/// <summary>
/// Returns the TypeNameHierarchy out of an object.
/// </summary>
/// <param name="obj">Object to get the TypeNameHierarchy from.</param>
protected override IEnumerable<string> GetTypeNameHierarchy(object obj)
{
XmlNode node = (XmlNode)obj;
string nodeNamespace = node.NamespaceURI;
IEnumerable<string> baseTypeNames = GetDotNetTypeNameHierarchy(obj);
if (string.IsNullOrEmpty(nodeNamespace))
{
foreach (string baseType in baseTypeNames)
{
yield return baseType;
}
}
else
{
StringBuilder firstType = null;
foreach (string baseType in baseTypeNames)
{
if (firstType == null)
{
firstType = new StringBuilder(baseType);
firstType.Append('#');
firstType.Append(node.NamespaceURI);
firstType.Append('#');
firstType.Append(node.LocalName);
yield return firstType.ToString();
}
yield return baseType;
}
}
}
/// <summary>
/// Retrieves all the properties available in the object.
/// </summary>
/// <param name="obj">Object to get all the property information from.</param>
/// <param name="members">Collection where the members will be added.</param>
protected override void DoAddAllProperties<T>(object obj, PSMemberInfoInternalCollection<T> members)
{
XmlNode node = (XmlNode)obj;
Dictionary<string, List<XmlNode>> nodeArrays = new Dictionary<string, List<XmlNode>>(StringComparer.OrdinalIgnoreCase);
if (node.Attributes != null)
{
foreach (XmlNode attribute in node.Attributes)
{
List<XmlNode> nodeList;
if (!nodeArrays.TryGetValue(attribute.LocalName, out nodeList))
{
nodeList = new List<XmlNode>();
nodeArrays[attribute.LocalName] = nodeList;
}
nodeList.Add(attribute);
}
}
if (node.ChildNodes != null)
{
foreach (XmlNode childNode in node.ChildNodes)
{
// Win8: 437544 ignore whitespace
if (childNode is XmlWhitespace)
{
continue;
}
List<XmlNode> nodeList;
if (!nodeArrays.TryGetValue(childNode.LocalName, out nodeList))
{
nodeList = new List<XmlNode>();
nodeArrays[childNode.LocalName] = nodeList;
}
nodeList.Add(childNode);
}
}
foreach (KeyValuePair<string, List<XmlNode>> nodeArrayEntry in nodeArrays)
{
members.Add(new PSProperty(nodeArrayEntry.Key, this, obj, nodeArrayEntry.Value.ToArray()) as T);
}
}
/// <summary>
/// Returns null if propertyName is not a property in the adapter or
/// the corresponding PSProperty with its adapterData set to information
/// to be used when retrieving the property.
/// </summary>
/// <param name="obj">Object to retrieve the PSProperty from.</param>
/// <param name="propertyName">Name of the property to be retrieved.</param>
/// <returns>The PSProperty corresponding to propertyName from obj.</returns>
protected override PSProperty DoGetProperty(object obj, string propertyName)
{
XmlNode[] nodes = FindNodes(obj, propertyName, StringComparison.OrdinalIgnoreCase);
if (nodes.Length == 0)
{
return null;
}
return new PSProperty(nodes[0].LocalName, this, obj, nodes);
}
protected override PSProperty DoGetFirstPropertyOrDefault(object obj, MemberNamePredicate predicate)
{
XmlNode node = FindNode(obj, predicate);
return node == null ? null : new PSProperty(node.LocalName, this, obj, node);
}
/// <summary>
/// Returns true if the property is settable.
/// </summary>
/// <param name="property">Property to check.</param>
/// <returns>True if the property is settable.</returns>
protected override bool PropertyIsSettable(PSProperty property)
{
XmlNode[] nodes = (XmlNode[])property.adapterData;
Diagnostics.Assert(nodes.Length != 0, "DoGetProperty would not return an empty array, it would return null instead");
if (nodes.Length != 1)
{
return false;
}
XmlNode node = nodes[0];
if (node is XmlText)
{
return true;
}
if (node is XmlAttribute)
{
return true;
}
XmlAttributeCollection nodeAttributes = node.Attributes;
if ((nodeAttributes != null) && (nodeAttributes.Count != 0))
{
return false;
}
XmlNodeList nodeChildren = node.ChildNodes;
if ((nodeChildren == null) || (nodeChildren.Count == 0))
{
return true;
}
if ((nodeChildren.Count == 1) && (nodeChildren[0].NodeType == XmlNodeType.Text))
{
return true;
}
return false;
}
/// <summary>
/// Returns true if the property is gettable.
/// </summary>
/// <param name="property">Property to check.</param>
/// <returns>True if the property is gettable.</returns>
protected override bool PropertyIsGettable(PSProperty property)
{
return true;
}
private static object GetNodeObject(XmlNode node)
{
XmlText text = node as XmlText;
if (text != null)
{
return text.InnerText;
}
XmlAttributeCollection nodeAttributes = node.Attributes;
// A node with attributes should not be simplified
if ((nodeAttributes != null) && (nodeAttributes.Count != 0))
{
return node;
}
// If node does not have any children, return the innertext of the node
if (!node.HasChildNodes)
{
return node.InnerText;
}
XmlNodeList nodeChildren = node.ChildNodes;
// nodeChildren will not be null as we already verified iff the node has children.
if ((nodeChildren.Count == 1) && (nodeChildren[0].NodeType == XmlNodeType.Text))
{
return node.InnerText;
}
XmlAttribute attribute = node as XmlAttribute;
if (attribute != null)
{
return attribute.Value;
}
return node;
}
/// <summary>
/// Returns the value from a property coming from a previous call to DoGetProperty.
/// </summary>
/// <param name="property">PSProperty coming from a previous call to DoGetProperty.</param>
/// <returns>The value of the property.</returns>
protected override object PropertyGet(PSProperty property)
{
XmlNode[] nodes = (XmlNode[])property.adapterData;
if (nodes.Length == 1)
{
return GetNodeObject(nodes[0]);
}
object[] returnValue = new object[nodes.Length];
for (int i = 0; i < nodes.Length; i++)
{
returnValue[i] = GetNodeObject(nodes[i]);
}
return returnValue;
}
/// <summary>
/// Sets the value of a property coming from a previous call to DoGetProperty.
/// </summary>
/// <param name="property">PSProperty coming from a previous call to DoGetProperty.</param>
/// <param name="setValue">Value to set the property with.</param>
/// <param name="convertIfPossible">Instructs the adapter to convert before setting, if the adapter supports conversion.</param>
protected override void PropertySet(PSProperty property, object setValue, bool convertIfPossible)
{
// XML is always a string so implicitly convert to string
string valueString = LanguagePrimitives.ConvertTo<string>(setValue);
XmlNode[] nodes = (XmlNode[])property.adapterData;
Diagnostics.Assert(nodes.Length != 0, "DoGetProperty would not return an empty array, it would return null instead");
if (nodes.Length > 1)
{
throw new SetValueException("XmlNodeSetRestrictionsMoreThanOneNode",
null,
ExtendedTypeSystem.XmlNodeSetShouldBeAString,
property.Name);
}
XmlNode node = nodes[0];
XmlText text = node as XmlText;
if (text != null)
{
text.InnerText = valueString;
return;
}
XmlAttributeCollection nodeAttributes = node.Attributes;
// A node with attributes cannot be set
if ((nodeAttributes != null) && (nodeAttributes.Count != 0))
{
throw new SetValueException("XmlNodeSetRestrictionsNodeWithAttributes",
null,
ExtendedTypeSystem.XmlNodeSetShouldBeAString,
property.Name);
}
XmlNodeList nodeChildren = node.ChildNodes;
if (nodeChildren == null || nodeChildren.Count == 0)
{
node.InnerText = valueString;
return;
}
if ((nodeChildren.Count == 1) && (nodeChildren[0].NodeType == XmlNodeType.Text))
{
node.InnerText = valueString;
return;
}
XmlAttribute attribute = node as XmlAttribute;
if (attribute != null)
{
attribute.Value = valueString;
return;
}
throw new SetValueException("XmlNodeSetRestrictionsUnknownNodeType",
null,
ExtendedTypeSystem.XmlNodeSetShouldBeAString,
property.Name);
}
/// <summary>
/// Returns the name of the type corresponding to the property.
/// </summary>
/// <param name="property">PSProperty obtained in a previous DoGetProperty.</param>
/// <param name="forDisplay">True if the result is for display purposes only.</param>
/// <returns>The name of the type corresponding to the property.</returns>
protected override string PropertyType(PSProperty property, bool forDisplay)
{
object value = null;
try
{
value = BasePropertyGet(property);
}
catch (GetValueException)
{
}
var type = value == null ? typeof(object) : value.GetType();
return forDisplay ? ToStringCodeMethods.Type(type) : type.FullName;
}
#endregion virtual
/// <summary>
/// Auxiliary in GetProperty to perform case sensitive and case insensitive searches
/// in the child nodes.
/// </summary>
/// <param name="obj">XmlNode to extract property from.</param>
/// <param name="propertyName">Property to look for.</param>
/// <param name="comparisonType">Type pf comparison to perform.</param>
/// <returns>The corresponding XmlNode or null if not present.</returns>
private static XmlNode[] FindNodes(object obj, string propertyName, StringComparison comparisonType)
{
List<XmlNode> retValue = new List<XmlNode>();
XmlNode node = (XmlNode)obj;
if (node.Attributes != null)
{
foreach (XmlNode attribute in node.Attributes)
{
if (attribute.LocalName.Equals(propertyName, comparisonType))
{
retValue.Add(attribute);
}
}
}
foreach (XmlNode childNode in node.ChildNodes)
{
if (childNode is XmlWhitespace)
{
// Win8: 437544 ignore whitespace
continue;
}
if (childNode.LocalName.Equals(propertyName, comparisonType))
{
retValue.Add(childNode);
}
}
return retValue.ToArray();
}
private static XmlNode FindNode(object obj, MemberNamePredicate predicate)
{
var node = (XmlNode)obj;
if (node.Attributes != null)
{
foreach (XmlNode attribute in node.Attributes)
{
if (predicate(attribute.LocalName))
{
return attribute;
}
}
}
foreach (XmlNode childNode in node.ChildNodes)
{
if (childNode is XmlWhitespace)
{
// Win8: 437544 ignore whitespace
continue;
}
if (predicate(childNode.LocalName))
{
return childNode;
}
}
return null;
}
}
/// <summary>
/// Deals with DataRow objects.
/// </summary>
internal class DataRowAdapter : PropertyOnlyAdapter
{
#region virtual
/// <summary>
/// Retrieves all the properties available in the object.
/// </summary>
/// <param name="obj">Object to get all the property information from.</param>
/// <param name="members">Collection where the members will be added.</param>
protected override void DoAddAllProperties<T>(object obj, PSMemberInfoInternalCollection<T> members)
{
DataRow dataRow = (DataRow)obj;
if (dataRow.Table == null || dataRow.Table.Columns == null)
{
return;
}
foreach (DataColumn property in dataRow.Table.Columns)
{
members.Add(new PSProperty(property.ColumnName, this, obj, property.ColumnName) as T);
}
return;
}
/// <summary>
/// Returns null if propertyName is not a property in the adapter or
/// the corresponding PSProperty with its adapterData set to information
/// to be used when retrieving the property.
/// </summary>
/// <param name="obj">Object to retrieve the PSProperty from.</param>
/// <param name="propertyName">Name of the property to be retrieved.</param>
/// <returns>The PSProperty corresponding to propertyName from obj.</returns>
protected override PSProperty DoGetProperty(object obj, string propertyName)
{
DataRow dataRow = (DataRow)obj;
if (!dataRow.Table.Columns.Contains(propertyName))
{
return null;
}
string columnName = dataRow.Table.Columns[propertyName].ColumnName;
return new PSProperty(columnName, this, obj, columnName);
}
protected override PSProperty DoGetFirstPropertyOrDefault(object obj, MemberNamePredicate predicate)
{
DataRow dataRow = (DataRow)obj;
foreach (DataColumn property in dataRow.Table.Columns)
{
if (predicate(property.ColumnName))
{
return new PSProperty(property.ColumnName, this, obj, property.ColumnName);
}
}
return null;
}
/// <summary>
/// Returns the name of the type corresponding to the property.
/// </summary>
/// <param name="property">PSProperty obtained in a previous DoGetProperty.</param>
/// <param name="forDisplay">True if the result is for display purposes only.</param>
/// <returns>The name of the type corresponding to the property.</returns>
protected override string PropertyType(PSProperty property, bool forDisplay)
{
string columnName = (string)property.adapterData;
DataRow dataRow = (DataRow)property.baseObject;
var dataType = dataRow.Table.Columns[columnName].DataType;
return forDisplay ? ToStringCodeMethods.Type(dataType) : dataType.FullName;
}
/// <summary>
/// Returns true if the property is settable.
/// </summary>
/// <param name="property">Property to check.</param>
/// <returns>True if the property is settable.</returns>
protected override bool PropertyIsSettable(PSProperty property)
{
string columnName = (string)property.adapterData;
DataRow dataRow = (DataRow)property.baseObject;
return !dataRow.Table.Columns[columnName].ReadOnly;
}
/// <summary>
/// Returns true if the property is gettable.
/// </summary>
/// <param name="property">Property to check.</param>
/// <returns>True if the property is gettable.</returns>
protected override bool PropertyIsGettable(PSProperty property)
{
return true;
}
/// <summary>
/// Returns the value from a property coming from a previous call to DoGetProperty.
/// </summary>
/// <param name="property">PSProperty coming from a previous call to DoGetProperty.</param>
/// <returns>The value of the property.</returns>
protected override object PropertyGet(PSProperty property)
{
DataRow dataRow = (DataRow)property.baseObject;
return dataRow[(string)property.adapterData];
}
/// <summary>
/// Sets the value of a property coming from a previous call to DoGetProperty.
/// </summary>
/// <param name="property">PSProperty coming from a previous call to DoGetProperty.</param>
/// <param name="setValue">Value to set the property with.</param>
/// <param name="convertIfPossible">Instructs the adapter to convert before setting, if the adapter supports conversion.</param>
protected override void PropertySet(PSProperty property, object setValue, bool convertIfPossible)
{
DataRow dataRow = (DataRow)property.baseObject;
dataRow[(string)property.adapterData] = setValue;
return;
}
#endregion virtual
}
/// <summary>
/// Deals with DataRowView objects.
/// </summary>
internal class DataRowViewAdapter : PropertyOnlyAdapter
{
#region virtual
/// <summary>
/// Retrieves all the properties available in the object.
/// </summary>
/// <param name="obj">Object to get all the property information from.</param>
/// <param name="members">Collection where the members will be added.</param>
protected override void DoAddAllProperties<T>(object obj, PSMemberInfoInternalCollection<T> members)
{
DataRowView dataRowView = (DataRowView)obj;
if (dataRowView.Row == null || dataRowView.Row.Table == null || dataRowView.Row.Table.Columns == null)
{
return;
}
foreach (DataColumn property in dataRowView.Row.Table.Columns)
{
members.Add(new PSProperty(property.ColumnName, this, obj, property.ColumnName) as T);
}
return;
}
/// <summary>
/// Returns null if propertyName is not a property in the adapter or
/// the corresponding PSProperty with its adapterData set to information
/// to be used when retrieving the property.
/// </summary>
/// <param name="obj">Object to retrieve the PSProperty from.</param>
/// <param name="propertyName">Name of the property to be retrieved.</param>
/// <returns>The PSProperty corresponding to propertyName from obj.</returns>
protected override PSProperty DoGetProperty(object obj, string propertyName)
{
DataRowView dataRowView = (DataRowView)obj;
if (!dataRowView.Row.Table.Columns.Contains(propertyName))
{
return null;
}
string columnName = dataRowView.Row.Table.Columns[propertyName].ColumnName;
return new PSProperty(columnName, this, obj, columnName);
}
protected override PSProperty DoGetFirstPropertyOrDefault(object obj, MemberNamePredicate predicate)
{
DataRowView dataRowView = (DataRowView)obj;
foreach (DataColumn column in dataRowView.Row.Table.Columns)
{
string columnName = column.ColumnName;
if (predicate(columnName))
{
return new PSProperty(columnName, this, obj, columnName);
}
}
return null;
}
/// <summary>
/// Returns the name of the type corresponding to the property.
/// </summary>
/// <param name="property">PSProperty obtained in a previous DoGetProperty.</param>
/// <param name="forDisplay">True if the result is for display purposes only.</param>
/// <returns>The name of the type corresponding to the property.</returns>
protected override string PropertyType(PSProperty property, bool forDisplay)
{
string columnName = (string)property.adapterData;
DataRowView dataRowView = (DataRowView)property.baseObject;
var dataType = dataRowView.Row.Table.Columns[columnName].DataType;
return forDisplay ? ToStringCodeMethods.Type(dataType) : dataType.FullName;
}
/// <summary>
/// Returns true if the property is settable.
/// </summary>
/// <param name="property">Property to check.</param>
/// <returns>True if the property is settable.</returns>
protected override bool PropertyIsSettable(PSProperty property)
{
string columnName = (string)property.adapterData;
DataRowView dataRowView = (DataRowView)property.baseObject;
return !dataRowView.Row.Table.Columns[columnName].ReadOnly;
}
/// <summary>
/// Returns true if the property is gettable.
/// </summary>
/// <param name="property">Property to check.</param>
/// <returns>True if the property is gettable.</returns>
protected override bool PropertyIsGettable(PSProperty property)
{
return true;
}
/// <summary>
/// Returns the value from a property coming from a previous call to DoGetProperty.
/// </summary>
/// <param name="property">PSProperty coming from a previous call to DoGetProperty.</param>
/// <returns>The value of the property.</returns>
protected override object PropertyGet(PSProperty property)
{
DataRowView dataRowView = (DataRowView)property.baseObject;
return dataRowView[(string)property.adapterData];
}
/// <summary>
/// Sets the value of a property coming from a previous call to DoGetProperty.
/// </summary>
/// <param name="property">PSProperty coming from a previous call to DoGetProperty.</param>
/// <param name="setValue">Value to set the property with.</param>
/// <param name="convertIfPossible">Instructs the adapter to convert before setting, if the adapter supports conversion.</param>
protected override void PropertySet(PSProperty property, object setValue, bool convertIfPossible)
{
DataRowView dataRowView = (DataRowView)property.baseObject;
dataRowView[(string)property.adapterData] = setValue;
return;
}
#endregion virtual
}
internal class TypeInference
{
[TraceSource("ETS", "Extended Type System")]
private static readonly PSTraceSource s_tracer = PSTraceSource.GetTracer("ETS", "Extended Type System");
internal static MethodInformation Infer(MethodInformation genericMethod, Type[] argumentTypes)
{
Dbg.Assert(genericMethod != null, "Caller should verify that genericMethod != null");
Dbg.Assert(argumentTypes != null, "Caller should verify that arguments != null");
// the cast is safe, because
// 1) only ConstructorInfo and MethodInfo derive from MethodBase
// 2) ConstructorInfo.IsGenericMethod is always false
MethodInfo originalMethod = (MethodInfo)genericMethod.method;
MethodInfo inferredMethod = TypeInference.Infer(originalMethod, argumentTypes, genericMethod.hasVarArgs);
if (inferredMethod != null)
{
return new MethodInformation(inferredMethod, 0);
}
else
{
return null;
}
}
private static MethodInfo Infer(MethodInfo genericMethod, Type[] typesOfMethodArguments, bool hasVarArgs)
{
Dbg.Assert(genericMethod != null, "Caller should verify that genericMethod != null");
Dbg.Assert(typesOfMethodArguments != null, "Caller should verify that arguments != null");
if (!genericMethod.ContainsGenericParameters)
{
return genericMethod;
}
Type[] typeParameters = genericMethod.GetGenericArguments();
Type[] typesOfMethodParameters = genericMethod.GetParameters().Select(static p => p.ParameterType).ToArray();
MethodInfo inferredMethod = Infer(genericMethod, typeParameters, typesOfMethodParameters, typesOfMethodArguments);
// normal inference failed, perhaps instead of inferring for
// M<T1,T2,T3>(T1, T2, ..., params T3 [])
// we can try to infer for this signature instead
// M<T1,T2,T3>)(T1, T2, ..., T3, T3, T3, T3)
// where T3 is repeated appropriate number of times depending on the number of actual method arguments.
if (inferredMethod == null &&
hasVarArgs &&
typesOfMethodArguments.Length >= (typesOfMethodParameters.Length - 1))
{
IEnumerable<Type> typeOfRegularParameters = typesOfMethodParameters.Take(typesOfMethodParameters.Length - 1);
IEnumerable<Type> multipliedVarArgsElementType = Enumerable.Repeat(
typesOfMethodParameters[typesOfMethodParameters.Length - 1].GetElementType(),
typesOfMethodArguments.Length - typesOfMethodParameters.Length + 1);
inferredMethod = Infer(
genericMethod,
typeParameters,
typeOfRegularParameters.Concat(multipliedVarArgsElementType),
typesOfMethodArguments);
}
return inferredMethod;
}
private static MethodInfo Infer(MethodInfo genericMethod, ICollection<Type> typeParameters, IEnumerable<Type> typesOfMethodParameters, IEnumerable<Type> typesOfMethodArguments)
{
Dbg.Assert(genericMethod != null, "Caller should verify that genericMethod != null");
Dbg.Assert(typeParameters != null, "Caller should verify that typeParameters != null");
Dbg.Assert(typesOfMethodParameters != null, "Caller should verify that typesOfMethodParameters != null");
Dbg.Assert(typesOfMethodArguments != null, "Caller should verify that typesOfMethodArguments != null");
using (s_tracer.TraceScope("Inferring type parameters for the following method: {0}", genericMethod))
{
if ((s_tracer.Options & PSTraceSourceOptions.WriteLine) == PSTraceSourceOptions.WriteLine)
{
s_tracer.WriteLine(
"Types of method arguments: {0}",
string.Join(", ", typesOfMethodArguments.Select(static t => t.ToString()).ToArray()));
}
var typeInference = new TypeInference(typeParameters);
if (!typeInference.UnifyMultipleTerms(typesOfMethodParameters, typesOfMethodArguments))
{
return null;
}
IEnumerable<Type> inferredTypeParameters = typeParameters.Select(typeInference.GetInferredType);
if (inferredTypeParameters.Any(static inferredType => inferredType == null))
{
return null;
}
try
{
MethodInfo instantiatedMethod = genericMethod.MakeGenericMethod(inferredTypeParameters.ToArray());
s_tracer.WriteLine("Inference succesful: {0}", instantiatedMethod);
return instantiatedMethod;
}
catch (ArgumentException e)
{
// Inference failure - most likely due to generic constraints being violated (i.e. where T: IEnumerable)
s_tracer.WriteLine("Inference failure: {0}", e.Message);
return null;
}
}
}
private readonly HashSet<Type>[] _typeParameterIndexToSetOfInferenceCandidates;
#if DEBUG
private readonly HashSet<Type> _typeParametersOfTheMethod;
#endif
internal TypeInference(ICollection<Type> typeParameters)
{
#if DEBUG
Dbg.Assert(typeParameters != null, "Caller should verify that typeParameters != null");
Dbg.Assert(
typeParameters.All(t => t.IsGenericParameter),
"Caller should verify that typeParameters are really generic type parameters");
#endif
_typeParameterIndexToSetOfInferenceCandidates = new HashSet<Type>[typeParameters.Count];
#if DEBUG
List<int> listOfTypeParameterPositions = typeParameters.Select(static t => t.GenericParameterPosition).ToList();
listOfTypeParameterPositions.Sort();
Dbg.Assert(
listOfTypeParameterPositions.Count == listOfTypeParameterPositions.Distinct().Count(),
"No type parameters should occupy the same position");
Dbg.Assert(
listOfTypeParameterPositions.All(p => p >= 0),
"Type parameter positions should be between 0 and #ofParams");
Dbg.Assert(
listOfTypeParameterPositions.All(p => p < _typeParameterIndexToSetOfInferenceCandidates.Length),
"Type parameter positions should be between 0 and #ofParams");
_typeParametersOfTheMethod = new HashSet<Type>();
foreach (Type t in typeParameters)
{
_typeParametersOfTheMethod.Add(t);
}
#endif
}
internal Type GetInferredType(Type typeParameter)
{
#if DEBUG
Dbg.Assert(typeParameter != null, "Caller should verify typeParameter != null");
Dbg.Assert(
_typeParametersOfTheMethod.Contains(typeParameter),
"Caller should verify that typeParameter is actually a generic type parameter of the method");
#endif
ICollection<Type> inferenceCandidates =
_typeParameterIndexToSetOfInferenceCandidates[typeParameter.GenericParameterPosition];
if ((inferenceCandidates != null) && (inferenceCandidates.Any(static t => t == typeof(LanguagePrimitives.Null))))
{
Type firstValueType = inferenceCandidates.FirstOrDefault(static t => t.IsValueType);
if (firstValueType != null)
{
s_tracer.WriteLine("Cannot reconcile null and {0} (a value type)", firstValueType);
inferenceCandidates = null;
_typeParameterIndexToSetOfInferenceCandidates[typeParameter.GenericParameterPosition] = null;
}
else
{
inferenceCandidates = inferenceCandidates.Where(static t => t != typeof(LanguagePrimitives.Null)).ToList();
if (inferenceCandidates.Count == 0)
{
inferenceCandidates = null;
_typeParameterIndexToSetOfInferenceCandidates[typeParameter.GenericParameterPosition] = null;
}
}
}
if ((inferenceCandidates != null) && (inferenceCandidates.Count > 1))
{
// "base class" assignability-wise (to account for interfaces)
Type commonBaseClass = inferenceCandidates.FirstOrDefault(
potentiallyCommonBaseClass =>
inferenceCandidates.All(
otherCandidate =>
otherCandidate == potentiallyCommonBaseClass ||
potentiallyCommonBaseClass.IsAssignableFrom(otherCandidate)));
if (commonBaseClass != null)
{
inferenceCandidates.Clear();
inferenceCandidates.Add(commonBaseClass);
}
else
{
s_tracer.WriteLine("Multiple unreconcilable inferences for type parameter {0}", typeParameter);
inferenceCandidates = null;
_typeParameterIndexToSetOfInferenceCandidates[typeParameter.GenericParameterPosition] = null;
}
}
if (inferenceCandidates == null)
{
s_tracer.WriteLine("Couldn't infer type parameter {0}", typeParameter);
return null;
}
else
{
Dbg.Assert(inferenceCandidates.Count == 1, "inferenceCandidates should contain exactly 1 element at this point");
return inferenceCandidates.Single();
}
}
internal bool UnifyMultipleTerms(IEnumerable<Type> parameterTypes, IEnumerable<Type> argumentTypes)
{
List<Type> leftList = parameterTypes.ToList();
List<Type> rightList = argumentTypes.ToList();
if (leftList.Count != rightList.Count)
{
s_tracer.WriteLine("Mismatch in number of parameters and arguments");
return false;
}
for (int i = 0; i < leftList.Count; i++)
{
if (!this.Unify(leftList[i], rightList[i]))
{
s_tracer.WriteLine("Couldn't unify {0} with {1}", leftList[i], rightList[i]);
return false;
}
}
return true;
}
private bool Unify(Type parameterType, Type argumentType)
{
if (!parameterType.ContainsGenericParameters)
{
return true;
}
if (parameterType.IsGenericParameter)
{
#if DEBUG
Dbg.Assert(
_typeParametersOfTheMethod.Contains(parameterType),
"Only uninstantiated generic type parameters encountered in real life, should be the ones coming from the method");
#endif
HashSet<Type> inferenceCandidates = _typeParameterIndexToSetOfInferenceCandidates[parameterType.GenericParameterPosition];
if (inferenceCandidates == null)
{
inferenceCandidates = new HashSet<Type>();
_typeParameterIndexToSetOfInferenceCandidates[parameterType.GenericParameterPosition] = inferenceCandidates;
}
inferenceCandidates.Add(argumentType);
s_tracer.WriteLine("Inferred {0} => {1}", parameterType, argumentType);
return true;
}
if (parameterType.IsArray)
{
if (argumentType == typeof(LanguagePrimitives.Null))
{
return true;
}
if (argumentType.IsArray && parameterType.GetArrayRank() == argumentType.GetArrayRank())
{
return this.Unify(parameterType.GetElementType(), argumentType.GetElementType());
}
s_tracer.WriteLine("Couldn't unify array {0} with {1}", parameterType, argumentType);
return false;
}
if (parameterType.IsByRef)
{
if (argumentType.IsGenericType && argumentType.GetGenericTypeDefinition() == typeof(PSReference<>))
{
Type referencedType = argumentType.GetGenericArguments()[0];
if (referencedType == typeof(LanguagePrimitives.Null))
{
return true;
}
else
{
return this.Unify(
parameterType.GetElementType(),
referencedType);
}
}
else
{
s_tracer.WriteLine("Couldn't unify reference type {0} with {1}", parameterType, argumentType);
return false;
}
}
if (parameterType.IsGenericType && parameterType.GetGenericTypeDefinition() == typeof(Nullable<>))
{
if (argumentType == typeof(LanguagePrimitives.Null))
{
return true;
}
return this.Unify(parameterType.GetGenericArguments()[0], argumentType);
}
if (parameterType.IsGenericType)
{
if (argumentType == typeof(LanguagePrimitives.Null))
{
return true;
}
return this.UnifyConstructedType(parameterType, argumentType);
}
Dbg.Assert(false, "Unrecognized kind of type");
s_tracer.WriteLine("Unrecognized kind of type: {0}", parameterType);
return false;
}
private bool UnifyConstructedType(Type parameterType, Type argumentType)
{
Dbg.Assert(parameterType.IsGenericType, "Caller should verify parameterType.IsGenericType before calling this method");
if (IsEqualGenericTypeDefinition(parameterType, argumentType))
{
IEnumerable<Type> typeParametersOfParameterType = parameterType.GetGenericArguments();
IEnumerable<Type> typeArgumentsOfArgumentType = argumentType.GetGenericArguments();
return this.UnifyMultipleTerms(typeParametersOfParameterType, typeArgumentsOfArgumentType);
}
Type[] interfaces = argumentType.GetInterfaces();
for (int i = 0; i < interfaces.Length; i++)
{
if (IsEqualGenericTypeDefinition(parameterType, interfaces[i]))
{
return UnifyConstructedType(parameterType, interfaces[i]);
}
}
Type baseType = argumentType.BaseType;
while (baseType != null)
{
if (IsEqualGenericTypeDefinition(parameterType, baseType))
{
return UnifyConstructedType(parameterType, baseType);
}
baseType = baseType.BaseType;
}
s_tracer.WriteLine("Attempt to unify different constructed types: {0} and {1}", parameterType, argumentType);
return false;
}
private static bool IsEqualGenericTypeDefinition(Type parameterType, Type argumentType)
{
Dbg.Assert(parameterType.IsGenericType, "Caller should verify parameterType.IsGenericType before calling this method");
if (!argumentType.IsGenericType)
{
return false;
}
return parameterType.GetGenericTypeDefinition() == argumentType.GetGenericTypeDefinition();
}
}
}