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 all 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 All @@ -187,14 +186,14 @@
<Compile Include="$(BclSourcesRoot)\System\Reflection\MethodBase.CoreCLR.cs" />
<Compile Include="$(BclSourcesRoot)\System\Reflection\RtFieldInfo.cs" />
<Compile Include="$(BclSourcesRoot)\System\Reflection\RuntimeAssembly.cs" />
<Compile Include="$(BclSourcesRoot)\System\Reflection\RuntimeConstructorInfo.cs" />
<Compile Include="$(BclSourcesRoot)\System\Reflection\RuntimeConstructorInfo.CoreCLR.cs" />
<Compile Include="$(BclSourcesRoot)\System\Reflection\RuntimeCustomAttributeData.cs" />
<Compile Include="$(BclSourcesRoot)\System\Reflection\RuntimeEventInfo.cs" />
<Compile Include="$(BclSourcesRoot)\System\Reflection\RuntimeExceptionHandlingClause.cs" />
<Compile Include="$(BclSourcesRoot)\System\Reflection\RuntimeFieldInfo.cs" />
<Compile Include="$(BclSourcesRoot)\System\Reflection\RuntimeLocalVariableInfo.cs" />
<Compile Include="$(BclSourcesRoot)\System\Reflection\RuntimeMethodBody.cs" />
<Compile Include="$(BclSourcesRoot)\System\Reflection\RuntimeMethodInfo.cs" />
<Compile Include="$(BclSourcesRoot)\System\Reflection\RuntimeMethodInfo.CoreCLR.cs" />
<Compile Include="$(BclSourcesRoot)\System\Reflection\RuntimeModule.cs" />
<Compile Include="$(BclSourcesRoot)\System\Reflection\RuntimeParameterInfo.cs" />
<Compile Include="$(BclSourcesRoot)\System\Reflection\RuntimePropertyInfo.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 @@ -17,46 +17,46 @@ internal sealed unsafe class RtFieldInfo : RuntimeFieldInfo, IRuntimeFieldInfo
// lazy caching
private string? m_name;
private RuntimeType? m_fieldType;
private INVOCATION_FLAGS m_invocationFlags;
private InvocationFlags m_invocationFlags;

internal INVOCATION_FLAGS InvocationFlags
internal InvocationFlags InvocationFlags
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => (m_invocationFlags & INVOCATION_FLAGS.INVOCATION_FLAGS_INITIALIZED) != 0 ?
get => (m_invocationFlags & InvocationFlags.Initialized) != 0 ?
m_invocationFlags : InitializeInvocationFlags();
}

[MethodImpl(MethodImplOptions.NoInlining)]
private INVOCATION_FLAGS InitializeInvocationFlags()
private InvocationFlags InitializeInvocationFlags()
{
Type? declaringType = DeclaringType;

INVOCATION_FLAGS invocationFlags = 0;
InvocationFlags invocationFlags = 0;

// first take care of all the NO_INVOKE cases
if (declaringType != null && declaringType.ContainsGenericParameters)
{
invocationFlags |= INVOCATION_FLAGS.INVOCATION_FLAGS_NO_INVOKE;
invocationFlags |= InvocationFlags.NoInvoke;
}

// If the invocationFlags are still 0, then
// this should be an usable field, determine the other flags
if (invocationFlags == 0)
{
if ((m_fieldAttributes & FieldAttributes.InitOnly) != (FieldAttributes)0)
invocationFlags |= INVOCATION_FLAGS.INVOCATION_FLAGS_SPECIAL_FIELD;
invocationFlags |= InvocationFlags.SpecialField;

if ((m_fieldAttributes & FieldAttributes.HasFieldRVA) != (FieldAttributes)0)
invocationFlags |= INVOCATION_FLAGS.INVOCATION_FLAGS_SPECIAL_FIELD;
invocationFlags |= InvocationFlags.SpecialField;

// find out if the field type is one of the following: Primitive, Enum or Pointer
Type fieldType = FieldType;
if (fieldType.IsPointer || fieldType.IsEnum || fieldType.IsPrimitive)
invocationFlags |= INVOCATION_FLAGS.INVOCATION_FLAGS_FIELD_SPECIAL_CAST;
invocationFlags |= InvocationFlags.FieldSpecialCast;
}

// must be last to avoid threading problems
return m_invocationFlags = invocationFlags | INVOCATION_FLAGS.INVOCATION_FLAGS_INITIALIZED;
return m_invocationFlags = invocationFlags | InvocationFlags.Initialized;
}
#endregion

Expand Down Expand Up @@ -124,10 +124,10 @@ internal override RuntimeModule GetRuntimeModule()
[Diagnostics.DebuggerHidden]
public override object? GetValue(object? obj)
{
INVOCATION_FLAGS invocationFlags = InvocationFlags;
InvocationFlags invocationFlags = InvocationFlags;
RuntimeType? declaringType = DeclaringType as RuntimeType;

if ((invocationFlags & INVOCATION_FLAGS.INVOCATION_FLAGS_NO_INVOKE) != 0)
if ((invocationFlags & InvocationFlags.NoInvoke) != 0)
{
if (declaringType != null && DeclaringType!.ContainsGenericParameters)
throw new InvalidOperationException(SR.Arg_UnboundGenField);
Expand Down Expand Up @@ -170,10 +170,10 @@ internal override RuntimeModule GetRuntimeModule()
[Diagnostics.DebuggerHidden]
public override void SetValue(object? obj, object? value, BindingFlags invokeAttr, Binder? binder, CultureInfo? culture)
{
INVOCATION_FLAGS invocationFlags = InvocationFlags;
InvocationFlags invocationFlags = InvocationFlags;
RuntimeType? declaringType = DeclaringType as RuntimeType;

if ((invocationFlags & INVOCATION_FLAGS.INVOCATION_FLAGS_NO_INVOKE) != 0)
if ((invocationFlags & InvocationFlags.NoInvoke) != 0)
{
if (declaringType != null && declaringType.ContainsGenericParameters)
throw new InvalidOperationException(SR.Arg_UnboundGenField);
Expand Down
Loading