Skip to content

Commit a1f7a5c

Browse files
committed
Enable IL for testing; improve perf for ByRef param validation
1 parent 40eb391 commit a1f7a5c

File tree

11 files changed

+234
-175
lines changed

11 files changed

+234
-175
lines changed

src/coreclr/System.Private.CoreLib/src/System/Reflection/Emit/DynamicMethod.cs

+2-16
Original file line numberDiff line numberDiff line change
@@ -579,23 +579,9 @@ Signature LazyCreateSignature()
579579

580580
[DebuggerHidden]
581581
[DebuggerStepThrough]
582-
internal unsafe object? InvokeNonEmitUnsafe(object? obj, IntPtr* arguments, BindingFlags invokeAttr)
582+
internal unsafe object? InvokeNonEmitUnsafe(object? obj, IntPtr* arguments)
583583
{
584-
if ((invokeAttr & BindingFlags.DoNotWrapExceptions) == 0)
585-
{
586-
try
587-
{
588-
return RuntimeMethodHandle.InvokeMethod(obj, (void**)arguments, Signature, isConstructor: false);
589-
}
590-
catch (Exception e)
591-
{
592-
throw new TargetInvocationException(e);
593-
}
594-
}
595-
else
596-
{
597-
return RuntimeMethodHandle.InvokeMethod(obj, (void**)arguments, Signature, isConstructor: false);
598-
}
584+
return RuntimeMethodHandle.InvokeMethod(obj, (void**)arguments, Signature, isConstructor: false);
599585
}
600586

601587
public override object[] GetCustomAttributes(Type attributeType, bool inherit)

src/coreclr/System.Private.CoreLib/src/System/Reflection/Emit/DynamicMethodInvoker.cs

+17-5
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,6 @@
11
// Licensed to the .NET Foundation under one or more agreements.
22
// The .NET Foundation licenses this file to you under the MIT license.
33

4-
using System.Diagnostics;
5-
using System.Runtime.CompilerServices;
6-
74
namespace System.Reflection.Emit
85
{
96
internal sealed partial class DynamicMethodInvoker
@@ -17,8 +14,23 @@ public DynamicMethodInvoker(DynamicMethod dynamicMethod)
1714

1815
public unsafe object? InvokeUnsafe(object? obj, IntPtr* args, BindingFlags invokeAttr)
1916
{
20-
// Todo: add strategy for calling IL Emit-based version
21-
return _dynamicMethod.InvokeNonEmitUnsafe(obj, args, invokeAttr);
17+
// Always use the slow path; the Emit-based fast path can be added but in general dynamic
18+
// methods are invoked through a direct delegate, not through Invoke().
19+
if ((invokeAttr & BindingFlags.DoNotWrapExceptions) == 0)
20+
{
21+
try
22+
{
23+
return _dynamicMethod.InvokeNonEmitUnsafe(obj, args);
24+
}
25+
catch (Exception e)
26+
{
27+
throw new TargetInvocationException(e);
28+
}
29+
}
30+
else
31+
{
32+
return _dynamicMethod.InvokeNonEmitUnsafe(obj, args);
33+
}
2234
}
2335
}
2436
}

src/coreclr/System.Private.CoreLib/src/System/Reflection/RuntimeConstructorInfo.CoreCLR.cs

+4-15
Original file line numberDiff line numberDiff line change
@@ -95,26 +95,15 @@ Signature LazyCreateSignature()
9595

9696
internal BindingFlags BindingFlags => m_bindingFlags;
9797

98+
#pragma warning disable CA1822 // Mark members as static
99+
internal bool SupportsNewInvoke => true;
100+
#pragma warning restore CA1822 // Mark members as static
98101

99102
[DebuggerStepThrough]
100103
[DebuggerHidden]
101104
internal unsafe object InvokeNonEmitUnsafe(object? obj, IntPtr* args, Span<object?> argsForTemporaryMonoSupport, BindingFlags invokeAttr)
102105
{
103-
if ((invokeAttr & BindingFlags.DoNotWrapExceptions) == 0)
104-
{
105-
try
106-
{
107-
return RuntimeMethodHandle.InvokeMethod(obj, (void**)args, Signature, isConstructor: obj is null)!;
108-
}
109-
catch (Exception ex)
110-
{
111-
throw new TargetInvocationException(ex);
112-
}
113-
}
114-
else
115-
{
116-
return RuntimeMethodHandle.InvokeMethod(obj, (void**)args, Signature, isConstructor: obj is null)!;
117-
}
106+
return RuntimeMethodHandle.InvokeMethod(obj, (void**)args, Signature, isConstructor: obj is null)!;
118107
}
119108
#endregion
120109

src/coreclr/vm/reflectioninvocation.cpp

+1-3
Original file line numberDiff line numberDiff line change
@@ -624,14 +624,12 @@ FCIMPL4(Object*, RuntimeMethodHandle::InvokeMethod,
624624
else
625625
pThisPtr = OBJECTREFToObject(gc.retVal);
626626
}
627-
else
628-
if (!pMeth->GetMethodTable()->IsValueType())
627+
else if (!pMeth->GetMethodTable()->IsValueType())
629628
pThisPtr = OBJECTREFToObject(gc.target);
630629
else {
631630
if (pMeth->IsUnboxingStub())
632631
pThisPtr = OBJECTREFToObject(gc.target);
633632
else {
634-
//todo: add tests for this (calling a nullable)
635633
// Create a true boxed Nullable<T> and use that as the 'this' pointer.
636634
// since what is passed in is just a boxed T
637635
MethodTable* pMT = pMeth->GetMethodTable();

src/libraries/System.Private.CoreLib/src/System/Reflection/ConstructorInvoker.cs

+72-4
Original file line numberDiff line numberDiff line change
@@ -2,25 +2,93 @@
22
// The .NET Foundation licenses this file to you under the MIT license.
33

44
using System.Diagnostics;
5+
using System.Runtime.CompilerServices;
56

67
namespace System.Reflection
78
{
89
internal sealed partial class ConstructorInvoker
910
{
10-
private readonly RuntimeConstructorInfo _constructorInfo;
11+
private readonly RuntimeConstructorInfo _method;
1112
public InvocationFlags _invocationFlags;
13+
private bool _invoked;
14+
private bool _strategyDetermined;
15+
private InvokerEmitUtil.InvokeFunc<ConstructorInvoker>? _emitInvoke;
1216

1317
public ConstructorInvoker(RuntimeConstructorInfo constructorInfo)
1418
{
15-
_constructorInfo = constructorInfo;
19+
_method = constructorInfo;
1620
}
1721

1822
[DebuggerStepThrough]
1923
[DebuggerHidden]
2024
public unsafe object? InvokeUnsafe(object? obj, IntPtr* args, Span<object?> argsForTemporaryMonoSupport, BindingFlags invokeAttr)
2125
{
22-
// Todo: add strategy for calling IL Emit-based version
23-
return _constructorInfo.InvokeNonEmitUnsafe(obj, args, argsForTemporaryMonoSupport, invokeAttr);
26+
if (!_strategyDetermined)
27+
{
28+
if (!_invoked)
29+
{
30+
// The first time, ignoring race conditions, use the slow path.
31+
_invoked = true;
32+
33+
#if true
34+
// TEMP HACK FOR FORCING IL ON FIRST TIME:
35+
if (RuntimeFeature.IsDynamicCodeCompiled)
36+
{
37+
_emitInvoke = InvokerEmitUtil.CreateInvokeDelegate<ConstructorInvoker>(_method);
38+
}
39+
_strategyDetermined = true;
40+
#endif
41+
}
42+
else
43+
{
44+
if (RuntimeFeature.IsDynamicCodeCompiled)
45+
{
46+
_emitInvoke = InvokerEmitUtil.CreateInvokeDelegate<ConstructorInvoker>(_method);
47+
}
48+
49+
_strategyDetermined = true;
50+
}
51+
}
52+
53+
if (_method.SupportsNewInvoke)
54+
{
55+
if ((invokeAttr & BindingFlags.DoNotWrapExceptions) == 0)
56+
{
57+
try
58+
{
59+
// For the rare and broken scenario of calling the constructor directly through MethodBase.Invoke()
60+
// with a non-null 'obj', we use the slow path to avoid having two emit-based delegates.
61+
if (_emitInvoke != null && obj == null)
62+
{
63+
return _emitInvoke(this, obj, args);
64+
}
65+
else
66+
{
67+
return _method.InvokeNonEmitUnsafe(obj, args, argsForTemporaryMonoSupport, invokeAttr);
68+
}
69+
}
70+
catch (Exception e)
71+
{
72+
throw new TargetInvocationException(e);
73+
}
74+
}
75+
else
76+
{
77+
if (_emitInvoke != null && obj == null)
78+
{
79+
return _emitInvoke(this, obj, args);
80+
}
81+
else
82+
{
83+
return _method.InvokeNonEmitUnsafe(obj, args, argsForTemporaryMonoSupport, invokeAttr);
84+
}
85+
}
86+
}
87+
else
88+
{
89+
// Remove this branch once Mono has the same exception handling and managed conversion logic.
90+
return _method.InvokeNonEmitUnsafe(obj, args, argsForTemporaryMonoSupport, invokeAttr);
91+
}
2492
}
2593
}
2694
}

0 commit comments

Comments
 (0)