diff --git a/src/coreclr/System.Private.CoreLib/System.Private.CoreLib.csproj b/src/coreclr/System.Private.CoreLib/System.Private.CoreLib.csproj index 727ed12604990b..cb9ca774e43b10 100644 --- a/src/coreclr/System.Private.CoreLib/System.Private.CoreLib.csproj +++ b/src/coreclr/System.Private.CoreLib/System.Private.CoreLib.csproj @@ -176,7 +176,6 @@ - @@ -187,14 +186,14 @@ - + - + diff --git a/src/coreclr/System.Private.CoreLib/src/System/Reflection/Emit/DynamicMethod.cs b/src/coreclr/System.Private.CoreLib/src/System/Reflection/Emit/DynamicMethod.cs index 8b6a412abed329..0e4741ea643d03 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/Reflection/Emit/DynamicMethod.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/Reflection/Emit/DynamicMethod.cs @@ -463,7 +463,7 @@ internal RuntimeMethodHandle GetMethodDescriptor() Span 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); diff --git a/src/coreclr/System.Private.CoreLib/src/System/Reflection/MethodBase.CoreCLR.cs b/src/coreclr/System.Private.CoreLib/src/System/Reflection/MethodBase.CoreCLR.cs index 89829cba3479e5..ef988b742a88ec 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/Reflection/MethodBase.CoreCLR.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/Reflection/MethodBase.CoreCLR.cs @@ -66,58 +66,6 @@ internal virtual Type[] GetParameterTypes() return parameterTypes; } - - private protected Span CheckArguments(ref StackAllocedArguments stackArgs, ReadOnlySpan parameters, Binder? binder, - BindingFlags invokeAttr, CultureInfo? culture, Signature sig) - { - Debug.Assert(Unsafe.SizeOf() == StackAllocedArguments.MaxStackAllocArgCount * Unsafe.SizeOf(), - "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 copyOfParameters = (parameters.Length <= StackAllocedArguments.MaxStackAllocArgCount) - ? MemoryMarshal.CreateSpan(ref stackArgs._arg0, parameters.Length) - : new Span(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 } } diff --git a/src/coreclr/System.Private.CoreLib/src/System/Reflection/RtFieldInfo.cs b/src/coreclr/System.Private.CoreLib/src/System/Reflection/RtFieldInfo.cs index 9aac5488e60de7..1e41f2eb01b6aa 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/Reflection/RtFieldInfo.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/Reflection/RtFieldInfo.cs @@ -17,26 +17,26 @@ 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 @@ -44,19 +44,19 @@ private INVOCATION_FLAGS InitializeInvocationFlags() 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 @@ -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); @@ -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); diff --git a/src/coreclr/System.Private.CoreLib/src/System/Reflection/RuntimeConstructorInfo.CoreCLR.cs b/src/coreclr/System.Private.CoreLib/src/System/Reflection/RuntimeConstructorInfo.CoreCLR.cs new file mode 100644 index 00000000000000..8ac8a44be9448c --- /dev/null +++ b/src/coreclr/System.Private.CoreLib/src/System/Reflection/RuntimeConstructorInfo.CoreCLR.cs @@ -0,0 +1,246 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Collections.Generic; +using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; +using System.Globalization; +using System.Runtime.CompilerServices; +using System.Text; +using System.Threading; +using RuntimeTypeCache = System.RuntimeType.RuntimeTypeCache; + +namespace System.Reflection +{ + internal sealed partial class RuntimeConstructorInfo : ConstructorInfo, IRuntimeMethodInfo + { + #region Private Data Members + private volatile RuntimeType m_declaringType; + private RuntimeTypeCache m_reflectedTypeCache; + private string? m_toString; + private ParameterInfo[]? m_parameters; // Created lazily when GetParameters() is called. +#pragma warning disable CA1823, 414, 169 + private object? _empty1; // These empties are used to ensure that RuntimeConstructorInfo and RuntimeMethodInfo are have a layout which is sufficiently similar + private object? _empty2; + private object? _empty3; +#pragma warning restore CA1823, 414, 169 + private IntPtr m_handle; + private MethodAttributes m_methodAttributes; + private BindingFlags m_bindingFlags; + private Signature? m_signature; + private InvocationFlags m_invocationFlags; + + internal InvocationFlags InvocationFlags + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get + { + InvocationFlags flags = m_invocationFlags; + if ((flags & InvocationFlags.Initialized) == 0) + { + flags = ComputeAndUpdateInvocationFlags(this, ref m_invocationFlags); + } + return flags; + } + } + #endregion + + #region Constructor + internal RuntimeConstructorInfo( + RuntimeMethodHandleInternal handle, RuntimeType declaringType, RuntimeTypeCache reflectedTypeCache, + MethodAttributes methodAttributes, BindingFlags bindingFlags) + { + m_bindingFlags = bindingFlags; + m_reflectedTypeCache = reflectedTypeCache; + m_declaringType = declaringType; + m_handle = handle.Value; + m_methodAttributes = methodAttributes; + } + #endregion + + #region NonPublic Methods + RuntimeMethodHandleInternal IRuntimeMethodInfo.Value => new RuntimeMethodHandleInternal(m_handle); + + internal override bool CacheEquals(object? o) => + o is RuntimeConstructorInfo m && m.m_handle == m_handle; + + private Signature Signature + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get + { + [MethodImpl(MethodImplOptions.NoInlining)] // move lazy sig generation out of the hot path + Signature LazyCreateSignature() + { + Signature newSig = new Signature(this, m_declaringType); + Volatile.Write(ref m_signature, newSig); + return newSig; + } + + return m_signature ?? LazyCreateSignature(); + } + } + + private RuntimeType ReflectedTypeInternal => m_reflectedTypeCache.GetRuntimeType(); + + internal BindingFlags BindingFlags => m_bindingFlags; + #endregion + + #region Object Overrides + public override string ToString() + { + if (m_toString == null) + { + var sbName = new ValueStringBuilder(MethodNameBufferSize); + + // "Void" really doesn't make sense here. But we'll keep it for compat reasons. + sbName.Append("Void "); + + sbName.Append(Name); + + sbName.Append('('); + AppendParameters(ref sbName, GetParameterTypes(), CallingConvention); + sbName.Append(')'); + + m_toString = sbName.ToString(); + } + + return m_toString; + } + #endregion + + #region ICustomAttributeProvider + public override object[] GetCustomAttributes(bool inherit) + { + return CustomAttribute.GetCustomAttributes(this, (typeof(object) as RuntimeType)!); + } + + public override object[] GetCustomAttributes(Type attributeType, bool inherit) + { + if (attributeType == null) + throw new ArgumentNullException(nameof(attributeType)); + + if (attributeType.UnderlyingSystemType is not RuntimeType attributeRuntimeType) + throw new ArgumentException(SR.Arg_MustBeType, nameof(attributeType)); + + return CustomAttribute.GetCustomAttributes(this, attributeRuntimeType); + } + + public override bool IsDefined(Type attributeType, bool inherit) + { + if (attributeType == null) + throw new ArgumentNullException(nameof(attributeType)); + + if (attributeType.UnderlyingSystemType is not RuntimeType attributeRuntimeType) + throw new ArgumentException(SR.Arg_MustBeType, nameof(attributeType)); + + return CustomAttribute.IsDefined(this, attributeRuntimeType); + } + + public override IList GetCustomAttributesData() + { + return RuntimeCustomAttributeData.GetCustomAttributesInternal(this); + } + #endregion + + #region MemberInfo Overrides + public override string Name => RuntimeMethodHandle.GetName(this); + public override MemberTypes MemberType => MemberTypes.Constructor; + + public override Type? DeclaringType => m_reflectedTypeCache.IsGlobal ? null : m_declaringType; + + public sealed override bool HasSameMetadataDefinitionAs(MemberInfo other) => HasSameMetadataDefinitionAsCore(other); + + public override Type? ReflectedType => m_reflectedTypeCache.IsGlobal ? null : ReflectedTypeInternal; + + public override int MetadataToken => RuntimeMethodHandle.GetMethodDef(this); + public override Module Module => GetRuntimeModule(); + + internal RuntimeType GetRuntimeType() { return m_declaringType; } + internal RuntimeModule GetRuntimeModule() { return RuntimeTypeHandle.GetModule(m_declaringType); } + internal RuntimeAssembly GetRuntimeAssembly() { return GetRuntimeModule().GetRuntimeAssembly(); } + #endregion + + #region MethodBase Overrides + + // This seems to always returns System.Void. + internal override Type GetReturnType() { return Signature.ReturnType; } + + internal override ParameterInfo[] GetParametersNoCopy() => + m_parameters ??= RuntimeParameterInfo.GetParameters(this, this, Signature); + + public override ParameterInfo[] GetParameters() + { + ParameterInfo[] parameters = GetParametersNoCopy(); + + if (parameters.Length == 0) + return parameters; + + ParameterInfo[] ret = new ParameterInfo[parameters.Length]; + Array.Copy(parameters, ret, parameters.Length); + return ret; + } + + public override MethodImplAttributes GetMethodImplementationFlags() + { + return RuntimeMethodHandle.GetImplAttributes(this); + } + + public override RuntimeMethodHandle MethodHandle => new RuntimeMethodHandle(this); + + public override MethodAttributes Attributes => m_methodAttributes; + + public override CallingConventions CallingConvention => Signature.CallingConvention; + + private RuntimeType[] ArgumentTypes => Signature.Arguments; + + [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2059:RunClassConstructor", + Justification = "This ConstructorInfo instance represents the static constructor itself, so if this object was created, the static constructor exists.")] + private void InvokeClassConstructor() + { + Debug.Assert((InvocationFlags & InvocationFlags.RunClassConstructor) != 0); + var declaringType = DeclaringType; + + if (declaringType != null) + { + RuntimeHelpers.RunClassConstructor(declaringType.TypeHandle); + } + else + { + RuntimeHelpers.RunModuleConstructor(Module.ModuleHandle); + } + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private object? InvokeWorker(object? obj, BindingFlags invokeAttr, in Span arguments) + { + bool wrapExceptions = (invokeAttr & BindingFlags.DoNotWrapExceptions) == 0; + return RuntimeMethodHandle.InvokeMethod(obj, arguments, Signature, false, wrapExceptions); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private object InvokeCtorWorker(BindingFlags invokeAttr, in Span arguments) + { + bool wrapExceptions = (invokeAttr & BindingFlags.DoNotWrapExceptions) == 0; + 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.")] + public override MethodBody? GetMethodBody() + { + RuntimeMethodBody? mb = RuntimeMethodHandle.GetMethodBody(this, ReflectedTypeInternal); + if (mb != null) + mb._methodBase = this; + return mb; + } + + public override bool IsSecurityCritical => true; + + public override bool IsSecuritySafeCritical => false; + + public override bool IsSecurityTransparent => false; + + public override bool ContainsGenericParameters => DeclaringType != null && DeclaringType.ContainsGenericParameters; + #endregion + } +} diff --git a/src/coreclr/System.Private.CoreLib/src/System/Reflection/RuntimeConstructorInfo.cs b/src/coreclr/System.Private.CoreLib/src/System/Reflection/RuntimeConstructorInfo.cs deleted file mode 100644 index a930e0964a895e..00000000000000 --- a/src/coreclr/System.Private.CoreLib/src/System/Reflection/RuntimeConstructorInfo.cs +++ /dev/null @@ -1,415 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using System.Collections.Generic; -using System.Diagnostics; -using System.Diagnostics.CodeAnalysis; -using System.Globalization; -using System.Runtime.CompilerServices; -using System.Text; -using System.Threading; -using RuntimeTypeCache = System.RuntimeType.RuntimeTypeCache; - -namespace System.Reflection -{ - internal sealed class RuntimeConstructorInfo : ConstructorInfo, IRuntimeMethodInfo - { - #region Private Data Members - private volatile RuntimeType m_declaringType; - private RuntimeTypeCache m_reflectedTypeCache; - private string? m_toString; - private ParameterInfo[]? m_parameters; // Created lazily when GetParameters() is called. -#pragma warning disable CA1823, 414, 169 - private object? _empty1; // These empties are used to ensure that RuntimeConstructorInfo and RuntimeMethodInfo are have a layout which is sufficiently similar - private object? _empty2; - private object? _empty3; -#pragma warning restore CA1823, 414, 169 - private IntPtr m_handle; - private MethodAttributes m_methodAttributes; - private BindingFlags m_bindingFlags; - private Signature? m_signature; - private INVOCATION_FLAGS m_invocationFlags; - - 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(); - } - return flags; - } - } - #endregion - - #region Constructor - internal RuntimeConstructorInfo( - RuntimeMethodHandleInternal handle, RuntimeType declaringType, RuntimeTypeCache reflectedTypeCache, - MethodAttributes methodAttributes, BindingFlags bindingFlags) - { - m_bindingFlags = bindingFlags; - m_reflectedTypeCache = reflectedTypeCache; - m_declaringType = declaringType; - m_handle = handle.Value; - m_methodAttributes = methodAttributes; - } - #endregion - - #region NonPublic Methods - RuntimeMethodHandleInternal IRuntimeMethodInfo.Value => new RuntimeMethodHandleInternal(m_handle); - - internal override bool CacheEquals(object? o) => - o is RuntimeConstructorInfo m && m.m_handle == m_handle; - - private Signature Signature - { - [MethodImpl(MethodImplOptions.AggressiveInlining)] - get - { - [MethodImpl(MethodImplOptions.NoInlining)] // move lazy sig generation out of the hot path - Signature LazyCreateSignature() - { - Signature newSig = new Signature(this, m_declaringType); - Volatile.Write(ref m_signature, newSig); - return newSig; - } - - return m_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 - - #region Object Overrides - public override string ToString() - { - if (m_toString == null) - { - var sbName = new ValueStringBuilder(MethodNameBufferSize); - - // "Void" really doesn't make sense here. But we'll keep it for compat reasons. - sbName.Append("Void "); - - sbName.Append(Name); - - sbName.Append('('); - AppendParameters(ref sbName, GetParameterTypes(), CallingConvention); - sbName.Append(')'); - - m_toString = sbName.ToString(); - } - - return m_toString; - } - #endregion - - #region ICustomAttributeProvider - public override object[] GetCustomAttributes(bool inherit) - { - return CustomAttribute.GetCustomAttributes(this, (typeof(object) as RuntimeType)!); - } - - public override object[] GetCustomAttributes(Type attributeType, bool inherit) - { - if (attributeType == null) - throw new ArgumentNullException(nameof(attributeType)); - - if (attributeType.UnderlyingSystemType is not RuntimeType attributeRuntimeType) - throw new ArgumentException(SR.Arg_MustBeType, nameof(attributeType)); - - return CustomAttribute.GetCustomAttributes(this, attributeRuntimeType); - } - - public override bool IsDefined(Type attributeType, bool inherit) - { - if (attributeType == null) - throw new ArgumentNullException(nameof(attributeType)); - - if (attributeType.UnderlyingSystemType is not RuntimeType attributeRuntimeType) - throw new ArgumentException(SR.Arg_MustBeType, nameof(attributeType)); - - return CustomAttribute.IsDefined(this, attributeRuntimeType); - } - - public override IList GetCustomAttributesData() - { - return RuntimeCustomAttributeData.GetCustomAttributesInternal(this); - } - #endregion - - #region MemberInfo Overrides - public override string Name => RuntimeMethodHandle.GetName(this); - public override MemberTypes MemberType => MemberTypes.Constructor; - - public override Type? DeclaringType => m_reflectedTypeCache.IsGlobal ? null : m_declaringType; - - public sealed override bool HasSameMetadataDefinitionAs(MemberInfo other) => HasSameMetadataDefinitionAsCore(other); - - public override Type? ReflectedType => m_reflectedTypeCache.IsGlobal ? null : ReflectedTypeInternal; - - public override int MetadataToken => RuntimeMethodHandle.GetMethodDef(this); - public override Module Module => GetRuntimeModule(); - - internal RuntimeType GetRuntimeType() { return m_declaringType; } - internal RuntimeModule GetRuntimeModule() { return RuntimeTypeHandle.GetModule(m_declaringType); } - internal RuntimeAssembly GetRuntimeAssembly() { return GetRuntimeModule().GetRuntimeAssembly(); } - #endregion - - #region MethodBase Overrides - - // This seems to always returns System.Void. - internal override Type GetReturnType() { return Signature.ReturnType; } - - internal override ParameterInfo[] GetParametersNoCopy() => - m_parameters ??= RuntimeParameterInfo.GetParameters(this, this, Signature); - - public override ParameterInfo[] GetParameters() - { - ParameterInfo[] parameters = GetParametersNoCopy(); - - if (parameters.Length == 0) - return parameters; - - ParameterInfo[] ret = new ParameterInfo[parameters.Length]; - Array.Copy(parameters, ret, parameters.Length); - return ret; - } - - public override MethodImplAttributes GetMethodImplementationFlags() - { - return RuntimeMethodHandle.GetImplAttributes(this); - } - - public override RuntimeMethodHandle MethodHandle => new RuntimeMethodHandle(this); - - public override MethodAttributes Attributes => m_methodAttributes; - - 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)); - - // 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(); - - // ctor is generic or on a generic class - else if (declaringType.ContainsGenericParameters) - { - throw new MemberAccessException( - SR.Format(SR.Acc_CreateGenericEx, declaringType)); - } - - // ctor is declared on System.Void - else if (declaringType == typeof(void)) - throw new MemberAccessException(SR.Access_Void); - } - - [DoesNotReturn] - internal void ThrowNoInvokeException() - { - 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(); - } - - [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) - { - 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 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.CopyTo, as parameters.GetType() might not actually be typeof(object[]) - for (int index = 0; index < arguments.Length; index++) - parameters![index] = arguments[index]; - - return retValue; - } - - [RequiresUnreferencedCode("Trimming may change method bodies. For example it can change some instructions, remove branches or local variables.")] - public override MethodBody? GetMethodBody() - { - RuntimeMethodBody? mb = RuntimeMethodHandle.GetMethodBody(this, ReflectedTypeInternal); - if (mb != null) - mb._methodBase = this; - return mb; - } - - public override bool IsSecurityCritical => true; - - public override bool IsSecuritySafeCritical => false; - - public override bool IsSecurityTransparent => false; - - 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 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.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 - } -} diff --git a/src/coreclr/System.Private.CoreLib/src/System/Reflection/RuntimeMethodInfo.cs b/src/coreclr/System.Private.CoreLib/src/System/Reflection/RuntimeMethodInfo.CoreCLR.cs similarity index 76% rename from src/coreclr/System.Private.CoreLib/src/System/Reflection/RuntimeMethodInfo.cs rename to src/coreclr/System.Private.CoreLib/src/System/Reflection/RuntimeMethodInfo.CoreCLR.cs index 7810c94dcc9e17..4710e6d3b7e6e2 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/Reflection/RuntimeMethodInfo.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/Reflection/RuntimeMethodInfo.CoreCLR.cs @@ -13,7 +13,7 @@ namespace System.Reflection { - internal sealed class RuntimeMethodInfo : MethodInfo, IRuntimeMethodInfo + internal sealed partial class RuntimeMethodInfo : MethodInfo, IRuntimeMethodInfo { #region Private Data Members private IntPtr m_handle; @@ -27,59 +27,21 @@ internal sealed class RuntimeMethodInfo : MethodInfo, IRuntimeMethodInfo private Signature? m_signature; private RuntimeType m_declaringType; private object? m_keepalive; - private INVOCATION_FLAGS m_invocationFlags; + private InvocationFlags m_invocationFlags; - internal INVOCATION_FLAGS InvocationFlags + internal InvocationFlags InvocationFlags { [MethodImpl(MethodImplOptions.AggressiveInlining)] get { - [MethodImpl(MethodImplOptions.NoInlining)] // move lazy invocation flags population out of the hot path - INVOCATION_FLAGS LazyCreateInvocationFlags() + InvocationFlags flags = m_invocationFlags; + if ((flags & InvocationFlags.Initialized) == 0) { - INVOCATION_FLAGS invocationFlags = INVOCATION_FLAGS.INVOCATION_FLAGS_UNKNOWN; - - Type? declaringType = DeclaringType; - - // - // first take care of all the NO_INVOKE cases. - if (ContainsGenericParameters || - IsDisallowedByRefType(ReturnType) || - (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 - { - // Check for byref-like types - if ((declaringType != null && declaringType.IsByRefLike) || ReturnType.IsByRefLike) - invocationFlags |= INVOCATION_FLAGS.INVOCATION_FLAGS_CONTAINS_STACK_POINTERS; - } - - 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; } } - - private static bool IsDisallowedByRefType(Type type) - { - if (!type.IsByRef) - return false; - - Type elementType = type.GetElementType()!; - return elementType.IsByRefLike || elementType == typeof(void); - } #endregion #region Constructor @@ -339,6 +301,8 @@ public override MethodImplAttributes GetMethodImplementationFlags() public override CallingConventions CallingConvention => Signature.CallingConvention; + private RuntimeType[] ArgumentTypes => Signature.Arguments; + [RequiresUnreferencedCode("Trimming may change method bodies. For example it can change some instructions, remove branches or local variables.")] public override MethodBody? GetMethodBody() { @@ -352,106 +316,26 @@ public override MethodImplAttributes GetMethodImplementationFlags() #region Invocation Logic(On MemberBase) [MethodImpl(MethodImplOptions.AggressiveInlining)] - private void CheckConsistency(object? target) - { - // only test instance methods - if ((m_methodAttributes & MethodAttributes.Static) == 0) - { - if (!m_declaringType.IsInstanceOfType(target)) - { - if (target == null) - throw new TargetException(SR.RFLCT_Targ_StatMethReqTarg); - else - throw new TargetException(SR.RFLCT_Targ_ITargMismatch); - } - } - } - - [DoesNotReturn] - private void ThrowNoInvokeException() - { - // method is on a class that contains stack pointers - if ((InvocationFlags & INVOCATION_FLAGS.INVOCATION_FLAGS_CONTAINS_STACK_POINTERS) != 0) - { - throw new NotSupportedException(); - } - // method is vararg - else if ((CallingConvention & CallingConventions.VarArgs) == CallingConventions.VarArgs) - { - throw new NotSupportedException(); - } - // method is generic or on a generic class - else if (DeclaringType!.ContainsGenericParameters || ContainsGenericParameters) - { - throw new InvalidOperationException(SR.Arg_UnboundGenParam); - } - // method is abstract class - else if (IsAbstract) - { - throw new MemberAccessException(); - } - else if (ReturnType.IsByRef) - { - Type elementType = ReturnType.GetElementType()!; - if (elementType.IsByRefLike) - throw new NotSupportedException(SR.NotSupported_ByRefToByRefLikeReturn); - if (elementType == typeof(void)) - throw new NotSupportedException(SR.NotSupported_ByRefToVoidReturn); - } - - throw new TargetException(); - } - - [DebuggerStepThroughAttribute] - [Diagnostics.DebuggerHidden] - public override object? Invoke(object? obj, BindingFlags invokeAttr, Binder? binder, object?[]? parameters, CultureInfo? culture) + private object? InvokeWorker(object? obj, BindingFlags invokeAttr, in Span arguments) { - // INVOCATION_FLAGS_CONTAINS_STACK_POINTERS means that the struct (either the declaring type or the return type) - // contains pointers that point to the stack. This is either a ByRef or a TypedReference. These structs cannot - // be boxed and thus cannot be invoked through reflection which only deals with boxed value type objects. - if ((InvocationFlags & (INVOCATION_FLAGS.INVOCATION_FLAGS_NO_INVOKE | INVOCATION_FLAGS.INVOCATION_FLAGS_CONTAINS_STACK_POINTERS)) != 0) - ThrowNoInvokeException(); - - // check basic method consistency. This call will throw if there are problems in the target/method relationship - CheckConsistency(obj); - - Signature sig = Signature; - int actualCount = (parameters != null) ? parameters.Length : 0; - if (sig.Arguments.Length != actualCount) - throw new TargetParameterCountException(SR.Arg_ParmCnt); - - StackAllocedArguments stackArgs = default; // try to avoid intermediate array allocation if possible - Span arguments = default; - if (actualCount != 0) - { - arguments = CheckArguments(ref stackArgs, parameters!, binder, invokeAttr, culture, sig); - } - bool wrapExceptions = (invokeAttr & BindingFlags.DoNotWrapExceptions) == 0; - object? retValue = RuntimeMethodHandle.InvokeMethod(obj, arguments, Signature, false, wrapExceptions); - - // copy out. This should be made only if ByRef are present. - // n.b. cannot use Span.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(obj, arguments, Signature, false, wrapExceptions); } [DebuggerStepThroughAttribute] [Diagnostics.DebuggerHidden] internal object? InvokeOneParameter(object? obj, BindingFlags invokeAttr, Binder? binder, object? parameter, CultureInfo? culture) { - // INVOCATION_FLAGS_CONTAINS_STACK_POINTERS means that the struct (either the declaring type or the return type) + // ContainsStackPointers means that the struct (either the declaring type or the return type) // contains pointers that point to the stack. This is either a ByRef or a TypedReference. These structs cannot // be boxed and thus cannot be invoked through reflection which only deals with boxed value type objects. - if ((InvocationFlags & (INVOCATION_FLAGS.INVOCATION_FLAGS_NO_INVOKE | INVOCATION_FLAGS.INVOCATION_FLAGS_CONTAINS_STACK_POINTERS)) != 0) + if ((InvocationFlags & (InvocationFlags.NoInvoke | InvocationFlags.ContainsStackPointers)) != 0) { ThrowNoInvokeException(); } // check basic method consistency. This call will throw if there are problems in the target/method relationship - CheckConsistency(obj); + ValidateInvokeTarget(obj); Signature sig = Signature; if (sig.Arguments.Length != 1) @@ -460,7 +344,7 @@ private void ThrowNoInvokeException() } StackAllocedArguments stackArgs = default; - Span arguments = CheckArguments(ref stackArgs, new ReadOnlySpan(ref parameter, 1), binder, invokeAttr, culture, sig); + Span arguments = CheckArguments(ref stackArgs, new ReadOnlySpan(ref parameter, 1), binder, invokeAttr, culture, sig.Arguments); bool wrapExceptions = (invokeAttr & BindingFlags.DoNotWrapExceptions) == 0; return RuntimeMethodHandle.InvokeMethod(obj, arguments, Signature, constructor: false, wrapExceptions); diff --git a/src/libraries/System.Memory/tests/Span/Reflection.cs b/src/libraries/System.Memory/tests/Span/Reflection.cs index dbdcc7dbdcd336..89fc722414348e 100644 --- a/src/libraries/System.Memory/tests/Span/Reflection.cs +++ b/src/libraries/System.Memory/tests/Span/Reflection.cs @@ -65,7 +65,6 @@ public static void MemoryMarshal_GenericStaticReturningSpan() } [Fact] - [ActiveIssue("https://github.com/mono/mono/issues/14993", TestRuntimes.Mono)] public static void Span_Constructor() { Type type = typeof(Span); @@ -102,7 +101,6 @@ public static void Span_StaticOperator() } [Fact] - [ActiveIssue("https://github.com/mono/mono/issues/14998", TestRuntimes.Mono)] public static void Span_InstanceMethod() { Type type = typeof(Span); @@ -112,7 +110,6 @@ public static void Span_InstanceMethod() } [Fact] - [ActiveIssue("https://github.com/mono/mono/issues/14993", TestRuntimes.Mono)] public static void ReadOnlySpan_Constructor() { Type type = typeof(ReadOnlySpan); @@ -149,7 +146,6 @@ public static void ReadOnlySpan_Operator() } [Fact] - [ActiveIssue("https://github.com/mono/mono/issues/14998", TestRuntimes.Mono)] public static void ReadOnlySpan_InstanceMethod() { Type type = typeof(ReadOnlySpan); @@ -159,7 +155,6 @@ public static void ReadOnlySpan_InstanceMethod() } [Fact] - [ActiveIssue("https://github.com/mono/mono/issues/14998", TestRuntimes.Mono)] public static void Memory_PropertyReturningSpan() { Type type = typeof(Memory); @@ -169,7 +164,6 @@ public static void Memory_PropertyReturningSpan() } [Fact] - [ActiveIssue("https://github.com/mono/mono/issues/14962", TestRuntimes.Mono)] public static void ReadOnlyMemory_PropertyReturningReadOnlySpan() { Type type = typeof(ReadOnlyMemory); diff --git a/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems b/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems index 68f5c3af638c5f..600f29f4efaabe 100644 --- a/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems +++ b/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems @@ -602,6 +602,7 @@ + @@ -634,7 +635,9 @@ + + diff --git a/src/coreclr/System.Private.CoreLib/src/System/Reflection/INVOCATION_FLAGS.cs b/src/libraries/System.Private.CoreLib/src/System/Reflection/InvocationFlags.cs similarity index 50% rename from src/coreclr/System.Private.CoreLib/src/System/Reflection/INVOCATION_FLAGS.cs rename to src/libraries/System.Private.CoreLib/src/System/Reflection/InvocationFlags.cs index b6a55518914a22..1288e853aeed5c 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/Reflection/INVOCATION_FLAGS.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Reflection/InvocationFlags.cs @@ -8,31 +8,26 @@ namespace System.Reflection // so be careful if you change them // [Flags] - internal enum INVOCATION_FLAGS : uint + internal enum InvocationFlags : uint { - INVOCATION_FLAGS_UNKNOWN = 0x00000000, - INVOCATION_FLAGS_INITIALIZED = 0x00000001, + Unknown = 0x00000000, + Initialized = 0x00000001, // it's used for both method and field to signify that no access is allowed - INVOCATION_FLAGS_NO_INVOKE = 0x00000002, + NoInvoke = 0x00000002, // Set for static ctors, to ensure that the static ctor is run as a static ctor before it is explicitly executed via reflection - INVOCATION_FLAGS_RUN_CLASS_CONSTRUCTOR = 0x00000004, + RunClassConstructor = 0x00000004, // Set for static ctors and ctors on abstract types, which // can be invoked only if the "this" object is provided (even if it's null). - INVOCATION_FLAGS_NO_CTOR_INVOKE = 0x00000008, + NoConstructorInvoke = 0x00000008, // because field and method are different we can reuse the same bits // method - INVOCATION_FLAGS_IS_CTOR = 0x00000010, + IsConstructor = 0x00000010, /* unused 0x00000020 */ /* unused 0x00000040 */ - INVOCATION_FLAGS_IS_DELEGATE_CTOR = 0x00000080, - INVOCATION_FLAGS_CONTAINS_STACK_POINTERS = 0x00000100, + IsDelegateConstructor = 0x00000080, + ContainsStackPointers = 0x00000100, // field - INVOCATION_FLAGS_SPECIAL_FIELD = 0x00000010, - INVOCATION_FLAGS_FIELD_SPECIAL_CAST = 0x00000020, - - // temporary flag used for flagging invocation of method vs ctor - // this flag never appears on the instance m_invocationFlag and is simply - // passed down from within ConstructorInfo.Invoke() - INVOCATION_FLAGS_CONSTRUCTOR_INVOKE = 0x10000000, + SpecialField = 0x00000010, + FieldSpecialCast = 0x00000020, } } diff --git a/src/libraries/System.Private.CoreLib/src/System/Reflection/MethodBase.cs b/src/libraries/System.Private.CoreLib/src/System/Reflection/MethodBase.cs index 4db2c19e3b23c7..d14d3339b05e11 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Reflection/MethodBase.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Reflection/MethodBase.cs @@ -1,11 +1,13 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -using System.Globalization; using System.Diagnostics; using System.Diagnostics.CodeAnalysis; +using System.Globalization; using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; using System.Text; +using Internal.Runtime.CompilerServices; namespace System.Reflection { @@ -120,5 +122,74 @@ internal static void AppendParameters(ref ValueStringBuilder sbParamList, Type[] sbParamList.Append("..."); } } + + private protected void ValidateInvokeTarget(object? target) + { + // Confirm member invocation has an instance and is of the correct type + if (!IsStatic) + { + if (target == null) + { + throw new TargetException(SR.RFLCT_Targ_StatMethReqTarg); + } + + if (!DeclaringType!.IsInstanceOfType(target)) + { + throw new TargetException(SR.RFLCT_Targ_ITargMismatch); + } + } + } + + private protected Span CheckArguments(ref StackAllocedArguments stackArgs, ReadOnlySpan parameters, Binder? binder, + BindingFlags invokeAttr, CultureInfo? culture, RuntimeType[] sigTypes) + { + Debug.Assert(Unsafe.SizeOf() == StackAllocedArguments.MaxStackAllocArgCount * Unsafe.SizeOf(), + "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 copyOfParameters = (parameters.Length <= StackAllocedArguments.MaxStackAllocArgCount) + ? MemoryMarshal.CreateSpan(ref stackArgs._arg0, parameters.Length) + : new Span(new object?[parameters.Length]); + + ParameterInfo[]? p = null; + for (int i = 0; i < parameters.Length; i++) + { + object? arg = parameters[i]; + RuntimeType argRT = sigTypes[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 + } } } diff --git a/src/libraries/System.Private.CoreLib/src/System/Reflection/RuntimeConstructorInfo.cs b/src/libraries/System.Private.CoreLib/src/System/Reflection/RuntimeConstructorInfo.cs new file mode 100644 index 00000000000000..e3502601407cff --- /dev/null +++ b/src/libraries/System.Private.CoreLib/src/System/Reflection/RuntimeConstructorInfo.cs @@ -0,0 +1,178 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; +using System.Globalization; +using System.Runtime.CompilerServices; + +namespace System.Reflection +{ + internal sealed partial class RuntimeConstructorInfo : ConstructorInfo + { + [MethodImpl(MethodImplOptions.NoInlining)] // move lazy invocation flags population out of the hot path + private static InvocationFlags ComputeAndUpdateInvocationFlags(ConstructorInfo constructorInfo, ref InvocationFlags flagsToUpdate) + { + InvocationFlags invocationFlags = InvocationFlags.IsConstructor; // this is a given + + Type? declaringType = constructorInfo.DeclaringType; + + if (declaringType == typeof(void) + || declaringType != null && declaringType.ContainsGenericParameters // Enclosing type has unbound generics + || (constructorInfo.CallingConvention & CallingConventions.VarArgs) == CallingConventions.VarArgs // Managed varargs + ) + { + invocationFlags |= InvocationFlags.NoInvoke; + } + else if (constructorInfo.IsStatic) + { + invocationFlags |= InvocationFlags.RunClassConstructor | InvocationFlags.NoConstructorInvoke; + } + else if (declaringType != null && declaringType.IsAbstract) + { + invocationFlags |= InvocationFlags.NoConstructorInvoke; + } + else + { + // Check for byref-like types + if (declaringType != null && declaringType.IsByRefLike) + invocationFlags |= InvocationFlags.ContainsStackPointers; + + // Check for attempt to create a delegate class. + if (typeof(Delegate).IsAssignableFrom(constructorInfo.DeclaringType)) + invocationFlags |= InvocationFlags.IsDelegateConstructor; + } + + invocationFlags |= InvocationFlags.Initialized; + flagsToUpdate = invocationFlags; // accesses are guaranteed atomic + return invocationFlags; + } + + 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)); + + // 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(); + + // ctor is generic or on a generic class + else if (declaringType.ContainsGenericParameters) + { + throw new MemberAccessException( + SR.Format(SR.Acc_CreateGenericEx, declaringType)); + } + + // ctor is declared on System.Void + else if (declaringType == typeof(void)) + throw new MemberAccessException(SR.Access_Void); + } + + [DoesNotReturn] + internal void ThrowNoInvokeException() + { + 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(); + } + + [DebuggerStepThroughAttribute] + [Diagnostics.DebuggerHidden] + public override object? Invoke( + object? obj, BindingFlags invokeAttr, Binder? binder, object?[]? parameters, CultureInfo? culture) + { + if ((InvocationFlags & InvocationFlags.NoInvoke) != 0) + ThrowNoInvokeException(); + + ValidateInvokeTarget(obj); + + if ((InvocationFlags & InvocationFlags.RunClassConstructor) != 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. + InvokeClassConstructor(); + return null; + } + + // Correct number of arguments supplied + int actualCount = (parameters is null) ? 0 : parameters.Length; + if (ArgumentTypes.Length != actualCount) + { + throw new TargetParameterCountException(SR.Arg_ParmCnt); + } + + StackAllocedArguments stackArgs = default; + Span arguments = default; + if (actualCount != 0) + { + arguments = CheckArguments(ref stackArgs, parameters, binder, invokeAttr, culture, ArgumentTypes); + } + + object? retValue = InvokeWorker(obj, invokeAttr, arguments); + + // copy out. This should be made only if ByRef are present. + // n.b. cannot use Span.CopyTo, as parameters.GetType() might not actually be typeof(object[]) + for (int index = 0; index < arguments.Length; index++) + { + parameters![index] = arguments[index]; + } + + return retValue; + } + + [DebuggerStepThroughAttribute] + [Diagnostics.DebuggerHidden] + public override object Invoke(BindingFlags invokeAttr, Binder? binder, object?[]? parameters, CultureInfo? culture) + { + if ((InvocationFlags & (InvocationFlags.NoInvoke | InvocationFlags.ContainsStackPointers | InvocationFlags.NoConstructorInvoke)) != 0) + { + ThrowNoInvokeException(); + } + + // We don't need to explicitly invoke the class constructor here, + // JIT will insert the call to .cctor in the instance ctor. + + // Correct number of arguments supplied + int actualCount = (parameters is null) ? 0 : parameters.Length; + if (ArgumentTypes.Length != actualCount) + { + throw new TargetParameterCountException(SR.Arg_ParmCnt); + } + + StackAllocedArguments stackArgs = default; + Span arguments = default; + if (actualCount != 0) + { + arguments = CheckArguments(ref stackArgs, parameters, binder, invokeAttr, culture, ArgumentTypes); + } + + object retValue = InvokeCtorWorker(invokeAttr, arguments); + + // copy out. This should be made only if ByRef are present. + // n.b. cannot use Span.CopyTo, as parameters.GetType() might not actually be typeof(object[]) + for (int index = 0; index < arguments.Length; index++) + parameters![index] = arguments[index]; + + return retValue; + } + } +} \ No newline at end of file diff --git a/src/libraries/System.Private.CoreLib/src/System/Reflection/RuntimeMethodInfo.cs b/src/libraries/System.Private.CoreLib/src/System/Reflection/RuntimeMethodInfo.cs new file mode 100644 index 00000000000000..7d2f60c2a78167 --- /dev/null +++ b/src/libraries/System.Private.CoreLib/src/System/Reflection/RuntimeMethodInfo.cs @@ -0,0 +1,136 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; +using System.Globalization; +using System.Runtime.CompilerServices; + +namespace System.Reflection +{ + internal sealed partial class RuntimeMethodInfo : MethodInfo + { + [MethodImpl(MethodImplOptions.NoInlining)] // move lazy invocation flags population out of the hot path + private static InvocationFlags ComputeAndUpdateInvocationFlags(MethodInfo methodInfo, ref InvocationFlags flagsToUpdate) + { + InvocationFlags invocationFlags = InvocationFlags.Unknown; + + Type? declaringType = methodInfo.DeclaringType; + + if (methodInfo.ContainsGenericParameters // Method has unbound generics + || IsDisallowedByRefType(methodInfo.ReturnType) // Return type is an invalid by-ref (i.e., by-ref-like or void*) + || (methodInfo.CallingConvention & CallingConventions.VarArgs) == CallingConventions.VarArgs // Managed varargs + ) + { + invocationFlags = InvocationFlags.NoInvoke; + } + else + { + if (declaringType != null) + { + if (declaringType.ContainsGenericParameters) // Enclosing type has unbound generics + { + invocationFlags = InvocationFlags.NoInvoke; + } + else if (declaringType.IsByRefLike) // Check for byref-like types + { + invocationFlags |= InvocationFlags.ContainsStackPointers; + } + } + + if (methodInfo.ReturnType.IsByRefLike) // Check for byref-like types for return + { + invocationFlags |= InvocationFlags.ContainsStackPointers; + } + } + + invocationFlags |= InvocationFlags.Initialized; + flagsToUpdate = invocationFlags; // accesses are guaranteed atomic + return invocationFlags; + + static bool IsDisallowedByRefType(Type type) + { + if (!type.IsByRef) + return false; + + Type elementType = type.GetElementType()!; + return elementType.IsByRefLike || elementType == typeof(void); + } + } + + [DoesNotReturn] + private void ThrowNoInvokeException() + { + // method is on a class that contains stack pointers + if ((InvocationFlags & InvocationFlags.ContainsStackPointers) != 0) + { + throw new NotSupportedException(); + } + // method is vararg + else if ((CallingConvention & CallingConventions.VarArgs) == CallingConventions.VarArgs) + { + throw new NotSupportedException(); + } + // method is generic or on a generic class + else if (DeclaringType!.ContainsGenericParameters || ContainsGenericParameters) + { + throw new InvalidOperationException(SR.Arg_UnboundGenParam); + } + // method is abstract class + else if (IsAbstract) + { + throw new MemberAccessException(); + } + else if (ReturnType.IsByRef) + { + Type elementType = ReturnType.GetElementType()!; + if (elementType.IsByRefLike) + throw new NotSupportedException(SR.NotSupported_ByRefToByRefLikeReturn); + if (elementType == typeof(void)) + throw new NotSupportedException(SR.NotSupported_ByRefToVoidReturn); + } + + throw new TargetException(); + } + + [DebuggerStepThroughAttribute] + [Diagnostics.DebuggerHidden] + public override object? Invoke(object? obj, BindingFlags invokeAttr, Binder? binder, object?[]? parameters, CultureInfo? culture) + { + // ContainsStackPointers means that the struct (either the declaring type or the return type) + // contains pointers that point to the stack. This is either a ByRef or a TypedReference. These structs cannot + // be boxed and thus cannot be invoked through reflection which only deals with boxed value type objects. + if ((InvocationFlags & (InvocationFlags.NoInvoke | InvocationFlags.ContainsStackPointers)) != 0) + { + ThrowNoInvokeException(); + } + + ValidateInvokeTarget(obj); + + // Correct number of arguments supplied + int actualCount = (parameters is null) ? 0 : parameters.Length; + if (ArgumentTypes.Length != actualCount) + { + throw new TargetParameterCountException(SR.Arg_ParmCnt); + } + + StackAllocedArguments stackArgs = default; // try to avoid intermediate array allocation if possible + Span arguments = default; + if (actualCount != 0) + { + arguments = CheckArguments(ref stackArgs, parameters!, binder, invokeAttr, culture, ArgumentTypes); + } + + object? retValue = InvokeWorker(obj, invokeAttr, arguments); + + // copy out. This should be made only if ByRef are present. + // n.b. cannot use Span.CopyTo, as parameters.GetType() might not actually be typeof(object[]) + for (int index = 0; index < arguments.Length; index++) + { + parameters![index] = arguments[index]; + } + + return retValue; + } + } +} \ No newline at end of file diff --git a/src/mono/System.Private.CoreLib/System.Private.CoreLib.csproj b/src/mono/System.Private.CoreLib/System.Private.CoreLib.csproj index 64ee4cbd6ac644..622b664f10b801 100644 --- a/src/mono/System.Private.CoreLib/System.Private.CoreLib.csproj +++ b/src/mono/System.Private.CoreLib/System.Private.CoreLib.csproj @@ -202,7 +202,7 @@ - + diff --git a/src/mono/System.Private.CoreLib/src/System/Reflection/RuntimeMethodInfo.cs b/src/mono/System.Private.CoreLib/src/System/Reflection/RuntimeMethodInfo.Mono.cs similarity index 90% rename from src/mono/System.Private.CoreLib/src/System/Reflection/RuntimeMethodInfo.cs rename to src/mono/System.Private.CoreLib/src/System/Reflection/RuntimeMethodInfo.Mono.cs index a6568737354d4d..1c6e10f1db2339 100644 --- a/src/mono/System.Private.CoreLib/src/System/Reflection/RuntimeMethodInfo.cs +++ b/src/mono/System.Private.CoreLib/src/System/Reflection/RuntimeMethodInfo.Mono.cs @@ -32,6 +32,7 @@ using System.Runtime.InteropServices; using System.Reflection.Emit; using System.Text; +using System.Threading; using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using InteropServicesCallingConvention = System.Runtime.InteropServices.CallingConvention; @@ -139,7 +140,7 @@ internal static ParameterInfo GetReturnParameterInfo(RuntimeMethodInfo method) #region Sync with _MonoReflectionMethod in object-internals.h [StructLayout(LayoutKind.Sequential)] - internal sealed class RuntimeMethodInfo : MethodInfo + internal sealed partial class RuntimeMethodInfo : MethodInfo { #pragma warning disable 649 internal IntPtr mhandle; @@ -148,6 +149,22 @@ internal sealed class RuntimeMethodInfo : MethodInfo #pragma warning restore 649 #endregion private string? toString; + private RuntimeType[]? parameterTypes; + private InvocationFlags invocationFlags; + + internal InvocationFlags InvocationFlags + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get + { + InvocationFlags flags = invocationFlags; + if ((flags & InvocationFlags.Initialized) == 0) + { + flags = ComputeAndUpdateInvocationFlags(this, ref invocationFlags); + } + return flags; + } + } public override Module Module { @@ -326,6 +343,27 @@ internal override int GetParametersCount() return MonoMethodInfo.GetParametersInfo(mhandle, this).Length; } + private RuntimeType[] ArgumentTypes + { + get + { + if (parameterTypes != null) + { + return parameterTypes; + } + + ParameterInfo[] src = GetParametersInternal(); + RuntimeType[] dest = new RuntimeType[src.Length]; + for (int i = 0; i < dest.Length; ++i) + { + dest[i] = (RuntimeType)src[i].ParameterType; + } + + Interlocked.CompareExchange(ref parameterTypes, dest, null); + return dest; + } + } + /* * InternalInvoke() receives the parameters correctly converted by the * binder to match the types of the method signature. @@ -333,33 +371,11 @@ internal override int GetParametersCount() * Exceptions thrown by the called method propagate normally. */ [MethodImplAttribute(MethodImplOptions.InternalCall)] - internal extern object? InternalInvoke(object? obj, object?[]? parameters, out Exception? exc); + internal extern object? InternalInvoke(object? obj, in Span parameters, out Exception? exc); - [DebuggerHidden] - [DebuggerStepThrough] - public override object? Invoke(object? obj, BindingFlags invokeAttr, Binder? binder, object?[]? parameters, CultureInfo? culture) + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private object? InvokeWorker(object? obj, BindingFlags invokeAttr, in Span parameters) { - if (!IsStatic) - { - if (!DeclaringType.IsInstanceOfType(obj)) - { - if (obj == null) - throw new TargetException("Non-static method requires a target."); - else - throw new TargetException("Object does not match target type."); - } - } - - if (binder == null) - binder = Type.DefaultBinder; - - /*Avoid allocating an array every time*/ - ParameterInfo[] pinfo = GetParametersInternal(); - ConvertValues(binder, parameters, pinfo, culture, invokeAttr); - - if (ContainsGenericParameters) - throw new InvalidOperationException("Late bound operations cannot be performed on types or methods for which ContainsGenericParameters is true."); - Exception? exc; object? o = null; @@ -758,7 +774,7 @@ public override IList GetCustomAttributesData() } #region Sync with _MonoReflectionMethod in object-internals.h [StructLayout(LayoutKind.Sequential)] - internal sealed class RuntimeConstructorInfo : ConstructorInfo + internal sealed partial class RuntimeConstructorInfo : ConstructorInfo { #pragma warning disable 649 internal IntPtr mhandle; @@ -767,6 +783,22 @@ internal sealed class RuntimeConstructorInfo : ConstructorInfo #pragma warning restore 649 #endregion private string? toString; + private RuntimeType[]? parameterTypes; + private InvocationFlags invocationFlags; + + internal InvocationFlags InvocationFlags + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get + { + InvocationFlags flags = invocationFlags; + if ((flags & InvocationFlags.Initialized) == 0) + { + flags = ComputeAndUpdateInvocationFlags(this, ref invocationFlags); + } + return flags; + } + } public override Module Module { @@ -802,51 +834,55 @@ internal override int GetParametersCount() return pi == null ? 0 : pi.Length; } - /* - * InternalInvoke() receives the parameters correctly converted by the binder - * to match the types of the method signature. - */ - [MethodImplAttribute(MethodImplOptions.InternalCall)] - internal extern object InternalInvoke(object? obj, object?[]? parameters, out Exception exc); - - [DebuggerHidden] - [DebuggerStepThrough] - public override object? Invoke(object? obj, BindingFlags invokeAttr, Binder? binder, object?[]? parameters, CultureInfo? culture) + private RuntimeType[] ArgumentTypes { - if (obj == null) - { - if (!IsStatic) - throw new TargetException("Instance constructor requires a target"); - } - else if (!DeclaringType.IsInstanceOfType(obj)) + get { - throw new TargetException("Constructor does not match target type"); - } + if (parameterTypes != null) + { + return parameterTypes; + } - return DoInvoke(obj, invokeAttr, binder, parameters, culture); + ParameterInfo[] src = GetParametersInternal(); + RuntimeType[] dest = new RuntimeType[src.Length]; + for (int i = 0; i < dest.Length; ++i) + { + dest[i] = (RuntimeType)src[i].ParameterType; + } + + Interlocked.CompareExchange(ref parameterTypes, dest, null); + return dest; + } } - private object DoInvoke(object? obj, BindingFlags invokeAttr, Binder? binder, object?[]? parameters, CultureInfo? culture) + private void InvokeClassConstructor() { - if (binder == null) - binder = Type.DefaultBinder; - - ParameterInfo[] pinfo = MonoMethodInfo.GetParametersInfo(mhandle, this); - - RuntimeMethodInfo.ConvertValues(binder, parameters, pinfo, culture, invokeAttr); - - if (obj == null && DeclaringType.ContainsGenericParameters) - throw new MemberAccessException("Cannot create an instance of " + DeclaringType + " because Type.ContainsGenericParameters is true."); + // [TODO] Mechanism for invoking class constructor + // See https://github.com/dotnet/runtime/issues/40351 + } - if ((invokeAttr & BindingFlags.CreateInstance) != 0 && DeclaringType.IsAbstract) - { - throw new MemberAccessException(string.Format("Cannot create an instance of {0} because it is an abstract class", DeclaringType)); - } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal object? InvokeWorker(object? obj, BindingFlags invokeAttr, in Span arguments) + { + bool wrapExceptions = (invokeAttr & BindingFlags.DoNotWrapExceptions) == 0; + return InternalInvoke(obj, arguments, wrapExceptions); + } - return InternalInvoke(obj, parameters, (invokeAttr & BindingFlags.DoNotWrapExceptions) == 0)!; + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal object InvokeCtorWorker(BindingFlags invokeAttr, Span arguments) + { + bool wrapExceptions = (invokeAttr & BindingFlags.DoNotWrapExceptions) == 0; + return InternalInvoke(null, arguments, wrapExceptions)!; } - public object? InternalInvoke(object? obj, object?[]? parameters, bool wrapExceptions) + /* + * InternalInvoke() receives the parameters correctly converted by the binder + * to match the types of the method signature. + */ + [MethodImplAttribute(MethodImplOptions.InternalCall)] + internal extern object InternalInvoke(object? obj, in Span parameters, out Exception exc); + + private object? InternalInvoke(object? obj, Span parameters, bool wrapExceptions) { Exception exc; object? o = null; @@ -881,13 +917,6 @@ private object DoInvoke(object? obj, BindingFlags invokeAttr, Binder? binder, ob return obj == null ? o : null; } - [DebuggerHidden] - [DebuggerStepThrough] - public override object Invoke(BindingFlags invokeAttr, Binder? binder, object?[]? parameters, CultureInfo? culture) - { - return DoInvoke(null, invokeAttr, binder, parameters, culture); - } - public override RuntimeMethodHandle MethodHandle { get diff --git a/src/mono/System.Private.CoreLib/src/System/RuntimeType.Mono.cs b/src/mono/System.Private.CoreLib/src/System/RuntimeType.Mono.cs index 151ad2fb63b24a..dff7e639cb2bce 100644 --- a/src/mono/System.Private.CoreLib/src/System/RuntimeType.Mono.cs +++ b/src/mono/System.Private.CoreLib/src/System/RuntimeType.Mono.cs @@ -1653,10 +1653,10 @@ internal override FieldInfo GetField(FieldInfo fromNoninstanciated) throw new MissingMethodException("Cannot create an abstract class '{0}'.", FullName); } - return ctor.InternalInvoke(null, null, wrapExceptions); + return ctor.InvokeWorker(null, wrapExceptions ? BindingFlags.Default : BindingFlags.DoNotWrapExceptions, Span.Empty); } - internal object? CheckValue(object? value, Binder binder, CultureInfo? culture, BindingFlags invokeAttr) + internal object? CheckValue(object? value, Binder? binder, CultureInfo? culture, BindingFlags invokeAttr) { bool failed = false; object? res = TryConvertToType(value, ref failed); @@ -1968,7 +1968,7 @@ internal static object CreateInstanceForAnotherGenericParameter( if (ctor is null || !ctor.IsPublic) throw new MissingMethodException(SR.Format(SR.Arg_NoDefCTor, gt)); - return ctor.InternalInvoke(null, null, wrapExceptions: true)!; + return ctor.InvokeCtorWorker(BindingFlags.Default, Span.Empty)!; } [MethodImplAttribute(MethodImplOptions.InternalCall)] diff --git a/src/mono/mono/metadata/icall-def-netcore.h b/src/mono/mono/metadata/icall-def-netcore.h index a4340404b82a07..983cc5330f0cba 100644 --- a/src/mono/mono/metadata/icall-def-netcore.h +++ b/src/mono/mono/metadata/icall-def-netcore.h @@ -254,7 +254,7 @@ HANDLES(RASSEM_12, "get_location", ves_icall_System_Reflection_RuntimeAssembly_g ICALL_TYPE(MCMETH, "System.Reflection.RuntimeConstructorInfo", MCMETH_1) HANDLES(MCMETH_1, "GetGenericMethodDefinition_impl", ves_icall_RuntimeMethodInfo_GetGenericMethodDefinition, MonoReflectionMethod, 1, (MonoReflectionMethod)) -HANDLES(MCMETH_2, "InternalInvoke", ves_icall_InternalInvoke, MonoObject, 4, (MonoReflectionMethod, MonoObject, MonoArray, MonoExceptionOut)) +HANDLES(MCMETH_2, "InternalInvoke", ves_icall_InternalInvoke, MonoObject, 4, (MonoReflectionMethod, MonoObject, MonoSpanOfObjects_ref, MonoExceptionOut)) HANDLES_REUSE_WRAPPER(MCMETH_4, "get_metadata_token", ves_icall_reflection_get_token) ICALL_TYPE(CATTR_DATA, "System.Reflection.RuntimeCustomAttributeData", CATTR_DATA_1) diff --git a/src/mono/mono/metadata/icall-table.h b/src/mono/mono/metadata/icall-table.h index 31aa3aba238a04..f5d303d392304e 100644 --- a/src/mono/mono/metadata/icall-table.h +++ b/src/mono/mono/metadata/icall-table.h @@ -69,6 +69,7 @@ typedef MonoVTable *MonoVTable_ptr; typedef unsigned *unsigned_ptr; typedef mono_unichar2 *mono_unichar2_ptr; typedef mono_unichar4 *mono_unichar4_ptr; +typedef MonoSpanOfObjects *MonoSpanOfObjects_ref; typedef char **char_ptr_ref; typedef gint32 *gint32_ref; @@ -173,6 +174,7 @@ typedef MonoStringHandle MonoStringOutHandle; #define MONO_HANDLE_TYPE_WRAP_char_ptr_ref ICALL_HANDLES_WRAP_VALUETYPE_REF #define MONO_HANDLE_TYPE_WRAP_guint8_ptr_ref ICALL_HANDLES_WRAP_VALUETYPE_REF #define MONO_HANDLE_TYPE_WRAP_MonoResolveTokenError_ref ICALL_HANDLES_WRAP_VALUETYPE_REF +#define MONO_HANDLE_TYPE_WRAP_MonoSpanOfObjects_ref ICALL_HANDLES_WRAP_VALUETYPE_REF // HANDLE is not used just to avoid duplicate typedef warnings with some compilers. // gpointer == void* == HANDLE == FILE_HANDLE == PROCESS_HANDLE. diff --git a/src/mono/mono/metadata/icall.c b/src/mono/mono/metadata/icall.c index 8a01903ab30a80..0af369217b686f 100644 --- a/src/mono/mono/metadata/icall.c +++ b/src/mono/mono/metadata/icall.c @@ -3354,11 +3354,11 @@ ves_icall_RuntimeMethodInfo_GetGenericArguments (MonoReflectionMethodHandle ref_ MonoObjectHandle ves_icall_InternalInvoke (MonoReflectionMethodHandle method_handle, MonoObjectHandle this_arg_handle, - MonoArrayHandle params_handle, MonoExceptionHandleOut exception_out, MonoError *error) + MonoSpanOfObjects *params_span, MonoExceptionHandleOut exception_out, MonoError *error) { MonoReflectionMethod* const method = MONO_HANDLE_RAW (method_handle); MonoObject* const this_arg = MONO_HANDLE_RAW (this_arg_handle); - MonoArray* const params = MONO_HANDLE_RAW (params_handle); + g_assert (params_span != NULL); /* * Invoke from reflection is supposed to always be a virtual call (the API @@ -3388,17 +3388,6 @@ ves_icall_InternalInvoke (MonoReflectionMethodHandle method_handle, MonoObjectHa } if (this_arg) { - if (!mono_object_isinst_checked (this_arg, m->klass, error)) { - if (!is_ok (error)) { - exception = mono_error_convert_to_exception (error); - goto return_null; - } - this_name = mono_type_get_full_name (mono_object_class (this_arg)); - target_name = mono_type_get_full_name (m->klass); - msg = g_strdup_printf ("Object of type '%s' doesn't match target type '%s'", this_name, target_name); - exception = mono_exception_from_name_msg (mono_defaults.corlib, "System.Reflection", "TargetException", msg); - goto return_null; - } m = mono_object_get_virtual_method_internal (this_arg, m); /* must pass the pointer to the value for valuetype methods */ if (m_class_is_valuetype (m->klass)) { @@ -3411,43 +3400,15 @@ ves_icall_InternalInvoke (MonoReflectionMethodHandle method_handle, MonoObjectHa } } - if ((m->klass != NULL && m_class_is_byreflike (m->klass)) || m_class_is_byreflike (mono_class_from_mono_type_internal (sig->ret))) { - exception = mono_exception_from_name_msg (mono_defaults.corlib, "System", "NotSupportedException", "Cannot invoke method with stack pointers via reflection"); - goto return_null; - } - - if (m_type_is_byref (sig->ret)) { - MonoType* ret_byval = m_class_get_byval_arg (mono_class_from_mono_type_internal (sig->ret)); - if (ret_byval->type == MONO_TYPE_VOID) { - exception = mono_exception_from_name_msg (mono_defaults.corlib, "System", "NotSupportedException", "ByRef to void return values are not supported in reflection invocation"); - goto return_null; - } - if (m_class_is_byreflike (mono_class_from_mono_type_internal (ret_byval))) { - exception = mono_exception_from_name_msg (mono_defaults.corlib, "System", "NotSupportedException", "Cannot invoke method returning ByRef to ByRefLike type via reflection"); - goto return_null; - } - } - - pcount = params? mono_array_length_internal (params): 0; - if (pcount != sig->param_count) { - exception = mono_exception_from_name (mono_defaults.corlib, "System.Reflection", "TargetParameterCountException"); - goto return_null; - } - - if (mono_class_is_abstract (m->klass) && !strcmp (m->name, ".ctor") && !this_arg) { - exception = mono_exception_from_name_msg (mono_defaults.corlib, "System.Reflection", "TargetException", "Cannot invoke constructor of an abstract class."); - goto return_null; - } - image = m_class_get_image (m->klass); if (m_class_get_rank (m->klass) && !strcmp (m->name, ".ctor")) { int i; - pcount = mono_array_length_internal (params); + pcount = mono_span_length (params_span); uintptr_t * const lengths = g_newa (uintptr_t, pcount); /* Note: the synthetized array .ctors have int32 as argument type */ for (i = 0; i < pcount; ++i) - lengths [i] = *(int32_t*) ((char*)mono_array_get_internal (params, gpointer, i) + sizeof (MonoObject)); + lengths [i] = *(int32_t*) ((char*)mono_span_get (params_span, MonoObject*, i) + sizeof (MonoObject)); if (m_class_get_rank (m->klass) == 1 && sig->param_count == 2 && m_class_get_rank (m_class_get_element_class (m->klass))) { /* This is a ctor for jagged arrays. MS creates an array of arrays. */ @@ -3476,8 +3437,8 @@ ves_icall_InternalInvoke (MonoReflectionMethodHandle method_handle, MonoObjectHa intptr_t * const lower_bounds = (intptr_t *)g_alloca (sizeof (intptr_t) * pcount); for (i = 0; i < pcount / 2; ++i) { - lower_bounds [i] = *(int32_t*) ((char*)mono_array_get_internal (params, gpointer, (i * 2)) + sizeof (MonoObject)); - lengths [i] = *(int32_t*) ((char*)mono_array_get_internal (params, gpointer, (i * 2) + 1) + sizeof (MonoObject)); + lower_bounds [i] = *(int32_t*) ((char*)mono_span_get (params_span, MonoObject*, (i * 2)) + sizeof (MonoObject)); + lengths [i] = *(int32_t*) ((char*)mono_span_get (params_span, MonoObject*, (i * 2) + 1) + sizeof (MonoObject)); } arr = mono_array_new_full_checked (m->klass, lengths, lower_bounds, error); @@ -3485,7 +3446,7 @@ ves_icall_InternalInvoke (MonoReflectionMethodHandle method_handle, MonoObjectHa goto exit; } } - result = mono_runtime_invoke_array_checked (m, obj, params, error); + result = mono_runtime_invoke_span_checked (m, obj, params_span, error); goto exit; return_null: result = NULL; diff --git a/src/mono/mono/metadata/object-internals.h b/src/mono/mono/metadata/object-internals.h index b50ed3c47d8bdc..9d37d996b14088 100644 --- a/src/mono/mono/metadata/object-internals.h +++ b/src/mono/mono/metadata/object-internals.h @@ -175,6 +175,12 @@ struct _MonoArray { mono_64bitaligned_t vector [MONO_ZERO_LEN_ARRAY]; }; +/* match the layout of the managed definition of Span */ +typedef struct { + MonoObject** _pointer; + int32_t _length; +} MonoSpanOfObjects; + #define MONO_SIZEOF_MONO_ARRAY (MONO_STRUCT_OFFSET_CONSTANT (MonoArray, vector)) struct _MonoString { @@ -276,6 +282,32 @@ mono_handle_array_get_bounds_dim (MonoArrayHandle arr, gint32 dim, MonoArrayBoun *bounds = MONO_HANDLE_GETVAL (arr, bounds [dim]); } +#define mono_span_length(span) (span->_length) + +#define mono_span_get(span,type,idx) (type)(!span->_pointer ? NULL : span->_pointer[idx]) + +#define mono_span_addr(span,type,idx) (type*)(span->_pointer + idx) + +#define mono_span_setref(span,index,value) \ + do { \ + void **__p = (void **) mono_span_addr ((span), void*, (index)); \ + mono_gc_wbarrier_generic_store_internal (__p, (MonoObject*)(value)); \ + /* *__p = (value);*/ \ + } while (0) + +static inline MonoSpanOfObjects +mono_span_create_from_object_array (MonoArray *arr) { + MonoSpanOfObjects span; + if (arr != NULL) { + span._length = (int32_t)mono_array_length_internal (arr); + span._pointer = mono_array_addr_fast (arr, MonoObject*, 0); + } else { + span._length = 0; + span._pointer = NULL; + } + return span; +} + typedef struct { MonoObject obj; } MonoMarshalByRefObject; @@ -1838,7 +1870,7 @@ mono_runtime_try_invoke_array (MonoMethod *method, void *obj, MonoArray *params, MonoObject **exc, MonoError *error); MonoObject* -mono_runtime_invoke_array_checked (MonoMethod *method, void *obj, MonoArray *params, +mono_runtime_invoke_span_checked (MonoMethod *method, void *obj, MonoSpanOfObjects *params, MonoError *error); void* @@ -2059,7 +2091,7 @@ void mono_gc_wbarrier_set_field_internal (MonoObject *obj, void* field_ptr, MonoObject* value); void -mono_gc_wbarrier_set_arrayref_internal (MonoArray *arr, void* slot_ptr, MonoObject* value); +mono_gc_wbarrier_set_arrayref_internal (MonoArray *arr, void* slot_ptr, MonoObject* value); void mono_gc_wbarrier_arrayref_copy_internal (void* dest_ptr, const void* src_ptr, int count); diff --git a/src/mono/mono/metadata/object.c b/src/mono/mono/metadata/object.c index af53f4d8694dfc..11b61ad44c6008 100644 --- a/src/mono/mono/metadata/object.c +++ b/src/mono/mono/metadata/object.c @@ -4516,8 +4516,8 @@ mono_runtime_try_exec_main (MonoMethod *method, MonoArray *args, MonoObject **ex return do_try_exec_main (method, args, exc); } -/** invoke_array_extract_argument: - * @params: array of arguments to the method. +/** invoke_span_extract_argument: + * @params: span of object arguments to the method. * @i: the index of the argument to extract. * @t: ith type from the method signature. * @has_byref_nullables: outarg - TRUE if method expects a byref nullable argument @@ -4529,7 +4529,7 @@ mono_runtime_try_exec_main (MonoMethod *method, MonoArray *args, MonoObject **ex * On failure sets @error and returns NULL. */ static gpointer -invoke_array_extract_argument (MonoArray *params, int i, MonoType *t, MonoObject **pa_obj, gboolean* has_byref_nullables, MonoError *error) +invoke_span_extract_argument (MonoSpanOfObjects *params_span, int i, MonoType *t, MonoObject **pa_obj, gboolean* has_byref_nullables, MonoError *error) { MonoType *t_orig = t; gpointer result = NULL; @@ -4554,17 +4554,17 @@ invoke_array_extract_argument (MonoArray *params, int i, MonoType *t, MonoObject case MONO_TYPE_VALUETYPE: if (t->type == MONO_TYPE_VALUETYPE && mono_class_is_nullable (mono_class_from_mono_type_internal (t_orig))) { /* The runtime invoke wrapper needs the original boxed vtype, it does handle byref values as well. */ - *pa_obj = mono_array_get_internal (params, MonoObject*, i); + *pa_obj = mono_span_get (params_span, MonoObject*, i); result = *pa_obj; if (m_type_is_byref (t)) *has_byref_nullables = TRUE; } else { /* MS seems to create the objects if a null is passed in */ gboolean was_null = FALSE; - if (!mono_array_get_internal (params, MonoObject*, i)) { + if (!mono_span_get (params_span, MonoObject*, i)) { MonoObject *o = mono_object_new_checked (mono_class_from_mono_type_internal (t_orig), error); return_val_if_nok (error, NULL); - mono_array_setref_internal (params, i, o); + mono_span_setref (params_span, i, o); was_null = TRUE; } @@ -4576,15 +4576,15 @@ invoke_array_extract_argument (MonoArray *params, int i, MonoType *t, MonoObject * object, pass that to the callee, and replace the original * boxed object in the arg array with the copy. */ - MonoObject *orig = mono_array_get_internal (params, MonoObject*, i); + MonoObject *orig = mono_span_get (params_span, MonoObject*, i); MonoObject *copy = mono_value_box_checked (orig->vtable->klass, mono_object_unbox_internal (orig), error); return_val_if_nok (error, NULL); - mono_array_setref_internal (params, i, copy); + mono_span_setref (params_span, i, copy); } - *pa_obj = mono_array_get_internal (params, MonoObject*, i); + *pa_obj = mono_span_get (params_span, MonoObject*, i); result = mono_object_unbox_internal (*pa_obj); if (!m_type_is_byref (t) && was_null) - mono_array_setref_internal (params, i, NULL); + mono_span_setref (params_span, i, NULL); } break; case MONO_TYPE_STRING: @@ -4593,10 +4593,10 @@ invoke_array_extract_argument (MonoArray *params, int i, MonoType *t, MonoObject case MONO_TYPE_ARRAY: case MONO_TYPE_SZARRAY: if (m_type_is_byref (t)) { - result = mono_array_addr_internal (params, MonoObject*, i); + result = mono_span_addr (params_span, MonoObject*, i); // FIXME: I need to check this code path } else { - *pa_obj = mono_array_get_internal (params, MonoObject*, i); + *pa_obj = mono_span_get (params_span, MonoObject*, i); result = *pa_obj; } break; @@ -4610,7 +4610,7 @@ invoke_array_extract_argument (MonoArray *params, int i, MonoType *t, MonoObject MonoObject *arg; /* The argument should be an IntPtr */ - arg = mono_array_get_internal (params, MonoObject*, i); + arg = mono_span_get (params_span, MonoObject*, i); if (arg == NULL) { result = NULL; } else { @@ -4683,93 +4683,10 @@ mono_runtime_invoke_array (MonoMethod *method, void *obj, MonoArray *params, return res; } -/** - * mono_runtime_invoke_array_checked: - * \param method method to invoke - * \param obj object instance - * \param params arguments to the method - * \param error set on failure. - * Invokes the method represented by \p method on the object \p obj. - * - * \p obj is the \c this pointer, it should be NULL for static - * methods, a \c MonoObject* for object instances and a pointer to - * the value type for value types. - * - * The \p params array contains the arguments to the method with the - * same convention: \c MonoObject* pointers for object instances and - * pointers to the value type otherwise. The \c _invoke_array - * variant takes a C# \c object[] as the \p params argument (\c MonoArray*): - * in this case the value types are boxed inside the - * respective reference representation. - * - * From unmanaged code you'll usually use the - * mono_runtime_invoke_checked() variant. - * - * Note that this function doesn't handle virtual methods for - * you, it will exec the exact method you pass: we still need to - * expose a function to lookup the derived class implementation - * of a virtual method (there are examples of this in the code, - * though). - * - * On failure or exception, \p error will be set. In that case, you - * can't use the \c MonoObject* result from the function. - * - * If the method returns a value type, it is boxed in an object - * reference. - */ -MonoObject* -mono_runtime_invoke_array_checked (MonoMethod *method, void *obj, MonoArray *params, - MonoError *error) -{ - error_init (error); - return mono_runtime_try_invoke_array (method, obj, params, NULL, error); -} - -/** - * mono_runtime_try_invoke_array: - * \param method method to invoke - * \param obj object instance - * \param params arguments to the method - * \param exc exception information. - * \param error set on failure. - * Invokes the method represented by \p method on the object \p obj. - * - * \p obj is the \c this pointer, it should be NULL for static - * methods, a \c MonoObject* for object instances and a pointer to - * the value type for value types. - * - * The \p params array contains the arguments to the method with the - * same convention: \c MonoObject* pointers for object instances and - * pointers to the value type otherwise. The \c _invoke_array - * variant takes a C# \c object[] as the params argument (\c MonoArray*): - * in this case the value types are boxed inside the - * respective reference representation. - * - * From unmanaged code you'll usually use the - * mono_runtime_invoke_checked() variant. - * - * Note that this function doesn't handle virtual methods for - * you, it will exec the exact method you pass: we still need to - * expose a function to lookup the derived class implementation - * of a virtual method (there are examples of this in the code, - * though). - * - * You can pass NULL as the \p exc argument if you don't want to catch - * exceptions, otherwise, \c *exc will be set to the exception thrown, if - * any. On other failures, \p error will be set. If an exception is - * thrown or there's an error, you can't use the \c MonoObject* result - * from the function. - * - * If the method returns a value type, it is boxed in an object - * reference. - */ -MonoObject* -mono_runtime_try_invoke_array (MonoMethod *method, void *obj, MonoArray *params, +static MonoObject* +mono_runtime_try_invoke_span (MonoMethod *method, void *obj, MonoSpanOfObjects *params_span, MonoObject **exc, MonoError *error) { - MONO_REQ_GC_UNSAFE_MODE; - HANDLE_FUNCTION_ENTER (); - error_init (error); MonoMethodSignature *sig = mono_method_signature_internal (method); @@ -4777,13 +4694,14 @@ mono_runtime_try_invoke_array (MonoMethod *method, void *obj, MonoArray *params, MonoObject *res = NULL; int i; gboolean has_byref_nullables = FALSE; + int params_length = mono_span_length (params_span); - if (NULL != params) { - pa = g_newa (gpointer, mono_array_length_internal (params)); - for (i = 0; i < mono_array_length_internal (params); i++) { + if (params_length > 0) { + pa = g_newa (gpointer, params_length); + for (i = 0; i < params_length; i++) { MonoType *t = sig->params [i]; MonoObject *pa_obj; - pa [i] = invoke_array_extract_argument (params, i, t, &pa_obj, &has_byref_nullables, error); + pa [i] = invoke_span_extract_argument (params_span, i, t, &pa_obj, &has_byref_nullables, error); if (pa_obj) MONO_HANDLE_PIN (pa_obj); goto_if_nok (error, exit_null); @@ -4797,7 +4715,7 @@ mono_runtime_try_invoke_array (MonoMethod *method, void *obj, MonoArray *params, /* Need to create a boxed vtype instead */ g_assert (!obj); - if (!params) { + if (params_length == 0) { goto_if_nok (error, exit_null); } else { res = mono_value_box_checked (m_class_get_cast_class (method->klass), pa [0], error); @@ -4892,25 +4810,104 @@ mono_runtime_try_invoke_array (MonoMethod *method, void *obj, MonoArray *params, g_assert (box_exc == NULL); mono_error_assert_ok (error); } - - if (has_byref_nullables) { - /* - * The runtime invoke wrapper already converted byref nullables back, - * and stored them in pa, we just need to copy them back to the - * managed array. - */ - for (i = 0; i < mono_array_length_internal (params); i++) { - MonoType *t = sig->params [i]; - - if (m_type_is_byref (t) && t->type == MONO_TYPE_GENERICINST && mono_class_is_nullable (mono_class_from_mono_type_internal (t))) - mono_array_setref_internal (params, i, pa [i]); - } - } } goto exit; exit_null: res = NULL; exit: + return res; +} + +/** + * mono_runtime_invoke_array_checked: + * \param method method to invoke + * \param obj object instance + * \param params arguments to the method + * \param error set on failure. + * Invokes the method represented by \p method on the object \p obj. + * + * \p obj is the \c this pointer, it should be NULL for static + * methods, a \c MonoObject* for object instances and a pointer to + * the value type for value types. + * + * The \p params span contains the arguments to the method with the + * same convention: \c MonoObject* pointers for object instances and + * pointers to the value type otherwise. The \c _invoke_array + * variant takes a C# \c object[] as the \p params argument (\c MonoArray*): + * in this case the value types are boxed inside the + * respective reference representation. + * + * From unmanaged code you'll usually use the + * mono_runtime_invoke_checked() variant. + * + * Note that this function doesn't handle virtual methods for + * you, it will exec the exact method you pass: we still need to + * expose a function to lookup the derived class implementation + * of a virtual method (there are examples of this in the code, + * though). + * + * On failure or exception, \p error will be set. In that case, you + * can't use the \c MonoObject* result from the function. + * + * If the method returns a value type, it is boxed in an object + * reference. + */ +MonoObject* +mono_runtime_invoke_span_checked (MonoMethod *method, void *obj, MonoSpanOfObjects *params, + MonoError *error) +{ + error_init (error); + return mono_runtime_try_invoke_span (method, obj, params, NULL, error); +} + +/** + * mono_runtime_try_invoke_array: + * \param method method to invoke + * \param obj object instance + * \param params arguments to the method + * \param exc exception information. + * \param error set on failure. + * Invokes the method represented by \p method on the object \p obj. + * + * \p obj is the \c this pointer, it should be NULL for static + * methods, a \c MonoObject* for object instances and a pointer to + * the value type for value types. + * + * The \p params array contains the arguments to the method with the + * same convention: \c MonoObject* pointers for object instances and + * pointers to the value type otherwise. The \c _invoke_array + * variant takes a C# \c object[] as the params argument (\c MonoArray*): + * in this case the value types are boxed inside the + * respective reference representation. + * + * From unmanaged code you'll usually use the + * mono_runtime_invoke_checked() variant. + * + * Note that this function doesn't handle virtual methods for + * you, it will exec the exact method you pass: we still need to + * expose a function to lookup the derived class implementation + * of a virtual method (there are examples of this in the code, + * though). + * + * You can pass NULL as the \p exc argument if you don't want to catch + * exceptions, otherwise, \c *exc will be set to the exception thrown, if + * any. On other failures, \p error will be set. If an exception is + * thrown or there's an error, you can't use the \c MonoObject* result + * from the function. + * + * If the method returns a value type, it is boxed in an object + * reference. + */ +MonoObject* +mono_runtime_try_invoke_array (MonoMethod *method, void *obj, MonoArray *params, + MonoObject **exc, MonoError *error) +{ + MONO_REQ_GC_UNSAFE_MODE; + HANDLE_FUNCTION_ENTER (); + + MonoSpanOfObjects params_span = mono_span_create_from_object_array (params); + MonoObject *res = mono_runtime_try_invoke_span (method, obj, ¶ms_span, exc, error); + HANDLE_FUNCTION_RETURN_VAL (res); }