Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Reflection Invoke share more coreclr/mono #60270

Merged
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -176,7 +176,6 @@
<Compile Include="$(BclSourcesRoot)\System\Reflection\Emit\TypeBuilderInstantiation.cs" />
<Compile Include="$(BclSourcesRoot)\System\Reflection\Emit\XXXOnTypeBuilderInstantiation.cs" />
<Compile Include="$(BclSourcesRoot)\System\Reflection\FieldInfo.CoreCLR.cs" />
<Compile Include="$(BclSourcesRoot)\System\Reflection\INVOCATION_FLAGS.cs" />
<Compile Include="$(BclSourcesRoot)\System\Reflection\LoaderAllocator.cs" />
<Compile Include="$(BclSourcesRoot)\System\Reflection\MdConstant.cs" />
<Compile Include="$(BclSourcesRoot)\System\Reflection\MdFieldInfo.cs" />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -463,7 +463,7 @@ internal RuntimeMethodHandle GetMethodDescriptor()
Span<object?> arguments = default;
if (actualCount != 0)
{
arguments = CheckArguments(ref stackArgs, parameters, binder, invokeAttr, culture, sig);
arguments = CheckArguments(ref stackArgs, parameters, binder, invokeAttr, culture, sig.Arguments);
}

object? retValue = RuntimeMethodHandle.InvokeMethod(null, arguments, sig, false, wrapExceptions);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -66,58 +66,6 @@ internal virtual Type[] GetParameterTypes()

return parameterTypes;
}

private protected Span<object?> CheckArguments(ref StackAllocedArguments stackArgs, ReadOnlySpan<object?> parameters, Binder? binder,
BindingFlags invokeAttr, CultureInfo? culture, Signature sig)
{
Debug.Assert(Unsafe.SizeOf<StackAllocedArguments>() == StackAllocedArguments.MaxStackAllocArgCount * Unsafe.SizeOf<object>(),
"MaxStackAllocArgCount not properly defined.");
Debug.Assert(!parameters.IsEmpty);

// We need to perform type safety validation against the incoming arguments, but we also need
// to be resilient against the possibility that some other thread (or even the binder itself!)
// may mutate the array after we've validated the arguments but before we've properly invoked
// the method. The solution is to copy the arguments to a different, not-user-visible buffer
// as we validate them. n.b. This disallows use of ArrayPool, as ArrayPool-rented arrays are
// considered user-visible to threads which may still be holding on to returned instances.

Span<object?> copyOfParameters = (parameters.Length <= StackAllocedArguments.MaxStackAllocArgCount)
? MemoryMarshal.CreateSpan(ref stackArgs._arg0, parameters.Length)
: new Span<object?>(new object?[parameters.Length]);

ParameterInfo[]? p = null;
for (int i = 0; i < parameters.Length; i++)
{
object? arg = parameters[i];
RuntimeType argRT = sig.Arguments[i];

if (arg == Type.Missing)
{
p ??= GetParametersNoCopy();
if (p[i].DefaultValue == System.DBNull.Value)
throw new ArgumentException(SR.Arg_VarMissNull, nameof(parameters));
arg = p[i].DefaultValue!;
}
copyOfParameters[i] = argRT.CheckValue(arg, binder, culture, invokeAttr);
}

return copyOfParameters;
}

// Helper struct to avoid intermediate object[] allocation in calls to the native reflection stack.
// Typical usage is to define a local of type default(StackAllocedArguments), then pass 'ref theLocal'
// as the first parameter to CheckArguments. CheckArguments will try to utilize storage within this
// struct instance if there's sufficient space; otherwise CheckArguments will allocate a temp array.
private protected struct StackAllocedArguments
{
internal const int MaxStackAllocArgCount = 4;
internal object? _arg0;
#pragma warning disable CA1823, CS0169, IDE0051 // accessed via 'CheckArguments' ref arithmetic
private object? _arg1;
private object? _arg2;
private object? _arg3;
#pragma warning restore CA1823, CS0169, IDE0051
}
#endregion
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@

namespace System.Reflection
{
internal sealed class RuntimeConstructorInfo : ConstructorInfo, IRuntimeMethodInfo
internal sealed partial class RuntimeConstructorInfo : ConstructorInfo, IRuntimeMethodInfo
{
#region Private Data Members
private volatile RuntimeType m_declaringType;
Expand All @@ -35,51 +35,10 @@ internal INVOCATION_FLAGS InvocationFlags
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get
{
[MethodImpl(MethodImplOptions.NoInlining)] // move lazy invocation flags population out of the hot path
INVOCATION_FLAGS LazyCreateInvocationFlags()
{
INVOCATION_FLAGS invocationFlags = INVOCATION_FLAGS.INVOCATION_FLAGS_IS_CTOR; // this is a given

Type? declaringType = DeclaringType;

//
// first take care of all the NO_INVOKE cases.
if (declaringType == typeof(void) ||
(declaringType != null && declaringType.ContainsGenericParameters) ||
((CallingConvention & CallingConventions.VarArgs) == CallingConventions.VarArgs))
{
// We don't need other flags if this method cannot be invoked
invocationFlags |= INVOCATION_FLAGS.INVOCATION_FLAGS_NO_INVOKE;
}
else if (IsStatic)
{
invocationFlags |= INVOCATION_FLAGS.INVOCATION_FLAGS_RUN_CLASS_CONSTRUCTOR |
INVOCATION_FLAGS.INVOCATION_FLAGS_NO_CTOR_INVOKE;
}
else if (declaringType != null && declaringType.IsAbstract)
{
invocationFlags |= INVOCATION_FLAGS.INVOCATION_FLAGS_NO_CTOR_INVOKE;
}
else
{
// Check for byref-like types
if (declaringType != null && declaringType.IsByRefLike)
invocationFlags |= INVOCATION_FLAGS.INVOCATION_FLAGS_CONTAINS_STACK_POINTERS;

// Check for attempt to create a delegate class.
if (typeof(Delegate).IsAssignableFrom(DeclaringType))
invocationFlags |= INVOCATION_FLAGS.INVOCATION_FLAGS_IS_DELEGATE_CTOR;
}

invocationFlags |= INVOCATION_FLAGS.INVOCATION_FLAGS_INITIALIZED;
m_invocationFlags = invocationFlags; // accesses are guaranteed atomic
return invocationFlags;
}

INVOCATION_FLAGS flags = m_invocationFlags;
if ((flags & INVOCATION_FLAGS.INVOCATION_FLAGS_INITIALIZED) == 0)
{
flags = LazyCreateInvocationFlags();
flags = ComputeAndUpdateInvocationFlags(this, ref m_invocationFlags);
}
return flags;
}
Expand Down Expand Up @@ -124,20 +83,6 @@ Signature LazyCreateSignature()

private RuntimeType ReflectedTypeInternal => m_reflectedTypeCache.GetRuntimeType();

private void CheckConsistency(object? target)
{
if (target == null && IsStatic)
return;

if (!m_declaringType.IsInstanceOfType(target))
{
if (target == null)
throw new TargetException(SR.RFLCT_Targ_StatMethReqTarg);

throw new TargetException(SR.RFLCT_Targ_ITargMismatch);
}
}

internal BindingFlags BindingFlags => m_bindingFlags;
#endregion

Expand Down Expand Up @@ -247,109 +192,35 @@ public override MethodImplAttributes GetMethodImplementationFlags()

public override CallingConventions CallingConvention => Signature.CallingConvention;

internal static void CheckCanCreateInstance(Type declaringType, bool isVarArg)
{
if (declaringType == null)
throw new ArgumentNullException(nameof(declaringType));

// ctor is declared on interface class
if (declaringType.IsInterface)
throw new MemberAccessException(
SR.Format(SR.Acc_CreateInterfaceEx, declaringType));
private RuntimeType[] ArgumentTypes => Signature.Arguments;

// ctor is on an abstract class
else if (declaringType.IsAbstract)
throw new MemberAccessException(
SR.Format(SR.Acc_CreateAbstEx, declaringType));

// ctor is on a class that contains stack pointers
else if (declaringType.GetRootElementType() == typeof(ArgIterator))
throw new NotSupportedException();

// ctor is vararg
else if (isVarArg)
throw new NotSupportedException();
[UnconditionalSuppressMessage("ReflectionAnalysis", "IL2059:RunClassConstructor",
Justification = "This ConstructorInfo instance represents the static constructor itself, so if this object was created, the static constructor exists.")]
private object? InvokeClassConstructor()
{
Debug.Assert((InvocationFlags & INVOCATION_FLAGS.INVOCATION_FLAGS_RUN_CLASS_CONSTRUCTOR) != 0);
var declaringType = DeclaringType;

// ctor is generic or on a generic class
else if (declaringType.ContainsGenericParameters)
{
throw new MemberAccessException(
SR.Format(SR.Acc_CreateGenericEx, declaringType));
}
if (declaringType != null)
RuntimeHelpers.RunClassConstructor(declaringType.TypeHandle);
else
RuntimeHelpers.RunModuleConstructor(Module.ModuleHandle);

// ctor is declared on System.Void
else if (declaringType == typeof(void))
throw new MemberAccessException(SR.Access_Void);
return null;
}

[DoesNotReturn]
internal void ThrowNoInvokeException()
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private object? InvokeWorker(object? obj, BindingFlags invokeAttr, Span<object?> arguments)
{
CheckCanCreateInstance(DeclaringType!, (CallingConvention & CallingConventions.VarArgs) == CallingConventions.VarArgs);

// ctor is .cctor
if ((Attributes & MethodAttributes.Static) == MethodAttributes.Static)
throw new MemberAccessException(SR.Acc_NotClassInit);

throw new TargetException();
bool wrapExceptions = (invokeAttr & BindingFlags.DoNotWrapExceptions) == 0;
return RuntimeMethodHandle.InvokeMethod(obj, arguments, Signature, false, wrapExceptions);
}

[DebuggerStepThroughAttribute]
[Diagnostics.DebuggerHidden]
[UnconditionalSuppressMessage("ReflectionAnalysis", "IL2059:RunClassConstructor",
Justification = "This ConstructorInfo instance represents the static constructor itself, so if this object was created, the static constructor exists.")]
public override object? Invoke(
object? obj, BindingFlags invokeAttr, Binder? binder, object?[]? parameters, CultureInfo? culture)
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private object InvokeCtorWorker(BindingFlags invokeAttr, Span<object?> arguments)
{
INVOCATION_FLAGS invocationFlags = InvocationFlags;

if ((invocationFlags & INVOCATION_FLAGS.INVOCATION_FLAGS_NO_INVOKE) != 0)
ThrowNoInvokeException();

// check basic method consistency. This call will throw if there are problems in the target/method relationship
CheckConsistency(obj);

if ((invocationFlags & INVOCATION_FLAGS.INVOCATION_FLAGS_RUN_CLASS_CONSTRUCTOR) != 0)
{
// Run the class constructor through the class constructor mechanism instead of the Invoke path.
// This avoids allowing mutation of readonly static fields, and initializes the type correctly.

var declaringType = DeclaringType;

if (declaringType != null)
RuntimeHelpers.RunClassConstructor(declaringType.TypeHandle);
else
RuntimeHelpers.RunModuleConstructor(Module.ModuleHandle);

return null;
}

Signature sig = Signature;

// get the signature
int formalCount = sig.Arguments.Length;
int actualCount = (parameters != null) ? parameters.Length : 0;
if (formalCount != actualCount)
throw new TargetParameterCountException(SR.Arg_ParmCnt);

// if we are here we passed all the previous checks. Time to look at the arguments
bool wrapExceptions = (invokeAttr & BindingFlags.DoNotWrapExceptions) == 0;

StackAllocedArguments stackArgs = default;
Span<object?> arguments = default;
if (actualCount != 0)
{
arguments = CheckArguments(ref stackArgs, parameters, binder, invokeAttr, culture, sig);
}

object? retValue = RuntimeMethodHandle.InvokeMethod(obj, arguments, sig, false, wrapExceptions);

// copy out. This should be made only if ByRef are present.
// n.b. cannot use Span<T>.CopyTo, as parameters.GetType() might not actually be typeof(object[])
for (int index = 0; index < arguments.Length; index++)
parameters![index] = arguments[index];

return retValue;
return RuntimeMethodHandle.InvokeMethod(null, arguments, Signature, true, wrapExceptions)!;
}

[RequiresUnreferencedCode("Trimming may change method bodies. For example it can change some instructions, remove branches or local variables.")]
Expand All @@ -369,47 +240,5 @@ internal void ThrowNoInvokeException()

public override bool ContainsGenericParameters => DeclaringType != null && DeclaringType.ContainsGenericParameters;
#endregion

#region ConstructorInfo Overrides
[DebuggerStepThroughAttribute]
[Diagnostics.DebuggerHidden]
public override object Invoke(BindingFlags invokeAttr, Binder? binder, object?[]? parameters, CultureInfo? culture)
{
INVOCATION_FLAGS invocationFlags = InvocationFlags;

if ((invocationFlags & (INVOCATION_FLAGS.INVOCATION_FLAGS_NO_INVOKE | INVOCATION_FLAGS.INVOCATION_FLAGS_CONTAINS_STACK_POINTERS | INVOCATION_FLAGS.INVOCATION_FLAGS_NO_CTOR_INVOKE)) != 0)
ThrowNoInvokeException();

// get the signature
Signature sig = Signature;

int formalCount = sig.Arguments.Length;
int actualCount = (parameters != null) ? parameters.Length : 0;
if (formalCount != actualCount)
throw new TargetParameterCountException(SR.Arg_ParmCnt);

// We don't need to explicitly invoke the class constructor here,
// JIT/NGen will insert the call to .cctor in the instance ctor.

// if we are here we passed all the previous checks. Time to look at the arguments
bool wrapExceptions = (invokeAttr & BindingFlags.DoNotWrapExceptions) == 0;

StackAllocedArguments stackArgs = default;
Span<object?> arguments = default;
if (actualCount != 0)
{
arguments = CheckArguments(ref stackArgs, parameters, binder, invokeAttr, culture, sig);
}

object retValue = RuntimeMethodHandle.InvokeMethod(null, arguments, sig, true, wrapExceptions)!; // ctor must return non-null

// copy out. This should be made only if ByRef are present.
// n.b. cannot use Span<T>.CopyTo, as parameters.GetType() might not actually be typeof(object[])
for (int index = 0; index < arguments.Length; index++)
parameters![index] = arguments[index];

return retValue;
}
#endregion
}
}
Loading