Skip to content

Commit ceba29f

Browse files
authored
Optimize generic MethodInfo for Func<T1, T2, TResult> (#4838)
1 parent 820f1e2 commit ceba29f

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

43 files changed

+800
-162
lines changed

src/Microsoft.ML.Core/EntryPoints/EntryPointUtils.cs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,9 @@ namespace Microsoft.ML.EntryPoints
1414
[BestFriend]
1515
internal static class EntryPointUtils
1616
{
17+
private static readonly FuncStaticMethodInfo1<TlcModule.RangeAttribute, object, bool> _isValueWithinRangeMethodInfo
18+
= new FuncStaticMethodInfo1<TlcModule.RangeAttribute, object, bool>(IsValueWithinRange<int>);
19+
1720
private static bool IsValueWithinRange<T>(TlcModule.RangeAttribute range, object obj)
1821
{
1922
T val;
@@ -33,13 +36,12 @@ public static bool IsValueWithinRange(this TlcModule.RangeAttribute range, objec
3336
{
3437
Contracts.AssertValue(range);
3538
Contracts.AssertValue(val);
36-
Func<TlcModule.RangeAttribute, object, bool> fn = IsValueWithinRange<int>;
3739
// Avoid trying to cast double as float. If range
3840
// was specified using floats, but value being checked
3941
// is double, change range to be of type double
4042
if (range.Type == typeof(float) && val is double)
4143
range.CastToDouble();
42-
return Utils.MarshalInvoke(fn, range.Type, range, val);
44+
return Utils.MarshalInvoke(_isValueWithinRangeMethodInfo, range.Type, range, val);
4345
}
4446

4547
/// <summary>
Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
// Licensed to the .NET Foundation under one or more agreements.
2+
// The .NET Foundation licenses this file to you under the MIT license.
3+
// See the LICENSE file in the project root for more information.
4+
5+
#nullable enable
6+
7+
using System;
8+
using System.Linq.Expressions;
9+
using System.Reflection;
10+
using Microsoft.ML.Runtime;
11+
12+
namespace Microsoft.ML.Internal.Utilities
13+
{
14+
/// <summary>
15+
/// Represents the <see cref="MethodInfo"/> for a generic function corresponding to <see cref="Func{T1, T2, TResult}"/>,
16+
/// with the following characteristics:
17+
///
18+
/// <list type="bullet">
19+
/// <item><description>The method is an instance method on an object of type <typeparamref name="TTarget"/>.</description></item>
20+
/// <item><description>One generic type argument.</description></item>
21+
/// <item><description>A return value of <typeparamref name="TResult"/>.</description></item>
22+
/// </list>
23+
/// </summary>
24+
/// <typeparam name="TTarget">The type of the receiver of the instance method.</typeparam>
25+
/// <typeparam name="T1">The type of the first parameter of the method.</typeparam>
26+
/// <typeparam name="T2">The type of the second parameter of the method.</typeparam>
27+
/// <typeparam name="TResult">The type of the return value of the method.</typeparam>
28+
internal sealed class FuncInstanceMethodInfo1<TTarget, T1, T2, TResult> : FuncMethodInfo1<T1, T2, TResult>
29+
where TTarget : class
30+
{
31+
private static readonly string _targetTypeCheckMessage = $"Should have a target type of '{typeof(TTarget)}'";
32+
33+
public FuncInstanceMethodInfo1(Func<T1, T2, TResult> function)
34+
: this(function.Method)
35+
{
36+
}
37+
38+
private FuncInstanceMethodInfo1(MethodInfo methodInfo)
39+
: base(methodInfo)
40+
{
41+
Contracts.CheckParam(!GenericMethodDefinition.IsStatic, nameof(methodInfo), "Should be an instance method");
42+
Contracts.CheckParam(GenericMethodDefinition.DeclaringType == typeof(TTarget), nameof(methodInfo), _targetTypeCheckMessage);
43+
}
44+
45+
/// <summary>
46+
/// Creates a <see cref="FuncInstanceMethodInfo1{TTarget, T1, T2, TResult}"/> representing the <see cref="MethodInfo"/>
47+
/// for a generic instance method. This helper method allows the instance to be created prior to the creation of
48+
/// any instances of the target type. The following example shows the creation of an instance representing the
49+
/// <see cref="object.Equals(object)"/> method:
50+
///
51+
/// <code>
52+
/// FuncInstanceMethodInfo1&lt;object, object, int&gt;.Create(obj => obj.Equals)
53+
/// </code>
54+
/// </summary>
55+
/// <param name="expression">The expression which creates the delegate for an instance of the target type.</param>
56+
/// <returns>A <see cref="FuncInstanceMethodInfo1{TTarget, T1, T2, TResult}"/> representing the <see cref="MethodInfo"/>
57+
/// for the generic instance method.</returns>
58+
public static FuncInstanceMethodInfo1<TTarget, T1, T2, TResult> Create(Expression<Func<TTarget, Func<T1, T2, TResult>>> expression)
59+
{
60+
if (!(expression is { Body: UnaryExpression { Operand: MethodCallExpression methodCallExpression } }))
61+
{
62+
throw Contracts.ExceptParam(nameof(expression), "Unexpected expression form");
63+
}
64+
65+
// Verify that we are calling MethodInfo.CreateDelegate(Type, object)
66+
Contracts.CheckParam(methodCallExpression.Method.DeclaringType == typeof(MethodInfo), nameof(expression), "Unexpected expression form");
67+
Contracts.CheckParam(methodCallExpression.Method.Name == nameof(MethodInfo.CreateDelegate), nameof(expression), "Unexpected expression form");
68+
Contracts.CheckParam(methodCallExpression.Method.GetParameters().Length == 2, nameof(expression), "Unexpected expression form");
69+
Contracts.CheckParam(methodCallExpression.Method.GetParameters()[0].ParameterType == typeof(Type), nameof(expression), "Unexpected expression form");
70+
Contracts.CheckParam(methodCallExpression.Method.GetParameters()[1].ParameterType == typeof(object), nameof(expression), "Unexpected expression form");
71+
72+
// Verify that we are creating a delegate of type Func<T, TResult>
73+
Contracts.CheckParam(methodCallExpression.Arguments.Count == 2, nameof(expression), "Unexpected expression form");
74+
Contracts.CheckParam(methodCallExpression.Arguments[0] is ConstantExpression, nameof(expression), "Unexpected expression form");
75+
Contracts.CheckParam(((ConstantExpression)methodCallExpression.Arguments[0]).Type == typeof(Type), nameof(expression), "Unexpected expression form");
76+
Contracts.CheckParam((Type)((ConstantExpression)methodCallExpression.Arguments[0]).Value == typeof(Func<T1, T2, TResult>), nameof(expression), "Unexpected expression form");
77+
Contracts.CheckParam(methodCallExpression.Arguments[1] is ParameterExpression, nameof(expression), "Unexpected expression form");
78+
Contracts.CheckParam(methodCallExpression.Arguments[1] == expression.Parameters[0], nameof(expression), "Unexpected expression form");
79+
80+
// Check the MethodInfo
81+
Contracts.CheckParam(methodCallExpression.Object is ConstantExpression, nameof(expression), "Unexpected expression form");
82+
Contracts.CheckParam(((ConstantExpression)methodCallExpression.Object).Type == typeof(MethodInfo), nameof(expression), "Unexpected expression form");
83+
84+
var methodInfo = (MethodInfo)((ConstantExpression)methodCallExpression.Object).Value;
85+
Contracts.CheckParam(expression.Body is UnaryExpression, nameof(expression), "Unexpected expression form");
86+
Contracts.CheckParam(((UnaryExpression)expression.Body).Operand is MethodCallExpression, nameof(expression), "Unexpected expression form");
87+
88+
return new FuncInstanceMethodInfo1<TTarget, T1, T2, TResult>(methodInfo);
89+
}
90+
}
91+
}
Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
// Licensed to the .NET Foundation under one or more agreements.
2+
// The .NET Foundation licenses this file to you under the MIT license.
3+
// See the LICENSE file in the project root for more information.
4+
5+
#nullable enable
6+
7+
using System;
8+
using System.Linq.Expressions;
9+
using System.Reflection;
10+
using Microsoft.ML.Runtime;
11+
12+
namespace Microsoft.ML.Internal.Utilities
13+
{
14+
/// <summary>
15+
/// Represents the <see cref="MethodInfo"/> for a generic function corresponding to <see cref="Func{T1, T2, TResult}"/>,
16+
/// with the following characteristics:
17+
///
18+
/// <list type="bullet">
19+
/// <item><description>The method is an instance method on an object of type <typeparamref name="TTarget"/>.</description></item>
20+
/// <item><description>Two generic type arguments.</description></item>
21+
/// <item><description>A return value of <typeparamref name="TResult"/>.</description></item>
22+
/// </list>
23+
/// </summary>
24+
/// <typeparam name="TTarget">The type of the receiver of the instance method.</typeparam>
25+
/// <typeparam name="T1">The type of the first parameter of the method.</typeparam>
26+
/// <typeparam name="T2">The type of the second parameter of the method.</typeparam>
27+
/// <typeparam name="TResult">The type of the return value of the method.</typeparam>
28+
internal sealed class FuncInstanceMethodInfo2<TTarget, T1, T2, TResult> : FuncMethodInfo2<T1, T2, TResult>
29+
where TTarget : class
30+
{
31+
private static readonly string _targetTypeCheckMessage = $"Should have a target type of '{typeof(TTarget)}'";
32+
33+
public FuncInstanceMethodInfo2(Func<T1, T2, TResult> function)
34+
: this(function.Method)
35+
{
36+
}
37+
38+
private FuncInstanceMethodInfo2(MethodInfo methodInfo)
39+
: base(methodInfo)
40+
{
41+
Contracts.CheckParam(!GenericMethodDefinition.IsStatic, nameof(methodInfo), "Should be an instance method");
42+
Contracts.CheckParam(GenericMethodDefinition.DeclaringType == typeof(TTarget), nameof(methodInfo), _targetTypeCheckMessage);
43+
}
44+
45+
/// <summary>
46+
/// Creates a <see cref="FuncInstanceMethodInfo1{TTarget, T1, T2, TResult}"/> representing the <see cref="MethodInfo"/>
47+
/// for a generic instance method. This helper method allows the instance to be created prior to the creation of
48+
/// any instances of the target type. The following example shows the creation of an instance representing the
49+
/// <see cref="object.Equals(object)"/> method:
50+
///
51+
/// <code>
52+
/// FuncInstanceMethodInfo1&lt;object, object, int&gt;.Create(obj => obj.Equals)
53+
/// </code>
54+
/// </summary>
55+
/// <param name="expression">The expression which creates the delegate for an instance of the target type.</param>
56+
/// <returns>A <see cref="FuncInstanceMethodInfo1{TTarget, T1, T2, TResult}"/> representing the <see cref="MethodInfo"/>
57+
/// for the generic instance method.</returns>
58+
public static FuncInstanceMethodInfo2<TTarget, T1, T2, TResult> Create(Expression<Func<TTarget, Func<T1, T2, TResult>>> expression)
59+
{
60+
if (!(expression is { Body: UnaryExpression { Operand: MethodCallExpression methodCallExpression } }))
61+
{
62+
throw Contracts.ExceptParam(nameof(expression), "Unexpected expression form");
63+
}
64+
65+
// Verify that we are calling MethodInfo.CreateDelegate(Type, object)
66+
Contracts.CheckParam(methodCallExpression.Method.DeclaringType == typeof(MethodInfo), nameof(expression), "Unexpected expression form");
67+
Contracts.CheckParam(methodCallExpression.Method.Name == nameof(MethodInfo.CreateDelegate), nameof(expression), "Unexpected expression form");
68+
Contracts.CheckParam(methodCallExpression.Method.GetParameters().Length == 2, nameof(expression), "Unexpected expression form");
69+
Contracts.CheckParam(methodCallExpression.Method.GetParameters()[0].ParameterType == typeof(Type), nameof(expression), "Unexpected expression form");
70+
Contracts.CheckParam(methodCallExpression.Method.GetParameters()[1].ParameterType == typeof(object), nameof(expression), "Unexpected expression form");
71+
72+
// Verify that we are creating a delegate of type Func<T, TResult>
73+
Contracts.CheckParam(methodCallExpression.Arguments.Count == 2, nameof(expression), "Unexpected expression form");
74+
Contracts.CheckParam(methodCallExpression.Arguments[0] is ConstantExpression, nameof(expression), "Unexpected expression form");
75+
Contracts.CheckParam(((ConstantExpression)methodCallExpression.Arguments[0]).Type == typeof(Type), nameof(expression), "Unexpected expression form");
76+
Contracts.CheckParam((Type)((ConstantExpression)methodCallExpression.Arguments[0]).Value == typeof(Func<T1, T2, TResult>), nameof(expression), "Unexpected expression form");
77+
Contracts.CheckParam(methodCallExpression.Arguments[1] is ParameterExpression, nameof(expression), "Unexpected expression form");
78+
Contracts.CheckParam(methodCallExpression.Arguments[1] == expression.Parameters[0], nameof(expression), "Unexpected expression form");
79+
80+
// Check the MethodInfo
81+
Contracts.CheckParam(methodCallExpression.Object is ConstantExpression, nameof(expression), "Unexpected expression form");
82+
Contracts.CheckParam(((ConstantExpression)methodCallExpression.Object).Type == typeof(MethodInfo), nameof(expression), "Unexpected expression form");
83+
84+
var methodInfo = (MethodInfo)((ConstantExpression)methodCallExpression.Object).Value;
85+
Contracts.CheckParam(expression.Body is UnaryExpression, nameof(expression), "Unexpected expression form");
86+
Contracts.CheckParam(((UnaryExpression)expression.Body).Operand is MethodCallExpression, nameof(expression), "Unexpected expression form");
87+
88+
return new FuncInstanceMethodInfo2<TTarget, T1, T2, TResult>(methodInfo);
89+
}
90+
}
91+
}
Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
// Licensed to the .NET Foundation under one or more agreements.
2+
// The .NET Foundation licenses this file to you under the MIT license.
3+
// See the LICENSE file in the project root for more information.
4+
5+
#nullable enable
6+
7+
using System;
8+
using System.Linq.Expressions;
9+
using System.Reflection;
10+
using Microsoft.ML.Runtime;
11+
12+
namespace Microsoft.ML.Internal.Utilities
13+
{
14+
/// <summary>
15+
/// Represents the <see cref="MethodInfo"/> for a generic function corresponding to <see cref="Func{T1, T2, TResult}"/>,
16+
/// with the following characteristics:
17+
///
18+
/// <list type="bullet">
19+
/// <item><description>The method is an instance method on an object of type <typeparamref name="TTarget"/>.</description></item>
20+
/// <item><description>Three generic type arguments.</description></item>
21+
/// <item><description>A return value of <typeparamref name="TResult"/>.</description></item>
22+
/// </list>
23+
/// </summary>
24+
/// <typeparam name="TTarget">The type of the receiver of the instance method.</typeparam>
25+
/// <typeparam name="T1">The type of the first parameter of the method.</typeparam>
26+
/// <typeparam name="T2">The type of the second parameter of the method.</typeparam>
27+
/// <typeparam name="TResult">The type of the return value of the method.</typeparam>
28+
internal sealed class FuncInstanceMethodInfo3<TTarget, T1, T2, TResult> : FuncMethodInfo3<T1, T2, TResult>
29+
where TTarget : class
30+
{
31+
private static readonly string _targetTypeCheckMessage = $"Should have a target type of '{typeof(TTarget)}'";
32+
33+
public FuncInstanceMethodInfo3(Func<T1, T2, TResult> function)
34+
: this(function.Method)
35+
{
36+
}
37+
38+
private FuncInstanceMethodInfo3(MethodInfo methodInfo)
39+
: base(methodInfo)
40+
{
41+
Contracts.CheckParam(!GenericMethodDefinition.IsStatic, nameof(methodInfo), "Should be an instance method");
42+
Contracts.CheckParam(GenericMethodDefinition.DeclaringType == typeof(TTarget), nameof(methodInfo), _targetTypeCheckMessage);
43+
}
44+
45+
/// <summary>
46+
/// Creates a <see cref="FuncInstanceMethodInfo1{TTarget, T1, T2, TResult}"/> representing the <see cref="MethodInfo"/>
47+
/// for a generic instance method. This helper method allows the instance to be created prior to the creation of
48+
/// any instances of the target type. The following example shows the creation of an instance representing the
49+
/// <see cref="object.Equals(object)"/> method:
50+
///
51+
/// <code>
52+
/// FuncInstanceMethodInfo1&lt;object, object, int&gt;.Create(obj => obj.Equals)
53+
/// </code>
54+
/// </summary>
55+
/// <param name="expression">The expression which creates the delegate for an instance of the target type.</param>
56+
/// <returns>A <see cref="FuncInstanceMethodInfo1{TTarget, T1, T2, TResult}"/> representing the <see cref="MethodInfo"/>
57+
/// for the generic instance method.</returns>
58+
public static FuncInstanceMethodInfo3<TTarget, T1, T2, TResult> Create(Expression<Func<TTarget, Func<T1, T2, TResult>>> expression)
59+
{
60+
if (!(expression is { Body: UnaryExpression { Operand: MethodCallExpression methodCallExpression } }))
61+
{
62+
throw Contracts.ExceptParam(nameof(expression), "Unexpected expression form");
63+
}
64+
65+
// Verify that we are calling MethodInfo.CreateDelegate(Type, object)
66+
Contracts.CheckParam(methodCallExpression.Method.DeclaringType == typeof(MethodInfo), nameof(expression), "Unexpected expression form");
67+
Contracts.CheckParam(methodCallExpression.Method.Name == nameof(MethodInfo.CreateDelegate), nameof(expression), "Unexpected expression form");
68+
Contracts.CheckParam(methodCallExpression.Method.GetParameters().Length == 2, nameof(expression), "Unexpected expression form");
69+
Contracts.CheckParam(methodCallExpression.Method.GetParameters()[0].ParameterType == typeof(Type), nameof(expression), "Unexpected expression form");
70+
Contracts.CheckParam(methodCallExpression.Method.GetParameters()[1].ParameterType == typeof(object), nameof(expression), "Unexpected expression form");
71+
72+
// Verify that we are creating a delegate of type Func<T, TResult>
73+
Contracts.CheckParam(methodCallExpression.Arguments.Count == 2, nameof(expression), "Unexpected expression form");
74+
Contracts.CheckParam(methodCallExpression.Arguments[0] is ConstantExpression, nameof(expression), "Unexpected expression form");
75+
Contracts.CheckParam(((ConstantExpression)methodCallExpression.Arguments[0]).Type == typeof(Type), nameof(expression), "Unexpected expression form");
76+
Contracts.CheckParam((Type)((ConstantExpression)methodCallExpression.Arguments[0]).Value == typeof(Func<T1, T2, TResult>), nameof(expression), "Unexpected expression form");
77+
Contracts.CheckParam(methodCallExpression.Arguments[1] is ParameterExpression, nameof(expression), "Unexpected expression form");
78+
Contracts.CheckParam(methodCallExpression.Arguments[1] == expression.Parameters[0], nameof(expression), "Unexpected expression form");
79+
80+
// Check the MethodInfo
81+
Contracts.CheckParam(methodCallExpression.Object is ConstantExpression, nameof(expression), "Unexpected expression form");
82+
Contracts.CheckParam(((ConstantExpression)methodCallExpression.Object).Type == typeof(MethodInfo), nameof(expression), "Unexpected expression form");
83+
84+
var methodInfo = (MethodInfo)((ConstantExpression)methodCallExpression.Object).Value;
85+
Contracts.CheckParam(expression.Body is UnaryExpression, nameof(expression), "Unexpected expression form");
86+
Contracts.CheckParam(((UnaryExpression)expression.Body).Operand is MethodCallExpression, nameof(expression), "Unexpected expression form");
87+
88+
return new FuncInstanceMethodInfo3<TTarget, T1, T2, TResult>(methodInfo);
89+
}
90+
}
91+
}

0 commit comments

Comments
 (0)