Skip to content

Commit 5195418

Browse files
authored
Add IL Emit support for MethodInfo.Invoke() and friends (#67917)
1 parent 1e41844 commit 5195418

Some content is hidden

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

45 files changed

+1196
-654
lines changed

src/coreclr/System.Private.CoreLib/System.Private.CoreLib.csproj

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
<Project Sdk="Microsoft.NET.Sdk">
1+
<Project Sdk="Microsoft.NET.Sdk">
22

33
<PropertyGroup>
44
<EnableDefaultItems>false</EnableDefaultItems>
@@ -155,12 +155,12 @@
155155
<Compile Include="$(BclSourcesRoot)\System\Reflection\AssemblyName.CoreCLR.cs" />
156156
<Compile Include="$(BclSourcesRoot)\System\Reflection\Associates.cs" />
157157
<Compile Include="$(BclSourcesRoot)\System\Reflection\ConstructorInfo.CoreCLR.cs" />
158+
<Compile Include="$(BclSourcesRoot)\System\Reflection\ConstructorInvoker.CoreCLR.cs" />
158159
<Compile Include="$(BclSourcesRoot)\System\Reflection\Emit\AssemblyBuilder.cs" />
159160
<Compile Include="$(BclSourcesRoot)\System\Reflection\Emit\ConstructorBuilder.cs" />
160161
<Compile Include="$(BclSourcesRoot)\System\Reflection\Emit\CustomAttributeBuilder.cs" />
161162
<Compile Include="$(BclSourcesRoot)\System\Reflection\Emit\DynamicILGenerator.cs" />
162163
<Compile Include="$(BclSourcesRoot)\System\Reflection\Emit\DynamicMethod.cs" />
163-
<Compile Include="$(BclSourcesRoot)\System\Reflection\Emit\DynamicMethodInvoker.cs" />
164164
<Compile Include="$(BclSourcesRoot)\System\Reflection\Emit\EnumBuilder.cs" />
165165
<Compile Include="$(BclSourcesRoot)\System\Reflection\Emit\EventBuilder.cs" />
166166
<Compile Include="$(BclSourcesRoot)\System\Reflection\Emit\FieldBuilder.cs" />
@@ -187,6 +187,7 @@
187187
<Compile Include="$(BclSourcesRoot)\System\Reflection\Metadata\AssemblyExtensions.cs" />
188188
<Compile Include="$(BclSourcesRoot)\System\Reflection\Metadata\MetadataUpdater.cs" />
189189
<Compile Include="$(BclSourcesRoot)\System\Reflection\MethodBase.CoreCLR.cs" />
190+
<Compile Include="$(BclSourcesRoot)\System\Reflection\MethodInvoker.CoreCLR.cs" />
190191
<Compile Include="$(BclSourcesRoot)\System\Reflection\RtFieldInfo.cs" />
191192
<Compile Include="$(BclSourcesRoot)\System\Reflection\RuntimeAssembly.cs" />
192193
<Compile Include="$(BclSourcesRoot)\System\Reflection\RuntimeConstructorInfo.CoreCLR.cs" />
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
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+
4+
using System.Runtime.CompilerServices;
5+
6+
namespace System.Reflection
7+
{
8+
internal partial class ConstructorInvoker
9+
{
10+
public InvocationFlags _invocationFlags;
11+
12+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
13+
private unsafe object? InterpretedInvoke(object? obj, IntPtr* arguments)
14+
{
15+
return RuntimeMethodHandle.InvokeMethod(obj, (void**)arguments, _method.Signature, isConstructor: obj is null)!;
16+
}
17+
}
18+
}

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

Lines changed: 37 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ public sealed class DynamicMethod : MethodInfo
2323
private RuntimeModule m_module = null!;
2424
internal bool m_skipVisibility;
2525
internal RuntimeType? m_typeOwner; // can be null
26-
private DynamicMethodInvoker? _invoker;
26+
private MethodInvoker? _invoker;
2727
private Signature? _signature;
2828

2929
// We want the creator of the DynamicMethod to control who has access to the
@@ -434,13 +434,12 @@ internal RuntimeMethodHandle GetMethodDescriptor()
434434

435435
public override bool IsSecurityTransparent => false;
436436

437-
private DynamicMethodInvoker Invoker
437+
private MethodInvoker Invoker
438438
{
439439
[MethodImpl(MethodImplOptions.AggressiveInlining)]
440440
get
441441
{
442-
_invoker ??= new DynamicMethodInvoker(this);
443-
442+
_invoker ??= new MethodInvoker(this, Signature);
444443
return _invoker;
445444
}
446445
}
@@ -490,7 +489,7 @@ Signature LazyCreateSignature()
490489
{
491490
if (argCount == 0)
492491
{
493-
retValue = Invoker.InvokeUnsafe(obj, args: default, invokeAttr);
492+
retValue = Invoker.InlinedInvoke(obj, args: default, invokeAttr);
494493
}
495494
else if (argCount > MaxStackAllocArgCount)
496495
{
@@ -501,8 +500,8 @@ Signature LazyCreateSignature()
501500
{
502501
Debug.Assert(parameters != null);
503502
StackAllocedArguments argStorage = default;
504-
Span<object?> copyOfParameters = new Span<object?>(ref argStorage._arg0, argCount);
505-
Span<bool> shouldCopyBackParameters = new Span<bool>(ref argStorage._copyBack0, argCount);
503+
Span<object?> copyOfParameters = new(ref argStorage._arg0, argCount);
504+
Span<ParameterCopyBackAction> shouldCopyBackParameters = new(ref argStorage._copyBack0, argCount);
506505

507506
StackAllocatedByRefs byrefStorage = default;
508507
IntPtr* pByRefStorage = (IntPtr*)&byrefStorage;
@@ -517,14 +516,25 @@ Signature LazyCreateSignature()
517516
culture,
518517
invokeAttr);
519518

520-
retValue = Invoker.InvokeUnsafe(obj, pByRefStorage, invokeAttr);
519+
retValue = Invoker.InlinedInvoke(obj, pByRefStorage, invokeAttr);
521520

522521
// Copy modified values out. This should be done only with ByRef or Type.Missing parameters.
523522
for (int i = 0; i < argCount; i++)
524523
{
525-
if (shouldCopyBackParameters[i])
524+
ParameterCopyBackAction action = shouldCopyBackParameters[i];
525+
if (action != ParameterCopyBackAction.None)
526526
{
527-
parameters[i] = copyOfParameters[i];
527+
if (action == ParameterCopyBackAction.Copy)
528+
{
529+
parameters[i] = copyOfParameters[i];
530+
}
531+
else
532+
{
533+
Debug.Assert(action == ParameterCopyBackAction.CopyNullable);
534+
Debug.Assert(copyOfParameters[i] != null);
535+
Debug.Assert(((RuntimeType)copyOfParameters[i]!.GetType()).IsNullableOfT);
536+
parameters[i] = RuntimeMethodHandle.ReboxFromNullable(copyOfParameters[i]);
537+
}
528538
}
529539
}
530540
}
@@ -546,15 +556,15 @@ Signature LazyCreateSignature()
546556
CultureInfo? culture)
547557
{
548558
object[] objHolder = new object[argCount];
549-
Span<object?> copyOfParameters = new Span<object?>(objHolder, 0, argCount);
559+
Span<object?> copyOfParameters = new(objHolder, 0, argCount);
550560

551561
// We don't check a max stack size since we are invoking a method which
552562
// naturally requires a stack size that is dependent on the arg count\size.
553563
IntPtr* pByRefStorage = stackalloc IntPtr[argCount];
554564
Buffer.ZeroMemory((byte*)pByRefStorage, (uint)(argCount * sizeof(IntPtr)));
555565

556-
bool* boolHolder = stackalloc bool[argCount];
557-
Span<bool> shouldCopyBackParameters = new Span<bool>(boolHolder, argCount);
566+
ParameterCopyBackAction* copyBackActions = stackalloc ParameterCopyBackAction[argCount];
567+
Span<ParameterCopyBackAction> shouldCopyBackParameters = new(copyBackActions, argCount);
558568

559569
GCFrameRegistration reg = new(pByRefStorage, (uint)argCount, areByRefs: true);
560570

@@ -572,7 +582,7 @@ Signature LazyCreateSignature()
572582
culture,
573583
invokeAttr);
574584

575-
retValue = mi.Invoker.InvokeUnsafe(obj, pByRefStorage, invokeAttr);
585+
retValue = mi.Invoker.InlinedInvoke(obj, pByRefStorage, invokeAttr);
576586
}
577587
finally
578588
{
@@ -582,36 +592,26 @@ Signature LazyCreateSignature()
582592
// Copy modified values out. This should be done only with ByRef or Type.Missing parameters.
583593
for (int i = 0; i < argCount; i++)
584594
{
585-
if (shouldCopyBackParameters[i])
595+
ParameterCopyBackAction action = shouldCopyBackParameters[i];
596+
if (action != ParameterCopyBackAction.None)
586597
{
587-
parameters[i] = copyOfParameters[i];
598+
if (action == ParameterCopyBackAction.Copy)
599+
{
600+
parameters[i] = copyOfParameters[i];
601+
}
602+
else
603+
{
604+
Debug.Assert(action == ParameterCopyBackAction.CopyNullable);
605+
Debug.Assert(copyOfParameters[i] != null);
606+
Debug.Assert(((RuntimeType)copyOfParameters[i]!.GetType()).IsNullableOfT);
607+
parameters[i] = RuntimeMethodHandle.ReboxFromNullable(copyOfParameters[i]);
608+
}
588609
}
589610
}
590611

591612
return retValue;
592613
}
593614

594-
[DebuggerHidden]
595-
[DebuggerStepThrough]
596-
internal unsafe object? InvokeNonEmitUnsafe(object? obj, IntPtr* arguments, BindingFlags invokeAttr)
597-
{
598-
if ((invokeAttr & BindingFlags.DoNotWrapExceptions) == 0)
599-
{
600-
try
601-
{
602-
return RuntimeMethodHandle.InvokeMethod(obj, (void**)arguments, Signature, isConstructor: false);
603-
}
604-
catch (Exception e)
605-
{
606-
throw new TargetInvocationException(e);
607-
}
608-
}
609-
else
610-
{
611-
return RuntimeMethodHandle.InvokeMethod(obj, (void**)arguments, Signature, isConstructor: false);
612-
}
613-
}
614-
615615
public override object[] GetCustomAttributes(Type attributeType, bool inherit)
616616
{
617617
return m_dynMethod.GetCustomAttributes(attributeType, inherit);

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

Lines changed: 0 additions & 24 deletions
This file was deleted.
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
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+
4+
using System.Runtime.CompilerServices;
5+
6+
namespace System.Reflection
7+
{
8+
internal partial class MethodInvoker
9+
{
10+
private readonly Signature _signature;
11+
internal InvocationFlags _invocationFlags;
12+
13+
public MethodInvoker(MethodBase method, Signature signature)
14+
{
15+
_method = method;
16+
_signature = signature;
17+
18+
#if USE_NATIVE_INVOKE
19+
// Always use the native invoke; useful for testing.
20+
_strategyDetermined = true;
21+
#elif USE_EMIT_INVOKE
22+
// Always use emit invoke (if IsDynamicCodeCompiled == true); useful for testing.
23+
_invoked = true;
24+
#endif
25+
}
26+
27+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
28+
private unsafe object? InterpretedInvoke(object? obj, IntPtr* arguments)
29+
{
30+
return RuntimeMethodHandle.InvokeMethod(obj, (void**)arguments, _signature, isConstructor: false);
31+
}
32+
}
33+
}

src/coreclr/System.Private.CoreLib/src/System/Reflection/RtFieldInfo.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -236,7 +236,7 @@ public override void SetValue(object? obj, object? value, BindingFlags invokeAtt
236236

237237
CheckConsistency(obj);
238238

239-
bool _ref = false;
239+
ParameterCopyBackAction _ref = default;
240240
RuntimeType fieldType = (RuntimeType)FieldType;
241241
if (value is null)
242242
{

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

Lines changed: 0 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -95,27 +95,6 @@ Signature LazyCreateSignature()
9595

9696
internal BindingFlags BindingFlags => m_bindingFlags;
9797

98-
99-
[DebuggerStepThrough]
100-
[DebuggerHidden]
101-
internal unsafe object InvokeNonEmitUnsafe(object? obj, IntPtr* args, Span<object?> argsForTemporaryMonoSupport, BindingFlags invokeAttr)
102-
{
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-
}
118-
}
11998
#endregion
12099

121100
#region Object Overrides

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

Lines changed: 7 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ private MethodInvoker Invoker
4848
[MethodImpl(MethodImplOptions.AggressiveInlining)]
4949
get
5050
{
51-
m_invoker ??= new MethodInvoker(this);
51+
m_invoker ??= new MethodInvoker(this, Signature);
5252
return m_invoker;
5353
}
5454
}
@@ -351,7 +351,7 @@ public override MethodImplAttributes GetMethodImplementationFlags()
351351
StackAllocedArguments argStorage = default;
352352
Span<object?> copyOfParameters = new(ref argStorage._arg0, 1);
353353
ReadOnlySpan<object?> parameters = new(in parameter);
354-
Span<bool> shouldCopyBackParameters = new(ref argStorage._copyBack0, 1);
354+
Span<ParameterCopyBackAction> shouldCopyBackParameters = new(ref argStorage._copyBack0, 1);
355355

356356
StackAllocatedByRefs byrefStorage = default;
357357
IntPtr* pByRefStorage = (IntPtr*)&byrefStorage;
@@ -366,33 +366,16 @@ public override MethodImplAttributes GetMethodImplementationFlags()
366366
culture,
367367
invokeAttr);
368368

369-
retValue = Invoker.InvokeUnsafe(obj, pByRefStorage, copyOfParameters, invokeAttr);
369+
#if MONO // Temporary until Mono is updated.
370+
retValue = Invoker.InlinedInvoke(obj, copyOfParameters, invokeAttr);
371+
#else
372+
retValue = Invoker.InlinedInvoke(obj, pByRefStorage, invokeAttr);
373+
#endif
370374
}
371375

372376
return retValue;
373377
}
374378

375-
[DebuggerHidden]
376-
[DebuggerStepThrough]
377-
internal unsafe object? InvokeNonEmitUnsafe(object? obj, IntPtr* arguments, Span<object?> argsForTemporaryMonoSupport, BindingFlags invokeAttr)
378-
{
379-
if ((invokeAttr & BindingFlags.DoNotWrapExceptions) == 0)
380-
{
381-
try
382-
{
383-
return RuntimeMethodHandle.InvokeMethod(obj, (void**)arguments, Signature, isConstructor: false);
384-
}
385-
catch (Exception e)
386-
{
387-
throw new TargetInvocationException(e);
388-
}
389-
}
390-
else
391-
{
392-
return RuntimeMethodHandle.InvokeMethod(obj, (void**)arguments, Signature, isConstructor: false);
393-
}
394-
}
395-
396379
#endregion
397380

398381
#region MethodInfo Overrides

src/coreclr/System.Private.CoreLib/src/System/RuntimeHandles.cs

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -138,6 +138,19 @@ internal static bool IsByRef(RuntimeType type)
138138
return corElemType == CorElementType.ELEMENT_TYPE_BYREF;
139139
}
140140

141+
internal static bool TryGetByRefElementType(RuntimeType type, [NotNullWhen(true)] out RuntimeType? elementType)
142+
{
143+
CorElementType corElemType = GetCorElementType(type);
144+
if (corElemType == CorElementType.ELEMENT_TYPE_BYREF)
145+
{
146+
elementType = GetElementType(type);
147+
return true;
148+
}
149+
150+
elementType = null;
151+
return false;
152+
}
153+
141154
internal static bool IsPointer(RuntimeType type)
142155
{
143156
CorElementType corElemType = GetCorElementType(type);
@@ -981,6 +994,12 @@ internal static MdUtf8String GetUtf8Name(RuntimeMethodHandleInternal method)
981994
[MethodImpl(MethodImplOptions.InternalCall)]
982995
internal static extern object? InvokeMethod(object? target, void** arguments, Signature sig, bool isConstructor);
983996

997+
[MethodImpl(MethodImplOptions.InternalCall)]
998+
internal static extern object? ReboxFromNullable(object? src);
999+
1000+
[MethodImpl(MethodImplOptions.InternalCall)]
1001+
internal static extern object ReboxToNullable(object? src, RuntimeType destNullableType);
1002+
9841003
[LibraryImport(RuntimeHelpers.QCall, EntryPoint = "RuntimeMethodHandle_GetMethodInstantiation")]
9851004
private static partial void GetMethodInstantiation(RuntimeMethodHandleInternal method, ObjectHandleOnStack types, Interop.BOOL fAsRuntimeTypeArray);
9861005

0 commit comments

Comments
 (0)