Skip to content
8 changes: 6 additions & 2 deletions src/System.Management.Automation/engine/CoreAdapter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2353,6 +2353,10 @@ private static readonly Dictionary<Type, Dictionary<string, EventCacheEntry>> s_
internal class MethodCacheEntry
{
internal 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)
{
Expand Down Expand Up @@ -3392,7 +3396,7 @@ internal T GetDotNetMethod<T>(object obj, string methodName) where T : PSMemberI
break;
}
}
return new PSMethod(methods[0].method.Name, this, obj, methods, isSpecial, isHidden) as T;
return PSMethod.Create(methods[0].method.Name, this, obj, methods, isSpecial, isHidden) as T;
}

internal void AddAllProperties<T>(object obj, PSMemberInfoInternalCollection<T> members, bool ignoreDuplicates) where T : PSMemberInfo
Expand Down Expand Up @@ -3468,7 +3472,7 @@ internal void AddAllMethods<T>(object obj, PSMemberInfoInternalCollection<T> mem
break;
}
}
members.Add(new PSMethod(name, this, obj, method, isSpecial, isHidden) as T);
members.Add(PSMethod.Create(name, this, obj, method, isSpecial, isHidden) as T);
}
}
}
Expand Down
114 changes: 114 additions & 0 deletions src/System.Management.Automation/engine/LanguagePrimitives.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3212,6 +3212,57 @@ private static Delegate ConvertScriptBlockToDelegate(object valueToConvert,
valueToConvert.ToString(), resultType.ToString(), exception.Message);
}


Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please remove extra line.

Copy link
Collaborator

@iSazonov iSazonov Nov 14, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please remove extra line.

private static Delegate ConvertPSMethodInfoToDelegate(object valueToConvert,
Type resultType,
bool recurse,
PSObject originalValueToConvert,
IFormatProvider formatProvider,
TypeTable backupTable)
{
PSMethod psMethod;
try
{
psMethod = (PSMethod) valueToConvert;

var maybeType = psMethod.instance as Type;
var methodInfoCandiates = maybeType != null
? maybeType.GetMethods(BindingFlags.Static | BindingFlags.Public)
: psMethod.instance.GetType().GetMethods(BindingFlags.Instance | BindingFlags.Public);

var targetMethodInfo = resultType.GetMethod("Invoke");

var comparator = new DelegateArgsComparator(targetMethodInfo);

foreach (var candidate in methodInfoCandiates)
{
if (candidate.Name != psMethod.Name)
{
continue;
}
if (comparator.SignatureMatches(candidate.ReturnType, candidate.GetParameters()))
{
return maybeType != null
? candidate.CreateDelegate(resultType)
: candidate.CreateDelegate(resultType, psMethod.instance);
}
}
}
catch (Exception e)
{
typeConversion.WriteLine("PSMethod to Delegate exception: \"{0}\".", e.Message);
throw new PSInvalidCastException("InvalidCastExceptionPSMethodToDelegate", e,
ExtendedTypeSystem.InvalidCastExceptionWithInnerException,
valueToConvert.ToString(), resultType.ToString(), e.Message);
}

var msg = String.Format(ExtendedTypeSystem.PSMethodToDelegateNoMatchingOverLoad, psMethod, resultType);
typeConversion.WriteLine($"PSMethod to Delegate exception: \"{msg}\".");
throw new PSInvalidCastException("InvalidCastExceptionPSMethodToDelegate", null,
ExtendedTypeSystem.InvalidCastExceptionWithInnerException,
valueToConvert.ToString(), resultType.ToString(), msg);
}

private static object ConvertToNullable(object valueToConvert,
Type resultType,
bool recursion,
Expand Down Expand Up @@ -4004,6 +4055,7 @@ private static object ConvertNotSupportedConversion(object valueToConvert,
return null;
}

[System.Diagnostics.DebuggerDisplay("{from.Name}->{to.Name}")]
private struct ConversionTypePair
{
internal Type from;
Expand Down Expand Up @@ -4057,6 +4109,7 @@ object Invoke(object valueToConvert,
TypeTable backupTable);
}

[System.Diagnostics.DebuggerDisplay("{_converter.Method.Name}")]
internal class ConversionData<T> : ConversionData
{
private readonly PSConverter<T> _converter;
Expand Down Expand Up @@ -4739,9 +4792,70 @@ private static ConversionData FigureLanguageConversion(Type fromType, Type toTyp
return CacheConversion<object>(fromType, toType, LanguagePrimitives.ConvertIntegerToEnum, ConversionRank.Language);
}

if (fromType.IsSubclassOf(typeof(PSMethod)) && toType.IsSubclassOf(typeof(Delegate)))
{
var mi = toType.GetMethod("Invoke");

var comparator = new DelegateArgsComparator(mi);
var signatureEnumerator = new PSMethodSignatureEnumerator(fromType);
while (signatureEnumerator.MoveNext())
{
var candidate = signatureEnumerator.Current.GetMethod("Invoke");

if (comparator.SignatureMatches(candidate.ReturnType, candidate.GetParameters()))
{
return CacheConversion<Delegate>(fromType, toType, LanguagePrimitives.ConvertPSMethodInfoToDelegate, ConversionRank.Language);
}
}
}

return null;
}

struct DelegateArgsComparator
{
private readonly ParameterInfo[] _targetParametersInfos;
private readonly Type _returnType;

public DelegateArgsComparator(MethodInfo targetMethodInfo)
{
_returnType = targetMethodInfo.ReturnType;
_targetParametersInfos = targetMethodInfo.GetParameters();
}

public bool SignatureMatches(Type returnType, ParameterInfo[] arguments)
{
return ReturnTypeMatches(returnType) && ParameterTypesMatches(arguments);
}

private bool ReturnTypeMatches(Type returnType)
{
return PSMethod.MatchesPSMethodProjectedType(_returnType, returnType, testAssignment: true);
}

private bool ParameterTypesMatches(ParameterInfo[] arguments)
{
var argsCount = _targetParametersInfos.Length;
// void is encoded as typeof(Unit) in the PSMethod<MethodGroup<>> as the last parameter
if (arguments.Length != argsCount)
{
return false;
}
for (int i = 0; i < arguments.Length; i++)
{
var arg = arguments[i];
var argType = arg.ParameterType;
var targetParamType = _targetParametersInfos[i].ParameterType;
var isOut = (arg.Attributes | ParameterAttributes.Out) == ParameterAttributes.Out;
if (!PSMethod.MatchesPSMethodProjectedType(targetParamType, argType, isOut: isOut))
{
return false;
}
}
return true;
}
}

private static PSConverter<object> FigureStaticCreateMethodConversion(Type fromType, Type toType)
{
// after discussing this with Jason, we decided that for now we only want to support string->CimSession conversion
Expand Down
Loading