Improve PSMethod-to-Delegate conversion (#6570)

Two small improvements:
1. Avoid unnecessary reflection in `ConvertPSMethodInfoToDelegate`. `PSMethod` already has the `MethodInfo` information with its `adapterData` field.
2. Avoid creating the generic `PSMethod<>` type for `PSMethod` that represents constructors, because constructors cannot be converted to a delegate anyways. In case that the `PSMethod` represents constructors, we use a simple `PSMethod` instance instead.

Also, rename the type `Unit` to `VOID` to make it more readable, since that type represents `typeof(void)`.
This commit is contained in:
Dongbo Wang
2018-04-06 17:01:04 -07:00
committed by GitHub
parent 46fa2260a8
commit 48be625379
3 changed files with 33 additions and 41 deletions
@@ -2345,7 +2345,7 @@ namespace System.Management.Automation
/// <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 Func<string, DotNetAdapter, object, DotNetAdapter.MethodCacheEntry, bool, bool, PSMethod> PSMethodCtor;
internal MethodCacheEntry(MethodBase[] methods)
{
@@ -11,6 +11,7 @@ using System.Linq;
using System.Linq.Expressions;
using System.Management.Automation.Language;
using System.Reflection;
using System.Reflection.Emit;
using System.Runtime.CompilerServices;
using System.Text.RegularExpressions;
using System.Xml;
@@ -20,7 +21,7 @@ using System.Management.Automation.Internal;
using System.Management.Automation.Runspaces;
using System.Diagnostics.CodeAnalysis; // for fxcop
using Dbg = System.Management.Automation.Diagnostics;
using System.Reflection.Emit;
using MethodCacheEntry = System.Management.Automation.DotNetAdapter.MethodCacheEntry;
#if !CORECLR
// System.DirectoryServices are not in CoreCLR
@@ -3210,31 +3211,24 @@ namespace System.Management.Automation
IFormatProvider formatProvider,
TypeTable backupTable)
{
PSMethod psMethod;
// We can only possibly convert PSMethod instance of the type PSMethod<T>.
// Such a PSMethod essentially represents a set of .NET method overloads.
var psMethod = (PSMethod)valueToConvert;
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 methods = (MethodCacheEntry)psMethod.adapterData;
var isStatic = psMethod.instance is Type;
var targetMethodInfo = resultType.GetMethod("Invoke");
var comparator = new DelegateArgsComparator(targetMethodInfo);
foreach (var candidate in methodInfoCandiates)
foreach (var methodInformation in methods.methodInformationStructures)
{
if (candidate.Name != psMethod.Name)
{
continue;
}
var candidate = (MethodInfo)methodInformation.method;
if (comparator.SignatureMatches(candidate.ReturnType, candidate.GetParameters()))
{
return maybeType != null
? candidate.CreateDelegate(resultType)
: candidate.CreateDelegate(resultType, psMethod.instance);
return isStatic ? candidate.CreateDelegate(resultType)
: candidate.CreateDelegate(resultType, psMethod.instance);
}
}
}
@@ -3246,12 +3240,12 @@ namespace System.Management.Automation
valueToConvert.ToString(), resultType.ToString(), e.Message);
}
var msg = String.Format(ExtendedTypeSystem.PSMethodToDelegateNoMatchingOverLoad, psMethod, resultType);
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,
@@ -4789,7 +4783,6 @@ namespace System.Management.Automation
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);
@@ -4824,7 +4817,7 @@ namespace System.Management.Automation
private bool ParameterTypesMatches(ParameterInfo[] arguments)
{
var argsCount = _targetParametersInfos.Length;
// void is encoded as typeof(Unit) in the PSMethod<MethodGroup<>> as the last parameter
// void is encoded as typeof(VOID) in the PSMethod<MethodGroup<>> as the last parameter
if (arguments.Length != argsCount)
{
return false;
@@ -2642,11 +2642,17 @@ namespace System.Management.Automation
internal static PSMethod Create(string name, DotNetAdapter dotNetInstanceAdapter, object baseObject, DotNetAdapter.MethodCacheEntry method, bool isSpecial, bool isHidden)
{
if (method.psmethodCtor == null)
if (method[0].method is ConstructorInfo)
{
method.psmethodCtor = CreatePSMethodConstructor(method.methodInformationStructures);
// Constructor cannot be converted to a delegate, so just return a simple PSMethod instance
return new PSMethod(name, dotNetInstanceAdapter, baseObject, method, isSpecial, isHidden);
}
return method.psmethodCtor.Invoke(name, dotNetInstanceAdapter, baseObject, method, isSpecial, isHidden);
if (method.PSMethodCtor == null)
{
method.PSMethodCtor = CreatePSMethodConstructor(method.methodInformationStructures);
}
return method.PSMethodCtor.Invoke(name, dotNetInstanceAdapter, baseObject, method, isSpecial, isHidden);
}
static Type GetMethodGroupType(MethodInfo methodInfo)
@@ -2697,7 +2703,7 @@ namespace System.Management.Automation
{
if (type == typeof(void))
{
return typeof(Unit);
return typeof(VOID);
}
if (type == typeof(TypedReference))
{
@@ -2742,7 +2748,7 @@ namespace System.Management.Automation
sourceType = sourceType.GenericTypeArguments[0];
}
if (targetType == typeof(void) && sourceType == typeof(Unit))
if (targetType == typeof(void) && sourceType == typeof(VOID))
{
return true;
}
@@ -2783,21 +2789,14 @@ namespace System.Management.Automation
private static Func<string, DotNetAdapter, object, object, bool, bool, PSMethod> CreatePSMethodConstructor(MethodInformation[] methods)
{
// Produce the PSMethod creator for MethodInfo objects
var types = new Type[methods.Length];
for (int i = 0; i < methods.Length; i++)
{
var mb = methods[i].method;
if (mb is MethodInfo mi)
{
types[i] = GetMethodGroupType(mi);
}
else
{
types[i] = typeof(Unit);
}
types[i] = GetMethodGroupType((MethodInfo)methods[i].method);
}
var methodGroupType = CreateMethodGroup(types, 0, types.Length);
var methodGroupType = CreateMethodGroup(types, 0, types.Length);
Type psMethodType = typeof(PSMethod<>).MakeGenericType(methodGroupType);
var delegateType = typeof(Func<string, DotNetAdapter, object, object, bool, bool, PSMethod>);
return (Func<string, DotNetAdapter, object, object, bool, bool, PSMethod>)Delegate.CreateDelegate(delegateType, psMethodType.GetMethod("Create", BindingFlags.NonPublic|BindingFlags.Static));
@@ -2946,9 +2945,9 @@ namespace System.Management.Automation
internal class MethodGroup<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16> : MethodGroup { }
internal class MethodGroup<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28, T29, T30, T31, T32> : MethodGroup { }
class Unit
class VOID
{
private Unit() { }
private VOID() { }
}
internal struct PSMethodSignatureEnumerator : IEnumerator<Type>