Skip to content

Commit

Permalink
Add IL Emit support for MethodInfo.Invoke() and friends (#67917)
Browse files Browse the repository at this point in the history
  • Loading branch information
steveharter authored May 9, 2022
1 parent 1e41844 commit 5195418
Show file tree
Hide file tree
Showing 45 changed files with 1,196 additions and 654 deletions.
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
<Project Sdk="Microsoft.NET.Sdk">
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<EnableDefaultItems>false</EnableDefaultItems>
Expand Down Expand Up @@ -155,12 +155,12 @@
<Compile Include="$(BclSourcesRoot)\System\Reflection\AssemblyName.CoreCLR.cs" />
<Compile Include="$(BclSourcesRoot)\System\Reflection\Associates.cs" />
<Compile Include="$(BclSourcesRoot)\System\Reflection\ConstructorInfo.CoreCLR.cs" />
<Compile Include="$(BclSourcesRoot)\System\Reflection\ConstructorInvoker.CoreCLR.cs" />
<Compile Include="$(BclSourcesRoot)\System\Reflection\Emit\AssemblyBuilder.cs" />
<Compile Include="$(BclSourcesRoot)\System\Reflection\Emit\ConstructorBuilder.cs" />
<Compile Include="$(BclSourcesRoot)\System\Reflection\Emit\CustomAttributeBuilder.cs" />
<Compile Include="$(BclSourcesRoot)\System\Reflection\Emit\DynamicILGenerator.cs" />
<Compile Include="$(BclSourcesRoot)\System\Reflection\Emit\DynamicMethod.cs" />
<Compile Include="$(BclSourcesRoot)\System\Reflection\Emit\DynamicMethodInvoker.cs" />
<Compile Include="$(BclSourcesRoot)\System\Reflection\Emit\EnumBuilder.cs" />
<Compile Include="$(BclSourcesRoot)\System\Reflection\Emit\EventBuilder.cs" />
<Compile Include="$(BclSourcesRoot)\System\Reflection\Emit\FieldBuilder.cs" />
Expand All @@ -187,6 +187,7 @@
<Compile Include="$(BclSourcesRoot)\System\Reflection\Metadata\AssemblyExtensions.cs" />
<Compile Include="$(BclSourcesRoot)\System\Reflection\Metadata\MetadataUpdater.cs" />
<Compile Include="$(BclSourcesRoot)\System\Reflection\MethodBase.CoreCLR.cs" />
<Compile Include="$(BclSourcesRoot)\System\Reflection\MethodInvoker.CoreCLR.cs" />
<Compile Include="$(BclSourcesRoot)\System\Reflection\RtFieldInfo.cs" />
<Compile Include="$(BclSourcesRoot)\System\Reflection\RuntimeAssembly.cs" />
<Compile Include="$(BclSourcesRoot)\System\Reflection\RuntimeConstructorInfo.CoreCLR.cs" />
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System.Runtime.CompilerServices;

namespace System.Reflection
{
internal partial class ConstructorInvoker
{
public InvocationFlags _invocationFlags;

[MethodImpl(MethodImplOptions.AggressiveInlining)]
private unsafe object? InterpretedInvoke(object? obj, IntPtr* arguments)
{
return RuntimeMethodHandle.InvokeMethod(obj, (void**)arguments, _method.Signature, isConstructor: obj is null)!;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ public sealed class DynamicMethod : MethodInfo
private RuntimeModule m_module = null!;
internal bool m_skipVisibility;
internal RuntimeType? m_typeOwner; // can be null
private DynamicMethodInvoker? _invoker;
private MethodInvoker? _invoker;
private Signature? _signature;

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

public override bool IsSecurityTransparent => false;

private DynamicMethodInvoker Invoker
private MethodInvoker Invoker
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get
{
_invoker ??= new DynamicMethodInvoker(this);

_invoker ??= new MethodInvoker(this, Signature);
return _invoker;
}
}
Expand Down Expand Up @@ -490,7 +489,7 @@ Signature LazyCreateSignature()
{
if (argCount == 0)
{
retValue = Invoker.InvokeUnsafe(obj, args: default, invokeAttr);
retValue = Invoker.InlinedInvoke(obj, args: default, invokeAttr);
}
else if (argCount > MaxStackAllocArgCount)
{
Expand All @@ -501,8 +500,8 @@ Signature LazyCreateSignature()
{
Debug.Assert(parameters != null);
StackAllocedArguments argStorage = default;
Span<object?> copyOfParameters = new Span<object?>(ref argStorage._arg0, argCount);
Span<bool> shouldCopyBackParameters = new Span<bool>(ref argStorage._copyBack0, argCount);
Span<object?> copyOfParameters = new(ref argStorage._arg0, argCount);
Span<ParameterCopyBackAction> shouldCopyBackParameters = new(ref argStorage._copyBack0, argCount);

StackAllocatedByRefs byrefStorage = default;
IntPtr* pByRefStorage = (IntPtr*)&byrefStorage;
Expand All @@ -517,14 +516,25 @@ Signature LazyCreateSignature()
culture,
invokeAttr);

retValue = Invoker.InvokeUnsafe(obj, pByRefStorage, invokeAttr);
retValue = Invoker.InlinedInvoke(obj, pByRefStorage, invokeAttr);

// Copy modified values out. This should be done only with ByRef or Type.Missing parameters.
for (int i = 0; i < argCount; i++)
{
if (shouldCopyBackParameters[i])
ParameterCopyBackAction action = shouldCopyBackParameters[i];
if (action != ParameterCopyBackAction.None)
{
parameters[i] = copyOfParameters[i];
if (action == ParameterCopyBackAction.Copy)
{
parameters[i] = copyOfParameters[i];
}
else
{
Debug.Assert(action == ParameterCopyBackAction.CopyNullable);
Debug.Assert(copyOfParameters[i] != null);
Debug.Assert(((RuntimeType)copyOfParameters[i]!.GetType()).IsNullableOfT);
parameters[i] = RuntimeMethodHandle.ReboxFromNullable(copyOfParameters[i]);
}
}
}
}
Expand All @@ -546,15 +556,15 @@ Signature LazyCreateSignature()
CultureInfo? culture)
{
object[] objHolder = new object[argCount];
Span<object?> copyOfParameters = new Span<object?>(objHolder, 0, argCount);
Span<object?> copyOfParameters = new(objHolder, 0, argCount);

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

bool* boolHolder = stackalloc bool[argCount];
Span<bool> shouldCopyBackParameters = new Span<bool>(boolHolder, argCount);
ParameterCopyBackAction* copyBackActions = stackalloc ParameterCopyBackAction[argCount];
Span<ParameterCopyBackAction> shouldCopyBackParameters = new(copyBackActions, argCount);

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

Expand All @@ -572,7 +582,7 @@ Signature LazyCreateSignature()
culture,
invokeAttr);

retValue = mi.Invoker.InvokeUnsafe(obj, pByRefStorage, invokeAttr);
retValue = mi.Invoker.InlinedInvoke(obj, pByRefStorage, invokeAttr);
}
finally
{
Expand All @@ -582,36 +592,26 @@ Signature LazyCreateSignature()
// Copy modified values out. This should be done only with ByRef or Type.Missing parameters.
for (int i = 0; i < argCount; i++)
{
if (shouldCopyBackParameters[i])
ParameterCopyBackAction action = shouldCopyBackParameters[i];
if (action != ParameterCopyBackAction.None)
{
parameters[i] = copyOfParameters[i];
if (action == ParameterCopyBackAction.Copy)
{
parameters[i] = copyOfParameters[i];
}
else
{
Debug.Assert(action == ParameterCopyBackAction.CopyNullable);
Debug.Assert(copyOfParameters[i] != null);
Debug.Assert(((RuntimeType)copyOfParameters[i]!.GetType()).IsNullableOfT);
parameters[i] = RuntimeMethodHandle.ReboxFromNullable(copyOfParameters[i]);
}
}
}

return retValue;
}

[DebuggerHidden]
[DebuggerStepThrough]
internal unsafe object? InvokeNonEmitUnsafe(object? obj, IntPtr* arguments, BindingFlags invokeAttr)
{
if ((invokeAttr & BindingFlags.DoNotWrapExceptions) == 0)
{
try
{
return RuntimeMethodHandle.InvokeMethod(obj, (void**)arguments, Signature, isConstructor: false);
}
catch (Exception e)
{
throw new TargetInvocationException(e);
}
}
else
{
return RuntimeMethodHandle.InvokeMethod(obj, (void**)arguments, Signature, isConstructor: false);
}
}

public override object[] GetCustomAttributes(Type attributeType, bool inherit)
{
return m_dynMethod.GetCustomAttributes(attributeType, inherit);
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System.Runtime.CompilerServices;

namespace System.Reflection
{
internal partial class MethodInvoker
{
private readonly Signature _signature;
internal InvocationFlags _invocationFlags;

public MethodInvoker(MethodBase method, Signature signature)
{
_method = method;
_signature = signature;

#if USE_NATIVE_INVOKE
// Always use the native invoke; useful for testing.
_strategyDetermined = true;
#elif USE_EMIT_INVOKE
// Always use emit invoke (if IsDynamicCodeCompiled == true); useful for testing.
_invoked = true;
#endif
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
private unsafe object? InterpretedInvoke(object? obj, IntPtr* arguments)
{
return RuntimeMethodHandle.InvokeMethod(obj, (void**)arguments, _signature, isConstructor: false);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -236,7 +236,7 @@ public override void SetValue(object? obj, object? value, BindingFlags invokeAtt

CheckConsistency(obj);

bool _ref = false;
ParameterCopyBackAction _ref = default;
RuntimeType fieldType = (RuntimeType)FieldType;
if (value is null)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -95,27 +95,6 @@ Signature LazyCreateSignature()

internal BindingFlags BindingFlags => m_bindingFlags;


[DebuggerStepThrough]
[DebuggerHidden]
internal unsafe object InvokeNonEmitUnsafe(object? obj, IntPtr* args, Span<object?> argsForTemporaryMonoSupport, BindingFlags invokeAttr)
{
if ((invokeAttr & BindingFlags.DoNotWrapExceptions) == 0)
{
try
{
return RuntimeMethodHandle.InvokeMethod(obj, (void**)args, Signature, isConstructor: obj is null)!;
}
catch (Exception ex)
{
throw new TargetInvocationException(ex);
}
}
else
{
return RuntimeMethodHandle.InvokeMethod(obj, (void**)args, Signature, isConstructor: obj is null)!;
}
}
#endregion

#region Object Overrides
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ private MethodInvoker Invoker
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get
{
m_invoker ??= new MethodInvoker(this);
m_invoker ??= new MethodInvoker(this, Signature);
return m_invoker;
}
}
Expand Down Expand Up @@ -351,7 +351,7 @@ public override MethodImplAttributes GetMethodImplementationFlags()
StackAllocedArguments argStorage = default;
Span<object?> copyOfParameters = new(ref argStorage._arg0, 1);
ReadOnlySpan<object?> parameters = new(in parameter);
Span<bool> shouldCopyBackParameters = new(ref argStorage._copyBack0, 1);
Span<ParameterCopyBackAction> shouldCopyBackParameters = new(ref argStorage._copyBack0, 1);

StackAllocatedByRefs byrefStorage = default;
IntPtr* pByRefStorage = (IntPtr*)&byrefStorage;
Expand All @@ -366,33 +366,16 @@ public override MethodImplAttributes GetMethodImplementationFlags()
culture,
invokeAttr);

retValue = Invoker.InvokeUnsafe(obj, pByRefStorage, copyOfParameters, invokeAttr);
#if MONO // Temporary until Mono is updated.
retValue = Invoker.InlinedInvoke(obj, copyOfParameters, invokeAttr);
#else
retValue = Invoker.InlinedInvoke(obj, pByRefStorage, invokeAttr);
#endif
}

return retValue;
}

[DebuggerHidden]
[DebuggerStepThrough]
internal unsafe object? InvokeNonEmitUnsafe(object? obj, IntPtr* arguments, Span<object?> argsForTemporaryMonoSupport, BindingFlags invokeAttr)
{
if ((invokeAttr & BindingFlags.DoNotWrapExceptions) == 0)
{
try
{
return RuntimeMethodHandle.InvokeMethod(obj, (void**)arguments, Signature, isConstructor: false);
}
catch (Exception e)
{
throw new TargetInvocationException(e);
}
}
else
{
return RuntimeMethodHandle.InvokeMethod(obj, (void**)arguments, Signature, isConstructor: false);
}
}

#endregion

#region MethodInfo Overrides
Expand Down
19 changes: 19 additions & 0 deletions src/coreclr/System.Private.CoreLib/src/System/RuntimeHandles.cs
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,19 @@ internal static bool IsByRef(RuntimeType type)
return corElemType == CorElementType.ELEMENT_TYPE_BYREF;
}

internal static bool TryGetByRefElementType(RuntimeType type, [NotNullWhen(true)] out RuntimeType? elementType)
{
CorElementType corElemType = GetCorElementType(type);
if (corElemType == CorElementType.ELEMENT_TYPE_BYREF)
{
elementType = GetElementType(type);
return true;
}

elementType = null;
return false;
}

internal static bool IsPointer(RuntimeType type)
{
CorElementType corElemType = GetCorElementType(type);
Expand Down Expand Up @@ -981,6 +994,12 @@ internal static MdUtf8String GetUtf8Name(RuntimeMethodHandleInternal method)
[MethodImpl(MethodImplOptions.InternalCall)]
internal static extern object? InvokeMethod(object? target, void** arguments, Signature sig, bool isConstructor);

[MethodImpl(MethodImplOptions.InternalCall)]
internal static extern object? ReboxFromNullable(object? src);

[MethodImpl(MethodImplOptions.InternalCall)]
internal static extern object ReboxToNullable(object? src, RuntimeType destNullableType);

[LibraryImport(RuntimeHelpers.QCall, EntryPoint = "RuntimeMethodHandle_GetMethodInstantiation")]
private static partial void GetMethodInstantiation(RuntimeMethodHandleInternal method, ObjectHandleOnStack types, Interop.BOOL fAsRuntimeTypeArray);

Expand Down
Loading

0 comments on commit 5195418

Please sign in to comment.