From d0bde833a5822299a86335fb97ffc476c19e80b3 Mon Sep 17 00:00:00 2001 From: Levi Broderick Date: Sat, 9 May 2020 15:36:47 -0700 Subject: [PATCH 01/64] Fast object instantiation, take two - Eliminates ObjectFactory except when caller explicitly wants a Func - Uses C# 9.0 function pointers to avoid introducing new JIT intrinsics --- eng/Versions.props | 2 +- .../System.Private.CoreLib.csproj | 5 +- .../Reflection/ObjectFactory.CoreCLR.cs | 126 +++++++++++++ .../UninitializedObjectFactory.CoreCLR.cs | 48 +++++ .../RuntimeHelpers.CoreCLR.cs | 69 ++++++- .../src/System/RuntimeHandles.cs | 6 + .../src/System/RuntimeType.CoreCLR.cs | 87 +++------ src/coreclr/src/vm/ecalllist.h | 4 + src/coreclr/src/vm/mscorlib.h | 2 + src/coreclr/src/vm/reflectioninvocation.cpp | 131 ++++++++++++++ src/coreclr/src/vm/reflectioninvocation.h | 2 + src/coreclr/src/vm/runtimehandles.cpp | 37 ++++ src/coreclr/src/vm/runtimehandles.h | 2 + .../src/System/Activator.RuntimeType.cs | 72 ++++++++ .../System.Runtime/ref/System.Runtime.cs | 3 + .../tests/System.Runtime.Tests.csproj | 1 + .../System/Reflection/ObjectFactoryTests.cs | 169 ++++++++++++++++++ 17 files changed, 696 insertions(+), 70 deletions(-) create mode 100644 src/coreclr/src/System.Private.CoreLib/src/System/Reflection/ObjectFactory.CoreCLR.cs create mode 100644 src/coreclr/src/System.Private.CoreLib/src/System/Reflection/UninitializedObjectFactory.CoreCLR.cs create mode 100644 src/libraries/System.Runtime/tests/System/Reflection/ObjectFactoryTests.cs diff --git a/eng/Versions.props b/eng/Versions.props index 0b398500e1356..d82c5a5d75d67 100644 --- a/eng/Versions.props +++ b/eng/Versions.props @@ -19,7 +19,7 @@ true false - 3.7.0-2.20258.1 + 3.7.0-ci.final dotnet $(ContainerName) diff --git a/src/coreclr/src/System.Private.CoreLib/System.Private.CoreLib.csproj b/src/coreclr/src/System.Private.CoreLib/System.Private.CoreLib.csproj index b0456f3825d1e..973cff8e1e337 100644 --- a/src/coreclr/src/System.Private.CoreLib/System.Private.CoreLib.csproj +++ b/src/coreclr/src/System.Private.CoreLib/System.Private.CoreLib.csproj @@ -60,7 +60,8 @@ <_FullFrameworkReferenceAssemblyPaths>$(MSBuildThisFileDirectory)/Documentation true $(OutputPath)$(MSBuildProjectName).xml - true + + false @@ -200,6 +201,7 @@ + @@ -212,6 +214,7 @@ + diff --git a/src/coreclr/src/System.Private.CoreLib/src/System/Reflection/ObjectFactory.CoreCLR.cs b/src/coreclr/src/System.Private.CoreLib/src/System/Reflection/ObjectFactory.CoreCLR.cs new file mode 100644 index 0000000000000..199e189f2b7ec --- /dev/null +++ b/src/coreclr/src/System.Private.CoreLib/src/System/Reflection/ObjectFactory.CoreCLR.cs @@ -0,0 +1,126 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Diagnostics; +using System.Runtime.CompilerServices; +using Internal.Runtime.CompilerServices; + +namespace System.Reflection +{ + // Creates initialized instances of reference types or of value types. + // For reference types, calls the parameterless ctor. + // For value types, calls the parameterless ctor if it exists; otherwise + // return a boxed default(T). Must not be used with Nullable. + internal unsafe sealed class ObjectFactory : UninitializedObjectFactory + { + private readonly void* _pfnCtor; + private readonly bool _isNonPublicCtor; + + // Creates a factory from an existing parameterless ctor + internal ObjectFactory(RuntimeMethodHandleInternal hCtor) + : base(RuntimeMethodHandle.GetDeclaringType(hCtor)) + { + _pfnCtor = (void*)RuntimeMethodHandle.GetFunctionPointer(hCtor); + Debug.Assert(_pfnCtor != null); + + _isNonPublicCtor = (RuntimeMethodHandle.GetAttributes(hCtor) & MethodAttributes.MemberAccessMask) != MethodAttributes.Public; + } + + private ObjectFactory(RuntimeType type) + : base(type) + { + Debug.Assert(_pMT->IsValueType); + _isNonPublicCtor = false; // default(T) is always "public" + } + + // Creates a factory for "box(default(T))" around a value type + internal static ObjectFactory CreateFactoryForValueTypeDefaultOfT(RuntimeType type) + { + return new ObjectFactory(type); + } + + public bool IsNonPublicCtor => _isNonPublicCtor; + + public object CreateInstance() + { + object newObj = CreateUninitializedInstance(); + + if (!_pMT->IsValueType) + { + // Common case: we're creating a reference type + ((delegate*)_pfnCtor)(newObj); + } + else + { + // Less common case: we're creating a boxed value type + // If an explicit parameterless ctor exists, call it now. + if (_pfnCtor != null) + { + ((delegate*)_pfnCtor)(ref newObj.GetRawData()); + } + } + + return newObj; + } + } + + // Similar to ObjectFactory, but does not box value types 'T'. + internal unsafe sealed class ObjectFactory : UninitializedObjectFactory + { + private readonly void* _pfnCtor; + + internal ObjectFactory() + : base((RuntimeType)typeof(T)) + { + RuntimeType type = (RuntimeType)typeof(T); + + // It's ok if there's no default constructor on a value type. + // We'll return default(T). For reference types, the constructor + // must be present. In all cases, if a constructor is present, it + // must be public. + + RuntimeMethodHandleInternal hCtor = RuntimeMethodHandleInternal.EmptyHandle; + if (_pMT->HasDefaultConstructor) + { + hCtor = RuntimeTypeHandle.GetDefaultConstructor(type); + Debug.Assert(!hCtor.IsNullHandle()); + if ((RuntimeMethodHandle.GetAttributes(hCtor) & MethodAttributes.MemberAccessMask) != MethodAttributes.Public) + { + // parameterless ctor exists but is not public + throw new MissingMethodException(SR.Format(SR.Arg_NoDefCTor, type)); + } + + _pfnCtor = (void*)RuntimeMethodHandle.GetFunctionPointer(hCtor); + } + else + { + if (!_pMT->IsValueType) + { + // parameterless ctor missing on reference type + throw new MissingMethodException(SR.Format(SR.Arg_NoDefCTor, type)); + } + } + } + + public T CreateInstance() + { + if (typeof(T).IsValueType) + { + T value = default!; + if (_pfnCtor != null) + { + ((delegate*)_pfnCtor)(ref value); + } + return value; + } + else + { + object value = CreateUninitializedInstance(); + Debug.Assert(_pfnCtor != null); + ((delegate*)_pfnCtor)(value!); + return Unsafe.As(ref value); + } + } + } +} diff --git a/src/coreclr/src/System.Private.CoreLib/src/System/Reflection/UninitializedObjectFactory.CoreCLR.cs b/src/coreclr/src/System.Private.CoreLib/src/System/Reflection/UninitializedObjectFactory.CoreCLR.cs new file mode 100644 index 0000000000000..a6dc8e196f3c4 --- /dev/null +++ b/src/coreclr/src/System.Private.CoreLib/src/System/Reflection/UninitializedObjectFactory.CoreCLR.cs @@ -0,0 +1,48 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Diagnostics; +using System.Runtime.CompilerServices; + +namespace System.Reflection +{ + // Creates zero-initialized instances of types. + // For reference types, equivalent of allocating memory without running ctor. + // For value types, equivalent of boxing default(T). + // Must not be used with Nullable. + internal unsafe class UninitializedObjectFactory + { + protected readonly MethodTable* _pMT; + private readonly delegate* _pfnAllocate; + private readonly RuntimeType _type; + + internal UninitializedObjectFactory(RuntimeType type) + { + Debug.Assert(type != null); + Debug.Assert(RuntimeHelpers.IsFastInstantiable(type)); + + _type = type; + _pMT = RuntimeTypeHandle.GetMethodTable(type); + _pfnAllocate = RuntimeHelpers.GetNewobjHelper(type); + + Debug.Assert(_pMT != null); + Debug.Assert(!_pMT->IsNullable); + Debug.Assert(_pfnAllocate != null); + } + + public object CreateUninitializedInstance() + { + // If a GC kicks in between the time we load the newobj + // helper address and the time we calli it, we don't want + // the Type object to be eligible for collection. To avoid + // this, we KeepAlive(this) - and the referenced Type - + // until we have an instance of the object. From that point + // onward, the object itself will keep the Type alive. + + object newObj = _pfnAllocate(_pMT); + GC.KeepAlive(this); + return newObj; + } + } +} diff --git a/src/coreclr/src/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.CoreCLR.cs b/src/coreclr/src/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.CoreCLR.cs index 49e8a4e08c8c0..70a8679a6abc2 100644 --- a/src/coreclr/src/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.CoreCLR.cs +++ b/src/coreclr/src/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.CoreCLR.cs @@ -4,6 +4,7 @@ using System.Diagnostics; using System.Diagnostics.CodeAnalysis; +using System.Reflection; using System.Runtime.InteropServices; using Internal.Runtime.CompilerServices; @@ -145,6 +146,41 @@ public static int OffsetToStringData [MethodImpl(MethodImplOptions.InternalCall)] internal static extern object AllocateUninitializedClone(object obj); + [MethodImpl(MethodImplOptions.InternalCall)] + internal static extern unsafe delegate* GetNewobjHelper(RuntimeType type); + + [MethodImpl(MethodImplOptions.InternalCall)] + internal static extern bool IsFastInstantiable(RuntimeType type); + + public static unsafe Func GetUninitializedObjectFactory(Type type) + { + if (type is null) + { + throw new ArgumentNullException(nameof(type)); + } + + if (!(type.UnderlyingSystemType is RuntimeType rt)) + { + throw new ArgumentException(SR.Arg_MustBeType, nameof(type)); + } + + type = null!; // just to make sure we don't use Type for the rest of the method + + if (rt.IsPointer || rt.IsByRef || rt.IsByRefLike || !RuntimeHelpers.IsFastInstantiable(rt)) + { + throw new ArgumentException( + paramName: nameof(type), + message: SR.NotSupported_Type); + } + + // Compat with GetUninitializedObject: if incoming type T is really + // Nullable, then this factory should return a boxed default(U) + // instead of a boxed default(Nullable). + + rt = (RuntimeType?)Nullable.GetUnderlyingType(rt) ?? rt; + return (Func)new UninitializedObjectFactory(rt).CreateUninitializedInstance; + } + /// true if given type is reference type or value type that contains references [Intrinsic] public static bool IsReferenceOrContainsReferences() @@ -332,10 +368,15 @@ internal unsafe struct MethodTable [FieldOffset(InterfaceMapOffset)] public MethodTable** InterfaceMap; - // WFLAGS_HIGH_ENUM + // WFLAGS_HIGH_ENUM & WFLAGS_LOW_ENUM + private const uint enum_flag_Category_Mask = 0x000F0000; + private const uint enum_flag_Category_Nullable = 0x00050000; + private const uint enum_flag_Category_ValueType = 0x00040000; + private const uint enum_flag_Category_ValueType_Mask = 0x000C0000; private const uint enum_flag_ContainsPointers = 0x01000000; private const uint enum_flag_HasComponentSize = 0x80000000; - private const uint enum_flag_HasTypeEquivalence = 0x00004000; + private const uint enum_flag_HasDefaultCtor = 0x00000200; + private const uint enum_flag_HasTypeEquivalence = 0x00004000; // TODO: shouldn't this be 0x02000000? // Types that require non-trivial interface cast have this bit set in the category private const uint enum_flag_NonTrivialInterfaceCast = 0x00080000 // enum_flag_Category_Array | 0x40000000 // enum_flag_ComObject @@ -391,6 +432,30 @@ public bool NonTrivialInterfaceCast } } + public bool HasDefaultConstructor + { + get + { + return (Flags & enum_flag_HasDefaultCtor) != 0; + } + } + + public bool IsNullable + { + get + { + return (Flags & enum_flag_Category_Mask) == enum_flag_Category_Nullable; + } + } + + public bool IsValueType + { + get + { + return (Flags & enum_flag_Category_ValueType_Mask) == enum_flag_Category_ValueType; + } + } + public bool HasTypeEquivalence { get diff --git a/src/coreclr/src/System.Private.CoreLib/src/System/RuntimeHandles.cs b/src/coreclr/src/System.Private.CoreLib/src/System/RuntimeHandles.cs index 5661ce8130ec5..35961de15a61a 100644 --- a/src/coreclr/src/System.Private.CoreLib/src/System/RuntimeHandles.cs +++ b/src/coreclr/src/System.Private.CoreLib/src/System/RuntimeHandles.cs @@ -249,6 +249,12 @@ public ModuleHandle GetModuleHandle() [MethodImpl(MethodImplOptions.InternalCall)] internal static extern RuntimeMethodHandleInternal GetMethodAt(RuntimeType type, int slot); + [MethodImpl(MethodImplOptions.InternalCall)] + internal static unsafe extern MethodTable* GetMethodTable(RuntimeType type); + + [MethodImpl(MethodImplOptions.InternalCall)] + internal static extern RuntimeMethodHandleInternal GetDefaultConstructor(RuntimeType type); + // This is managed wrapper for MethodTable::IntroducedMethodIterator internal struct IntroducedMethodEnumerator { diff --git a/src/coreclr/src/System.Private.CoreLib/src/System/RuntimeType.CoreCLR.cs b/src/coreclr/src/System.Private.CoreLib/src/System/RuntimeType.CoreCLR.cs index 93cfd86bc167b..a42625860ee5b 100644 --- a/src/coreclr/src/System.Private.CoreLib/src/System/RuntimeType.CoreCLR.cs +++ b/src/coreclr/src/System.Private.CoreLib/src/System/RuntimeType.CoreCLR.cs @@ -3890,46 +3890,6 @@ private void CreateInstanceCheckThis() return instance; } - // the cache entry - private sealed class ActivatorCache - { - // the delegate containing the call to the ctor - internal readonly RuntimeMethodHandleInternal _hCtorMethodHandle; - internal MethodAttributes _ctorAttributes; - internal CtorDelegate? _ctor; - - // Lazy initialization was performed - internal volatile bool _isFullyInitialized; - - private static ConstructorInfo? s_delegateCtorInfo; - - internal ActivatorCache(RuntimeMethodHandleInternal rmh) - { - _hCtorMethodHandle = rmh; - } - - private void Initialize() - { - if (!_hCtorMethodHandle.IsNullHandle()) - { - _ctorAttributes = RuntimeMethodHandle.GetAttributes(_hCtorMethodHandle); - - // The default ctor path is optimized for reference types only - ConstructorInfo delegateCtorInfo = s_delegateCtorInfo ??= typeof(CtorDelegate).GetConstructor(new Type[] { typeof(object), typeof(IntPtr) })!; - - // No synchronization needed here. In the worst case we create extra garbage - _ctor = (CtorDelegate)delegateCtorInfo.Invoke(new object?[] { null, RuntimeMethodHandle.GetFunctionPointer(_hCtorMethodHandle) }); - } - _isFullyInitialized = true; - } - - public void EnsureInitialized() - { - if (!_isFullyInitialized) - Initialize(); - } - } - /// /// The slow path of CreateInstanceDefaultCtor /// @@ -3947,8 +3907,17 @@ public void EnsureInitialized() if (canBeCached && fillCache) { - // cache the ctor - GenericCache = new ActivatorCache(runtimeCtor); + if (runtimeCtor.IsNullHandle()) + { + // value types with no parameterless ctor + Debug.Assert(this.IsValueType); + GenericCache = ObjectFactory.CreateFactoryForValueTypeDefaultOfT(this); + } + else + { + // reference types or value types with a parameterless ctor + GenericCache = new ObjectFactory(runtimeCtor); + } } return instance; @@ -3961,36 +3930,22 @@ public void EnsureInitialized() [DebuggerHidden] internal object? CreateInstanceDefaultCtor(bool publicOnly, bool skipCheckThis, bool fillCache, bool wrapExceptions) { - // Call the cached - if (GenericCache is ActivatorCache cacheEntry) + // Call the cached factory if it exists + if (GenericCache is ObjectFactory cachedFactory) { - cacheEntry.EnsureInitialized(); - - if (publicOnly) + if (cachedFactory.IsNonPublicCtor && publicOnly) { - if (cacheEntry._ctor != null && - (cacheEntry._ctorAttributes & MethodAttributes.MemberAccessMask) != MethodAttributes.Public) - { - throw new MissingMethodException(SR.Format(SR.Arg_NoDefCTor, this)); - } + throw new MissingMethodException(SR.Format(SR.Arg_NoDefCTor, this)); } - // Allocate empty object and call the default constructor if present. - object instance = RuntimeTypeHandle.Allocate(this); - Debug.Assert(cacheEntry._ctor != null || IsValueType); - if (cacheEntry._ctor != null) + try { - try - { - cacheEntry._ctor(instance); - } - catch (Exception e) when (wrapExceptions) - { - throw new TargetInvocationException(e); - } + return cachedFactory.CreateInstance(); + } + catch (Exception e) when (wrapExceptions) + { + throw new TargetInvocationException(e); } - - return instance; } if (!skipCheckThis) diff --git a/src/coreclr/src/vm/ecalllist.h b/src/coreclr/src/vm/ecalllist.h index 155ae2c516e7c..7950eae062bec 100644 --- a/src/coreclr/src/vm/ecalllist.h +++ b/src/coreclr/src/vm/ecalllist.h @@ -235,6 +235,8 @@ FCFuncStart(gCOMTypeHandleFuncs) FCFuncElement("GetToken", RuntimeTypeHandle::GetToken) FCFuncElement("_GetUtf8Name", RuntimeTypeHandle::GetUtf8Name) FCFuncElement("GetMethodAt", RuntimeTypeHandle::GetMethodAt) + FCFuncElement("GetMethodTable", RuntimeTypeHandle::GetMethodTable) + FCFuncElement("GetDefaultConstructor", RuntimeTypeHandle::GetDefaultConstructor) FCFuncElement("GetFields", RuntimeTypeHandle::GetFields) FCFuncElement("GetInterfaces", RuntimeTypeHandle::GetInterfaces) QCFuncElement("GetConstraints", RuntimeTypeHandle::GetConstraints) @@ -905,6 +907,8 @@ FCFuncStart(gRuntimeHelpers) FCFuncElement("EnsureSufficientExecutionStack", ReflectionInvocation::EnsureSufficientExecutionStack) FCFuncElement("TryEnsureSufficientExecutionStack", ReflectionInvocation::TryEnsureSufficientExecutionStack) FCFuncElement("GetUninitializedObjectInternal", ReflectionSerialization::GetUninitializedObject) + FCFuncElement("GetNewobjHelper", ReflectionSerialization::GetNewobjHelper) + FCFuncElement("IsFastInstantiable", ReflectionSerialization::IsFastInstantiable) QCFuncElement("AllocateTypeAssociatedMemoryInternal", RuntimeTypeHandle::AllocateTypeAssociatedMemory) FCFuncElement("AllocTailCallArgBuffer", TailCallHelp::AllocTailCallArgBuffer) FCFuncElement("FreeTailCallArgBuffer", TailCallHelp::FreeTailCallArgBuffer) diff --git a/src/coreclr/src/vm/mscorlib.h b/src/coreclr/src/vm/mscorlib.h index 7c0258449f0a5..600bf5e5b624e 100644 --- a/src/coreclr/src/vm/mscorlib.h +++ b/src/coreclr/src/vm/mscorlib.h @@ -726,6 +726,8 @@ DEFINE_METHOD(RUNTIME_HELPERS, GET_METHOD_TABLE, GetMethodTable, DEFINE_METHOD(RUNTIME_HELPERS, GET_RAW_DATA, GetRawData, NoSig) DEFINE_METHOD(RUNTIME_HELPERS, GET_RAW_ARRAY_DATA, GetRawArrayData, NoSig) DEFINE_METHOD(RUNTIME_HELPERS, GET_UNINITIALIZED_OBJECT, GetUninitializedObject, NoSig) +DEFINE_METHOD(RUNTIME_HELPERS, GET_NEWOBJ_HELPER, GetNewobjHelper, NoSig) +DEFINE_METHOD(RUNTIME_HELPERS, IS_FAST_INSTANTIABLE, IsFastInstantiable, NoSig) DEFINE_METHOD(RUNTIME_HELPERS, ENUM_EQUALS, EnumEquals, NoSig) DEFINE_METHOD(RUNTIME_HELPERS, ENUM_COMPARE_TO, EnumCompareTo, NoSig) DEFINE_METHOD(RUNTIME_HELPERS, ALLOC_TAILCALL_ARG_BUFFER, AllocTailCallArgBuffer, SM_Int_IntPtr_RetIntPtr) diff --git a/src/coreclr/src/vm/reflectioninvocation.cpp b/src/coreclr/src/vm/reflectioninvocation.cpp index b604b1a52d913..3e2c4541eb44e 100644 --- a/src/coreclr/src/vm/reflectioninvocation.cpp +++ b/src/coreclr/src/vm/reflectioninvocation.cpp @@ -2383,6 +2383,137 @@ FCIMPL1(Object*, ReflectionSerialization::GetUninitializedObject, ReflectClassBa } FCIMPLEND +FCIMPL1(void*, ReflectionSerialization::GetNewobjHelper, ReflectClassBaseObject* objTypeUNSAFE) { + FCALL_CONTRACT; + + void* retVal = NULL; + REFLECTCLASSBASEREF objType = (REFLECTCLASSBASEREF)objTypeUNSAFE; + + HELPER_METHOD_FRAME_BEGIN_RET_NOPOLL(); + + TypeHandle type = objType->GetType(); + + // Don't allow arrays, pointers, byrefs or function pointers. + if (type.IsTypeDesc() || type.IsArray()) + COMPlusThrow(kArgumentException, W("Argument_InvalidValue")); + + MethodTable* pMT = type.AsMethodTable(); + PREFIX_ASSUME(pMT != NULL); + + //We don't allow unitialized Strings or Utf8Strings. + if (pMT->HasComponentSize()) + { + COMPlusThrow(kArgumentException, W("Argument_NoUninitializedStrings")); + } + + // if this is an abstract class or an interface type then we will + // fail this + if (pMT->IsAbstract()) { + COMPlusThrow(kMemberAccessException, W("Acc_CreateAbst")); + } + + if (pMT->ContainsGenericVariables()) { + COMPlusThrow(kMemberAccessException, W("Acc_CreateGeneric")); + } + + if (pMT->IsByRefLike()) { + COMPlusThrow(kNotSupportedException, W("NotSupported_ByRefLike")); + } + + // Never allow allocation of generics actually instantiated over __Canon + if (pMT->IsSharedByGenericInstantiations()) { + COMPlusThrow(kNotSupportedException, W("NotSupported_Type")); + } + + // Never allow the allocation of an unitialized ContextBoundObject derived type, these must always be created with a paired + // transparent proxy or the jit will get confused. + +#ifdef FEATURE_COMINTEROP + // Also do not allow allocation of uninitialized RCWs (COM objects). + if (pMT->IsComObjectType()) + COMPlusThrow(kNotSupportedException, W("NotSupported_ManagedActivation")); +#endif // FEATURE_COMINTEROP + + Assembly* pAssem = pMT->GetAssembly(); + + if (!pMT->IsClassInited()) + { + pMT->CheckRestore(); + pMT->EnsureInstanceActive(); + pMT->CheckRunClassInitThrowing(); + } + + // TODO: Don't use a void* cast below. + + retVal = (void*)CEEJitInfo::getHelperFtnStatic(CEEInfo::getNewHelperStatic(pMT)); + + HELPER_METHOD_FRAME_END(); + return retVal; +} +FCIMPLEND + +FCIMPL1(FC_BOOL_RET, ReflectionSerialization::IsFastInstantiable, ReflectClassBaseObject* objTypeUNSAFE) { + FCALL_CONTRACT; + + BOOL retVal = FALSE; + REFLECTCLASSBASEREF objType = (REFLECTCLASSBASEREF)objTypeUNSAFE; + + HELPER_METHOD_FRAME_BEGIN_RET_NOPOLL(); + + TypeHandle type = objType->GetType(); + + // Don't allow arrays, pointers, byrefs or function pointers. + if (type.IsTypeDesc() || type.IsArray()) + goto Return; + + MethodTable* pMT = type.AsMethodTable(); + PREFIX_ASSUME(pMT != NULL); + + //We don't allow unitialized Strings or Utf8Strings. + if (pMT->HasComponentSize()) + { + goto Return; + } + + // if this is an abstract class or an interface type then we will + // fail this + if (pMT->IsAbstract()) { + goto Return; + } + + if (pMT->ContainsGenericVariables()) { + goto Return; + } + + if (pMT->IsByRefLike()) { + goto Return; + } + + // Never allow allocation of generics actually instantiated over __Canon + if (pMT->IsSharedByGenericInstantiations()) { + goto Return; + } + + // Never allow the allocation of an unitialized ContextBoundObject derived type, these must always be created with a paired + // transparent proxy or the jit will get confused. + +#ifdef FEATURE_COMINTEROP + // Also do not allow allocation of uninitialized RCWs (COM objects). + if (pMT->IsComObjectType()) + goto Return; +#endif // FEATURE_COMINTEROP + + retVal = TRUE; // all checks succeeded + +Return: + + do { /* dummy required for macro below to compile */ } while (0); + + HELPER_METHOD_FRAME_END(); + FC_RETURN_BOOL(retVal); +} +FCIMPLEND + //************************************************************************************************* //************************************************************************************************* //************************************************************************************************* diff --git a/src/coreclr/src/vm/reflectioninvocation.h b/src/coreclr/src/vm/reflectioninvocation.h index f4160204e8a0d..4052a685858cd 100644 --- a/src/coreclr/src/vm/reflectioninvocation.h +++ b/src/coreclr/src/vm/reflectioninvocation.h @@ -78,6 +78,8 @@ class ReflectionInvocation { class ReflectionSerialization { public: static FCDECL1(Object*, GetUninitializedObject, ReflectClassBaseObject* objTypeUNSAFE); + static FCDECL1(void*, GetNewobjHelper, ReflectClassBaseObject* objTypeUNSAFE); + static FCDECL1(FC_BOOL_RET, IsFastInstantiable, ReflectClassBaseObject* objTypeUNSAFE); }; class ReflectionEnum { diff --git a/src/coreclr/src/vm/runtimehandles.cpp b/src/coreclr/src/vm/runtimehandles.cpp index 82c793bf3f7af..317c8baaa0035 100644 --- a/src/coreclr/src/vm/runtimehandles.cpp +++ b/src/coreclr/src/vm/runtimehandles.cpp @@ -679,7 +679,44 @@ FCIMPL2(MethodDesc *, RuntimeTypeHandle::GetMethodAt, ReflectClassBaseObject *pT return pRetMethod; } +FCIMPLEND +FCIMPL1(MethodTable *, RuntimeTypeHandle::GetMethodTable, ReflectClassBaseObject *pTypeUNSAFE) { + CONTRACTL { + FCALL_CHECK; + } + CONTRACTL_END; + + REFLECTCLASSBASEREF refType = (REFLECTCLASSBASEREF)ObjectToOBJECTREF(pTypeUNSAFE); + + if (refType == NULL) + FCThrowRes(kArgumentNullException, W("Arg_InvalidHandle")); + + TypeHandle typeHandle = refType->GetType(); + return typeHandle.GetMethodTable(); +} +FCIMPLEND + +FCIMPL1(MethodDesc *, RuntimeTypeHandle::GetDefaultConstructor, ReflectClassBaseObject *pTypeUNSAFE) { + CONTRACTL { + FCALL_CHECK; + } + CONTRACTL_END; + + REFLECTCLASSBASEREF refType = (REFLECTCLASSBASEREF)ObjectToOBJECTREF(pTypeUNSAFE); + + if (refType == NULL) + FCThrowRes(kArgumentNullException, W("Arg_InvalidHandle")); + + TypeHandle typeHandle = refType->GetType(); + MethodDesc* pRetMethod = NULL; + + HELPER_METHOD_FRAME_BEGIN_RET_1(refType); + pRetMethod = typeHandle.GetMethodTable()->GetDefaultConstructor(); + HELPER_METHOD_FRAME_END(); + + return pRetMethod; +} FCIMPLEND FCIMPL3(FC_BOOL_RET, RuntimeTypeHandle::GetFields, ReflectClassBaseObject *pTypeUNSAFE, INT32 **result, INT32 *pCount) { diff --git a/src/coreclr/src/vm/runtimehandles.h b/src/coreclr/src/vm/runtimehandles.h index 7787e01675305..59493107a1e7a 100644 --- a/src/coreclr/src/vm/runtimehandles.h +++ b/src/coreclr/src/vm/runtimehandles.h @@ -243,6 +243,8 @@ class RuntimeTypeHandle { static FCDECL1(ReflectClassBaseObject*, GetElementType, ReflectClassBaseObject* pType); static FCDECL2(MethodDesc*, GetMethodAt, PTR_ReflectClassBaseObject pType, INT32 slot); + static FCDECL1(MethodTable*, GetMethodTable, PTR_ReflectClassBaseObject pType); + static FCDECL1(MethodDesc*, GetDefaultConstructor, PTR_ReflectClassBaseObject pType); static FCDECL1(INT32, GetNumVirtuals, ReflectClassBaseObject *pType); static diff --git a/src/libraries/System.Private.CoreLib/src/System/Activator.RuntimeType.cs b/src/libraries/System.Private.CoreLib/src/System/Activator.RuntimeType.cs index 76957ef99db0e..8f87f35104504 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Activator.RuntimeType.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Activator.RuntimeType.cs @@ -4,9 +4,11 @@ using System.Reflection; using System.Globalization; +using System.Runtime.CompilerServices; using System.Runtime.Loader; using System.Runtime.Remoting; using System.Threading; +using System.Diagnostics; namespace System { @@ -145,6 +147,76 @@ public static T CreateInstance() return (T)((RuntimeType)typeof(T)).CreateInstanceDefaultCtor(publicOnly: true, skipCheckThis: true, fillCache: true, wrapExceptions: true)!; } + public unsafe static Func CreateFactory() + { + // ObjectFactory ctor will perform correctness checks + ObjectFactory factory = new ObjectFactory(); + return factory.CreateInstance; + } + + public static unsafe Func CreateFactory(Type type, bool nonPublic) + { + if (type is null) + { + throw new ArgumentNullException(nameof(type)); + } + + if (!(type.UnderlyingSystemType is RuntimeType rt)) + { + throw new ArgumentException(SR.Arg_MustBeType, nameof(type)); + } + + type = null!; // just to make sure we don't use 'type' for the rest of the method + + if (rt.IsPointer || rt.IsByRef || rt.IsByRefLike || !RuntimeHelpers.IsFastInstantiable(rt)) + { + throw new ArgumentException( + paramName: nameof(type), + message: SR.NotSupported_Type); + } + + MethodTable* pMT = RuntimeTypeHandle.GetMethodTable(rt); + Debug.Assert(pMT != null); + + if (!pMT->HasDefaultConstructor) + { + // If no parameterless ctor exists, we can still fabricate an instance for value + // types, returning a boxed default(T). Unless the incoming type is a + // Nullable, at which point we'll return null instead of a "boxed null". + + if (pMT->IsValueType) + { + if (pMT->IsNullable) + { + return () => null; + } + else + { + ObjectFactory factory = ObjectFactory.CreateFactoryForValueTypeDefaultOfT(rt); + return factory.CreateInstance; + } + } + } + else + { + // If a parameterless ctor exists, perform visibility checks before linking to it. + + RuntimeMethodHandleInternal hCtor = RuntimeTypeHandle.GetDefaultConstructor(rt); + Debug.Assert(!hCtor.IsNullHandle()); + + if (nonPublic || (RuntimeMethodHandle.GetAttributes(hCtor) & MethodAttributes.MemberAccessMask) == MethodAttributes.Public) + { + ObjectFactory factory = new ObjectFactory(hCtor); + return factory.CreateInstance; + } + } + + // If we reached this point, no parameterless ctor was found, or the ctor + // was found but we can't link to it due to member access restrictions. + + throw new MissingMethodException(SR.Format(SR.Arg_NoDefCTor, rt)); + } + private static T CreateDefaultInstance() where T : struct => default; } } diff --git a/src/libraries/System.Runtime/ref/System.Runtime.cs b/src/libraries/System.Runtime/ref/System.Runtime.cs index 0ff7018d9f5d1..fc5ee96edcbf3 100644 --- a/src/libraries/System.Runtime/ref/System.Runtime.cs +++ b/src/libraries/System.Runtime/ref/System.Runtime.cs @@ -67,6 +67,8 @@ public AccessViolationException(string? message, System.Exception? innerExceptio public delegate void Action(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7, T8 arg8, T9 arg9); public static partial class Activator { + public static System.Func CreateFactory(System.Type type, bool nonPublic) { throw null; } + public static System.Func CreateFactory() { throw null; } public static System.Runtime.Remoting.ObjectHandle? CreateInstance(string assemblyName, string typeName) { throw null; } public static System.Runtime.Remoting.ObjectHandle? CreateInstance(string assemblyName, string typeName, bool ignoreCase, System.Reflection.BindingFlags bindingAttr, System.Reflection.Binder? binder, object?[]? args, System.Globalization.CultureInfo? culture, object?[]? activationAttributes) { throw null; } public static System.Runtime.Remoting.ObjectHandle? CreateInstance(string assemblyName, string typeName, object?[]? activationAttributes) { throw null; } @@ -9103,6 +9105,7 @@ public static void ExecuteCodeWithGuaranteedCleanup(System.Runtime.CompilerServi public static object? GetObjectValue(object? obj) { throw null; } public static T[] GetSubArray(T[] array, System.Range range) { throw null; } public static object GetUninitializedObject(System.Type type) { throw null; } + public static System.Func GetUninitializedObjectFactory(System.Type type) { throw null; } public static void InitializeArray(System.Array array, System.RuntimeFieldHandle fldHandle) { } public static bool IsReferenceOrContainsReferences() { throw null; } public static void PrepareConstrainedRegions() { } diff --git a/src/libraries/System.Runtime/tests/System.Runtime.Tests.csproj b/src/libraries/System.Runtime/tests/System.Runtime.Tests.csproj index 95118ff34a34c..0611285dba432 100644 --- a/src/libraries/System.Runtime/tests/System.Runtime.Tests.csproj +++ b/src/libraries/System.Runtime/tests/System.Runtime.Tests.csproj @@ -199,6 +199,7 @@ + diff --git a/src/libraries/System.Runtime/tests/System/Reflection/ObjectFactoryTests.cs b/src/libraries/System.Runtime/tests/System/Reflection/ObjectFactoryTests.cs new file mode 100644 index 0000000000000..430da045bce71 --- /dev/null +++ b/src/libraries/System.Runtime/tests/System/Reflection/ObjectFactoryTests.cs @@ -0,0 +1,169 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Collections.Generic; +using System.Collections.Tests; +using System.Runtime.CompilerServices; +using System.Tests; +using System.Tests.Types; +using Xunit; +using Xunit.Sdk; + +namespace System.Reflection.Tests +{ + public unsafe class ObjectFactoryTests + { + // Types that should fail when used in the ObjectFactory + public static IEnumerable NegativeTypes() + { + foreach (Type type in EnumerateTestCases()) + { + yield return new object[] { type }; + } + + IEnumerable EnumerateTestCases() + { + yield return null; + yield return typeof(int*); // pointers are disallowed + yield return typeof(int).MakeByRef(); // byrefs are disallowed + yield return typeof(Span); // ref structs are disallowed + yield return typeof(Enum); // uninstantiable type + yield return typeof(ValueType); // uninstantiable type + yield return typeof(Array); // uninstantiable type + yield return typeof(IDisposable); // interfaces disallowed + yield return typeof(string); // variable length types disallowed + yield return typeof(int[]); // variable length types disallowed + yield return typeof(MyAbstractClass); // abstract types disallowed + yield return typeof(List<>); // open generic types disallowed + yield return CanonType; // canon type disallowed + yield return typeof(List<>).MakeGenericType(CanonType); // shared instantiation disallowed + yield return typeof(ClassWithoutParameterlessCtor); // parameterless ctor not found + yield return typeof(StaticClass); // static types disallowed + yield return typeof(delegate*); // function pointers disallowed + yield return typeof(List<>).GetGenericArguments(0); // type parameters disallowed + } + } + + [Theory] + [MemberData(nameof(NegativeTypes))] + public void Activator_CreateFactory_OfInvalidType_Throws(Type type) + { + Exception throwException = Assert.ThrowsAny(() => Activator.CreateFactory(type, nonPublic: true)); + + switch (throwException) + { + case ArgumentException: + case MissingMethodException: + case NotSupportedException: + break; + + default: + throw new XunitException($"Unexpected exception {throwException.GetType()} thrown."); + } + } + + [Theory] + [MemberData(nameof(NegativeTypes))] + public void RuntimeHelpers_CreateUninitializedObjectFactory_OfInvalidType_Throws(Type type) + { + Exception throwException = Assert.ThrowsAny(() => RuntimeHelpers.CreateUninitializedObjectFactory(type)); + + switch (throwException) + { + case ArgumentException: + case MissingMethodException: + case NotSupportedException: + break; + + default: + throw new XunitException($"Unexpected exception {throwException.GetType()} thrown."); + } + } + + [Fact] + public void Activator_CreateFactory_ReferenceTypeWithParameterlessCtor() + { + ClassWithPublicParameterlessCtor c1 = Activator.CreateFactory(typeof(ClassWithPublicParameterlessCtor), nonPublic: false)() as ClassWithPublicParameterlessCtor; + Assert.NotNull(c1); + Assert.True(c1.WasConstructorCalled); + + ClassWithPublicParameterlessCtor c2 = Activator.CreateFactory(typeof(ClassWithPublicParameterlessCtor), nonPublic: true)() as ClassWithPublicParameterlessCtor; + Assert.NotNull(c2); + Assert.True(c2.WasConstructorCalled); + + // if ctor not visible, shouldn't even get as far as creating the factory + Assert.Throws(() => Activator.CreateFactory(typeof(ClassWithoutParameterlessCtor), nonPublic: false)); + + ClassWithoutParameterlessCtor c4 = Activator.CreateFactory(typeof(ClassWithoutParameterlessCtor), nonPublic: true)() as ClassWithoutParameterlessCtor; + Assert.NotNull(c4); + Assert.True(c4.WasConstructorCalled); + } + + [Fact] + public void RuntimeHelpers_CreateUninitializedObjectFactory_ReferenceTypeWithParameterlessCtor() + { + ClassWithPublicParameterlessCtor c1 = RuntimeHelpers.CreateUninitializedObjectFactory(typeof(ClassWithPublicParameterlessCtor))() as ClassWithPublicParameterlessCtor; + Assert.NotNull(c1); + Assert.False(c1.WasConstructorCalled); + + ClassWithNonPublicParameterlessCtor c2 = RuntimeHelpers.CreateUninitializedObjectFactory(typeof(ClassWithNonPublicParameterlessCtor))() as ClassWithNonPublicParameterlessCtor; + Assert.NotNull(c2); + Assert.False(c2.WasConstructorCalled); + + ClassWithoutParameterlessCtor c3 = RuntimeHelpers.CreateUninitializedObjectFactory(typeof(ClassWithoutParameterlessCtor))() as ClassWithoutParameterlessCtor; + Assert.NotNull(c3); + Assert.False(c3.WasConstructorCalled); + } + + // TODO Unit tests: + // When T is value type with custom ctor + // When T is value type without custom ctor + // When T is Nullable: Activator should return null, RuntimeHelpers should return boxed default(U) + // Activator.CreateFactory helper + + public static Type CanonType + { + get + { + Type type = typeof(object).Assembly.GetType("System.__Canon"); + Assert.NotNull(type); + return type; + } + } + + public abstract class MyAbstractClass { } + + public class ClassWithoutParameterlessCtor + { + public bool WasConstructorCalled; + + public ClassWithoutParameterlessCtor(int i) + { + WasConstructorCalled = true; + } + } + + public static class StaticClass { } + + public class ClassWithPublicParameterlessCtor + { + public bool WasConstructorCalled; + + public ClassWithPublicParameterlessCtor() + { + WasConstructorCalled = true; + } + } + + public class ClassWithNonPublicParameterlessCtor + { + public bool WasConstructorCalled; + + internal ClassWithNonPublicParameterlessCtor() + { + WasConstructorCalled = true; + } + } + } +} From edf3858d61867b1806ea31e63e122556effe430e Mon Sep 17 00:00:00 2001 From: Levi Broderick Date: Mon, 6 Jul 2020 18:33:09 -0700 Subject: [PATCH 02/64] PR feedback - Remove new public APIs - Remove most new native APIs - Plumb GetUninitializedObject atop new native code paths - Use actual builds from arcade --- .../System.Private.CoreLib.csproj | 2 - .../Reflection/ObjectFactory.CoreCLR.cs | 126 ------- .../UninitializedObjectFactory.CoreCLR.cs | 48 --- .../RuntimeHelpers.CoreCLR.cs | 64 ++-- .../src/System/RuntimeHandles.cs | 32 +- .../src/System/RuntimeType.CoreCLR.cs | 103 ++++-- src/coreclr/src/vm/ecalllist.h | 6 +- src/coreclr/src/vm/mscorlib.h | 2 - src/coreclr/src/vm/reflectioninvocation.cpp | 307 ++++++------------ src/coreclr/src/vm/reflectioninvocation.h | 7 - src/coreclr/src/vm/runtimehandles.cpp | 38 --- src/coreclr/src/vm/runtimehandles.h | 5 +- .../src/System/Activator.RuntimeType.cs | 70 ---- .../System.Runtime/ref/System.Runtime.cs | 3 - .../tests/System.Runtime.Tests.csproj | 1 - .../System/Reflection/ObjectFactoryTests.cs | 169 ---------- .../CompilerServices/RuntimeHelpersTests.cs | 72 +++- 17 files changed, 298 insertions(+), 757 deletions(-) delete mode 100644 src/coreclr/src/System.Private.CoreLib/src/System/Reflection/ObjectFactory.CoreCLR.cs delete mode 100644 src/coreclr/src/System.Private.CoreLib/src/System/Reflection/UninitializedObjectFactory.CoreCLR.cs delete mode 100644 src/libraries/System.Runtime/tests/System/Reflection/ObjectFactoryTests.cs diff --git a/src/coreclr/src/System.Private.CoreLib/System.Private.CoreLib.csproj b/src/coreclr/src/System.Private.CoreLib/System.Private.CoreLib.csproj index 72ba9561f5b89..2de93d7df6f66 100644 --- a/src/coreclr/src/System.Private.CoreLib/System.Private.CoreLib.csproj +++ b/src/coreclr/src/System.Private.CoreLib/System.Private.CoreLib.csproj @@ -196,7 +196,6 @@ - @@ -209,7 +208,6 @@ - diff --git a/src/coreclr/src/System.Private.CoreLib/src/System/Reflection/ObjectFactory.CoreCLR.cs b/src/coreclr/src/System.Private.CoreLib/src/System/Reflection/ObjectFactory.CoreCLR.cs deleted file mode 100644 index 199e189f2b7ec..0000000000000 --- a/src/coreclr/src/System.Private.CoreLib/src/System/Reflection/ObjectFactory.CoreCLR.cs +++ /dev/null @@ -1,126 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System.Diagnostics; -using System.Runtime.CompilerServices; -using Internal.Runtime.CompilerServices; - -namespace System.Reflection -{ - // Creates initialized instances of reference types or of value types. - // For reference types, calls the parameterless ctor. - // For value types, calls the parameterless ctor if it exists; otherwise - // return a boxed default(T). Must not be used with Nullable. - internal unsafe sealed class ObjectFactory : UninitializedObjectFactory - { - private readonly void* _pfnCtor; - private readonly bool _isNonPublicCtor; - - // Creates a factory from an existing parameterless ctor - internal ObjectFactory(RuntimeMethodHandleInternal hCtor) - : base(RuntimeMethodHandle.GetDeclaringType(hCtor)) - { - _pfnCtor = (void*)RuntimeMethodHandle.GetFunctionPointer(hCtor); - Debug.Assert(_pfnCtor != null); - - _isNonPublicCtor = (RuntimeMethodHandle.GetAttributes(hCtor) & MethodAttributes.MemberAccessMask) != MethodAttributes.Public; - } - - private ObjectFactory(RuntimeType type) - : base(type) - { - Debug.Assert(_pMT->IsValueType); - _isNonPublicCtor = false; // default(T) is always "public" - } - - // Creates a factory for "box(default(T))" around a value type - internal static ObjectFactory CreateFactoryForValueTypeDefaultOfT(RuntimeType type) - { - return new ObjectFactory(type); - } - - public bool IsNonPublicCtor => _isNonPublicCtor; - - public object CreateInstance() - { - object newObj = CreateUninitializedInstance(); - - if (!_pMT->IsValueType) - { - // Common case: we're creating a reference type - ((delegate*)_pfnCtor)(newObj); - } - else - { - // Less common case: we're creating a boxed value type - // If an explicit parameterless ctor exists, call it now. - if (_pfnCtor != null) - { - ((delegate*)_pfnCtor)(ref newObj.GetRawData()); - } - } - - return newObj; - } - } - - // Similar to ObjectFactory, but does not box value types 'T'. - internal unsafe sealed class ObjectFactory : UninitializedObjectFactory - { - private readonly void* _pfnCtor; - - internal ObjectFactory() - : base((RuntimeType)typeof(T)) - { - RuntimeType type = (RuntimeType)typeof(T); - - // It's ok if there's no default constructor on a value type. - // We'll return default(T). For reference types, the constructor - // must be present. In all cases, if a constructor is present, it - // must be public. - - RuntimeMethodHandleInternal hCtor = RuntimeMethodHandleInternal.EmptyHandle; - if (_pMT->HasDefaultConstructor) - { - hCtor = RuntimeTypeHandle.GetDefaultConstructor(type); - Debug.Assert(!hCtor.IsNullHandle()); - if ((RuntimeMethodHandle.GetAttributes(hCtor) & MethodAttributes.MemberAccessMask) != MethodAttributes.Public) - { - // parameterless ctor exists but is not public - throw new MissingMethodException(SR.Format(SR.Arg_NoDefCTor, type)); - } - - _pfnCtor = (void*)RuntimeMethodHandle.GetFunctionPointer(hCtor); - } - else - { - if (!_pMT->IsValueType) - { - // parameterless ctor missing on reference type - throw new MissingMethodException(SR.Format(SR.Arg_NoDefCTor, type)); - } - } - } - - public T CreateInstance() - { - if (typeof(T).IsValueType) - { - T value = default!; - if (_pfnCtor != null) - { - ((delegate*)_pfnCtor)(ref value); - } - return value; - } - else - { - object value = CreateUninitializedInstance(); - Debug.Assert(_pfnCtor != null); - ((delegate*)_pfnCtor)(value!); - return Unsafe.As(ref value); - } - } - } -} diff --git a/src/coreclr/src/System.Private.CoreLib/src/System/Reflection/UninitializedObjectFactory.CoreCLR.cs b/src/coreclr/src/System.Private.CoreLib/src/System/Reflection/UninitializedObjectFactory.CoreCLR.cs deleted file mode 100644 index a6dc8e196f3c4..0000000000000 --- a/src/coreclr/src/System.Private.CoreLib/src/System/Reflection/UninitializedObjectFactory.CoreCLR.cs +++ /dev/null @@ -1,48 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System.Diagnostics; -using System.Runtime.CompilerServices; - -namespace System.Reflection -{ - // Creates zero-initialized instances of types. - // For reference types, equivalent of allocating memory without running ctor. - // For value types, equivalent of boxing default(T). - // Must not be used with Nullable. - internal unsafe class UninitializedObjectFactory - { - protected readonly MethodTable* _pMT; - private readonly delegate* _pfnAllocate; - private readonly RuntimeType _type; - - internal UninitializedObjectFactory(RuntimeType type) - { - Debug.Assert(type != null); - Debug.Assert(RuntimeHelpers.IsFastInstantiable(type)); - - _type = type; - _pMT = RuntimeTypeHandle.GetMethodTable(type); - _pfnAllocate = RuntimeHelpers.GetNewobjHelper(type); - - Debug.Assert(_pMT != null); - Debug.Assert(!_pMT->IsNullable); - Debug.Assert(_pfnAllocate != null); - } - - public object CreateUninitializedInstance() - { - // If a GC kicks in between the time we load the newobj - // helper address and the time we calli it, we don't want - // the Type object to be eligible for collection. To avoid - // this, we KeepAlive(this) - and the referenced Type - - // until we have an instance of the object. From that point - // onward, the object itself will keep the Type alive. - - object newObj = _pfnAllocate(_pMT); - GC.KeepAlive(this); - return newObj; - } - } -} diff --git a/src/coreclr/src/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.CoreCLR.cs b/src/coreclr/src/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.CoreCLR.cs index 6f376410770ac..e63845472de1b 100644 --- a/src/coreclr/src/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.CoreCLR.cs +++ b/src/coreclr/src/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.CoreCLR.cs @@ -137,54 +137,25 @@ public static int OffsetToStringData [MethodImpl(MethodImplOptions.InternalCall)] public static extern bool TryEnsureSufficientExecutionStack(); - [MethodImpl(MethodImplOptions.InternalCall)] - private static extern object GetUninitializedObjectInternal( - // This API doesn't call any constructors, but the type needs to be seen as constructed. - // A type is seen as constructed if a constructor is kept. - // This obviously won't cover a type with no constructor. Reference types with no - // constructor are an academic problem. Valuetypes with no constructors are a problem, - // but IL Linker currently treats them as always implicitly boxed. - [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.NonPublicConstructors)] - Type type); - - [MethodImpl(MethodImplOptions.InternalCall)] - internal static extern object AllocateUninitializedClone(object obj); - - [MethodImpl(MethodImplOptions.InternalCall)] - internal static extern unsafe delegate* GetNewobjHelper(RuntimeType type); - - [MethodImpl(MethodImplOptions.InternalCall)] - internal static extern bool IsFastInstantiable(RuntimeType type); - - public static unsafe Func GetUninitializedObjectFactory(Type type) + private static unsafe object GetUninitializedObjectInternal(Type type) { - if (type is null) - { - throw new ArgumentNullException(nameof(type)); - } + RuntimeType rt = (RuntimeType)type; + Debug.Assert(rt != null); - if (!(type.UnderlyingSystemType is RuntimeType rt)) - { - throw new ArgumentException(SR.Arg_MustBeType, nameof(type)); - } + // If somebody asks us to create a Nullable, create a T instead. + delegate* newobjHelper = RuntimeTypeHandle.GetNewobjHelperFnPtr(rt, out MethodTable* pMT, unwrapNullable: true, allowCom: false); + Debug.Assert(newobjHelper != null); + Debug.Assert(pMT != null); - type = null!; // just to make sure we don't use Type for the rest of the method + object retVal = newobjHelper(pMT); + GC.KeepAlive(rt); // don't allow the type to be collected before the object is instantiated - if (rt.IsPointer || rt.IsByRef || rt.IsByRefLike || !RuntimeHelpers.IsFastInstantiable(rt)) - { - throw new ArgumentException( - paramName: nameof(type), - message: SR.NotSupported_Type); - } - - // Compat with GetUninitializedObject: if incoming type T is really - // Nullable, then this factory should return a boxed default(U) - // instead of a boxed default(Nullable). - - rt = (RuntimeType?)Nullable.GetUnderlyingType(rt) ?? rt; - return (Func)new UninitializedObjectFactory(rt).CreateUninitializedInstance; + return retVal; } + [MethodImpl(MethodImplOptions.InternalCall)] + internal static extern object AllocateUninitializedClone(object obj); + /// true if given type is reference type or value type that contains references [Intrinsic] public static bool IsReferenceOrContainsReferences() @@ -384,6 +355,7 @@ internal unsafe struct MethodTable private const uint enum_flag_Category_ValueType = 0x00040000; private const uint enum_flag_Category_ValueType_Mask = 0x000C0000; private const uint enum_flag_ContainsPointers = 0x01000000; + private const uint enum_flag_ComObject = 0x40000000; private const uint enum_flag_HasComponentSize = 0x80000000; private const uint enum_flag_HasDefaultCtor = 0x00000200; private const uint enum_flag_HasTypeEquivalence = 0x00004000; // TODO: shouldn't this be 0x02000000? @@ -451,6 +423,14 @@ public bool HasDefaultConstructor } } + public bool IsComObject + { + get + { + return (Flags & enum_flag_ComObject) != 0; + } + } + public bool IsNullable { get diff --git a/src/coreclr/src/System.Private.CoreLib/src/System/RuntimeHandles.cs b/src/coreclr/src/System.Private.CoreLib/src/System/RuntimeHandles.cs index 53c6b5329042e..501e96f9a1437 100644 --- a/src/coreclr/src/System.Private.CoreLib/src/System/RuntimeHandles.cs +++ b/src/coreclr/src/System.Private.CoreLib/src/System/RuntimeHandles.cs @@ -209,6 +209,32 @@ internal static bool HasElementType(RuntimeType type) [MethodImpl(MethodImplOptions.InternalCall)] internal static extern object CreateInstanceForAnotherGenericParameter(RuntimeType type, RuntimeType genericParameter); + /// + /// Given a RuntimeType, returns both the address of the JIT's newobj helper for that type and the + /// MethodTable* corresponding to that type. If the type is closed over + /// some T and is true, then returns the values for the 'T'. + /// + internal static delegate* GetNewobjHelperFnPtr(RuntimeType type, out MethodTable* pMT, bool unwrapNullable, bool allowCom) + { + Debug.Assert(type != null); + + delegate* pNewobjHelperTemp = null; + MethodTable* pMTTemp = null; + + GetNewobjHelperFnPtr( + new QCallTypeHandle(ref type), + &pNewobjHelperTemp, + &pMTTemp, + (unwrapNullable) ? Interop.BOOL.TRUE : Interop.BOOL.FALSE, + (allowCom) ? Interop.BOOL.TRUE : Interop.BOOL.FALSE); + + pMT = pMTTemp; + return pNewobjHelperTemp; + } + + [DllImport(RuntimeHelpers.QCall, CharSet = CharSet.Unicode)] + private static extern void GetNewobjHelperFnPtr(QCallTypeHandle typeHandle, delegate** ppNewobjHelper, MethodTable** ppMT, Interop.BOOL fUnwrapNullable, Interop.BOOL fAllowCom); + internal RuntimeType GetRuntimeType() { return m_type; @@ -249,12 +275,6 @@ public ModuleHandle GetModuleHandle() [MethodImpl(MethodImplOptions.InternalCall)] internal static extern RuntimeMethodHandleInternal GetMethodAt(RuntimeType type, int slot); - [MethodImpl(MethodImplOptions.InternalCall)] - internal static unsafe extern MethodTable* GetMethodTable(RuntimeType type); - - [MethodImpl(MethodImplOptions.InternalCall)] - internal static extern RuntimeMethodHandleInternal GetDefaultConstructor(RuntimeType type); - // This is managed wrapper for MethodTable::IntroducedMethodIterator internal struct IntroducedMethodEnumerator { diff --git a/src/coreclr/src/System.Private.CoreLib/src/System/RuntimeType.CoreCLR.cs b/src/coreclr/src/System.Private.CoreLib/src/System/RuntimeType.CoreCLR.cs index 826263214c54d..4e9612313c3d6 100644 --- a/src/coreclr/src/System.Private.CoreLib/src/System/RuntimeType.CoreCLR.cs +++ b/src/coreclr/src/System.Private.CoreLib/src/System/RuntimeType.CoreCLR.cs @@ -18,12 +18,6 @@ namespace System { - // this is a work around to get the concept of a calli. It's not as fast but it would be interesting to - // see how it compares to the current implementation. - // This delegate will disappear at some point in favor of calli - - internal delegate void CtorDelegate(object instance); - // Keep this in sync with FormatFlags defined in typestring.h internal enum TypeNameFormatFlags { @@ -3872,6 +3866,74 @@ private void CreateInstanceCheckThis() return instance; } + // the cache entry + private sealed unsafe class ActivatorCache + { + internal volatile delegate* _pfnNewobj; + internal readonly delegate* _pfnCtor; + internal MethodTable* _pMT; + internal readonly bool _ctorIsPublic; + + internal ActivatorCache(RuntimeMethodHandleInternal rmh) + { + if (rmh.IsNullHandle()) + { + // Value type with no explicit constructor, which means the "default" ctor + // is implicitly public and no-ops. + + _ctorIsPublic = true; + _pfnCtor = &GC.KeepAlive; // no-op fn + } + else + { + // Reference type with a parameterless ctor. + + _ctorIsPublic = (RuntimeMethodHandle.GetAttributes(rmh) & MethodAttributes.Public) != 0; + _pfnCtor = (delegate*)RuntimeMethodHandle.GetFunctionPointer(rmh); + } + + Debug.Assert(_pfnCtor != null); + } + + private void Initialize(RuntimeType type) + { + Debug.Assert(type != null); + + // If we reached this point, we already know that the construction information + // can be cached, so all of the runtime reflection checks succeeded. We just + // need to special-case Nullable (to return null). + // + // No synchronization is needed in this method since we have marked the _pfnNewobj + // field as volatile, and if there's multi-threaded access all threads will agree + // on the values to write anyway. + // + // !! IMPORTANT !! + // Don't assign the function pointer return value of GetNewobjHelperFnPtr directly + // to the backing field, as setting the field marks initialization as complete. + // Be sure to perform any last-minute checks *before* setting the backing field. + + delegate* pfnNewobj = RuntimeTypeHandle.GetNewobjHelperFnPtr(type, out _pMT, unwrapNullable: false, allowCom: true); + if (_pMT->IsNullable) + { + pfnNewobj = &GetNull; // Activator.CreateInstance(typeof(Nullable)) => null + } + + Debug.Assert(pfnNewobj != null); + _pfnNewobj = pfnNewobj; // setting this field marks the instance as fully initialized + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void EnsureInitialized(RuntimeType type) + { + if (_pfnNewobj == null) // use this field's value as the indicator of whether initialization is finished + { + Initialize(type); + } + } + + private static object? GetNull(MethodTable* pMT) => null; + } + /// /// The slow path of CreateInstanceDefaultCtor /// @@ -3889,17 +3951,7 @@ private void CreateInstanceCheckThis() if (canBeCached && fillCache) { - if (runtimeCtor.IsNullHandle()) - { - // value types with no parameterless ctor - Debug.Assert(this.IsValueType); - GenericCache = ObjectFactory.CreateFactoryForValueTypeDefaultOfT(this); - } - else - { - // reference types or value types with a parameterless ctor - GenericCache = new ObjectFactory(runtimeCtor); - } + GenericCache = new ActivatorCache(runtimeCtor); } return instance; @@ -3910,19 +3962,28 @@ private void CreateInstanceCheckThis() /// [DebuggerStepThrough] [DebuggerHidden] - internal object? CreateInstanceDefaultCtor(bool publicOnly, bool skipCheckThis, bool fillCache, bool wrapExceptions) + internal unsafe object? CreateInstanceDefaultCtor(bool publicOnly, bool skipCheckThis, bool fillCache, bool wrapExceptions) { // Call the cached factory if it exists - if (GenericCache is ObjectFactory cachedFactory) + if (GenericCache is ActivatorCache cache) { - if (cachedFactory.IsNonPublicCtor && publicOnly) + cache.EnsureInitialized(this); + + Debug.Assert(cache._pfnNewobj != null); + Debug.Assert(cache._pfnCtor != null); + + if (!cache._ctorIsPublic && publicOnly) { throw new MissingMethodException(SR.Format(SR.Arg_NoDefCTor, this)); } try { - return cachedFactory.CreateInstance(); + object? obj = cache._pfnNewobj(cache._pMT); + GC.KeepAlive(this); // can't allow the type to be collected before the object is created + + cache._pfnCtor(obj); + return obj; } catch (Exception e) when (wrapExceptions) { diff --git a/src/coreclr/src/vm/ecalllist.h b/src/coreclr/src/vm/ecalllist.h index d6308a3062b57..16904e0396275 100644 --- a/src/coreclr/src/vm/ecalllist.h +++ b/src/coreclr/src/vm/ecalllist.h @@ -231,8 +231,6 @@ FCFuncStart(gCOMTypeHandleFuncs) FCFuncElement("GetToken", RuntimeTypeHandle::GetToken) FCFuncElement("_GetUtf8Name", RuntimeTypeHandle::GetUtf8Name) FCFuncElement("GetMethodAt", RuntimeTypeHandle::GetMethodAt) - FCFuncElement("GetMethodTable", RuntimeTypeHandle::GetMethodTable) - FCFuncElement("GetDefaultConstructor", RuntimeTypeHandle::GetDefaultConstructor) FCFuncElement("GetFields", RuntimeTypeHandle::GetFields) FCFuncElement("GetInterfaces", RuntimeTypeHandle::GetInterfaces) QCFuncElement("GetConstraints", RuntimeTypeHandle::GetConstraints) @@ -260,6 +258,7 @@ FCFuncStart(gCOMTypeHandleFuncs) FCFuncElement("ContainsGenericVariables", RuntimeTypeHandle::ContainsGenericVariables) FCFuncElement("SatisfiesConstraints", RuntimeTypeHandle::SatisfiesConstraints) FCFuncElement("Allocate", RuntimeTypeHandle::Allocate) //for A.CI + QCFuncElement("GetNewobjHelperFnPtr", RuntimeTypeHandle::GetNewobjHelperFnPtr) FCFuncElement("CompareCanonicalHandles", RuntimeTypeHandle::CompareCanonicalHandles) FCIntrinsic("GetValueInternal", RuntimeTypeHandle::GetValueInternal, CORINFO_INTRINSIC_RTH_GetValueInternal) FCFuncElement("IsEquivalentTo", RuntimeTypeHandle::IsEquivalentTo) @@ -884,9 +883,6 @@ FCFuncStart(gRuntimeHelpers) FCFuncElement("AllocateUninitializedClone", ObjectNative::AllocateUninitializedClone) FCFuncElement("EnsureSufficientExecutionStack", ReflectionInvocation::EnsureSufficientExecutionStack) FCFuncElement("TryEnsureSufficientExecutionStack", ReflectionInvocation::TryEnsureSufficientExecutionStack) - FCFuncElement("GetUninitializedObjectInternal", ReflectionSerialization::GetUninitializedObject) - FCFuncElement("GetNewobjHelper", ReflectionSerialization::GetNewobjHelper) - FCFuncElement("IsFastInstantiable", ReflectionSerialization::IsFastInstantiable) QCFuncElement("AllocateTypeAssociatedMemoryInternal", RuntimeTypeHandle::AllocateTypeAssociatedMemory) FCFuncElement("AllocTailCallArgBuffer", TailCallHelp::AllocTailCallArgBuffer) FCFuncElement("FreeTailCallArgBuffer", TailCallHelp::FreeTailCallArgBuffer) diff --git a/src/coreclr/src/vm/mscorlib.h b/src/coreclr/src/vm/mscorlib.h index e8f42a55054a4..1f664b73398a6 100644 --- a/src/coreclr/src/vm/mscorlib.h +++ b/src/coreclr/src/vm/mscorlib.h @@ -705,8 +705,6 @@ DEFINE_METHOD(RUNTIME_HELPERS, GET_METHOD_TABLE, GetMethodTable, DEFINE_METHOD(RUNTIME_HELPERS, GET_RAW_DATA, GetRawData, NoSig) DEFINE_METHOD(RUNTIME_HELPERS, GET_RAW_ARRAY_DATA, GetRawArrayData, NoSig) DEFINE_METHOD(RUNTIME_HELPERS, GET_UNINITIALIZED_OBJECT, GetUninitializedObject, NoSig) -DEFINE_METHOD(RUNTIME_HELPERS, GET_NEWOBJ_HELPER, GetNewobjHelper, NoSig) -DEFINE_METHOD(RUNTIME_HELPERS, IS_FAST_INSTANTIABLE, IsFastInstantiable, NoSig) DEFINE_METHOD(RUNTIME_HELPERS, ENUM_EQUALS, EnumEquals, NoSig) DEFINE_METHOD(RUNTIME_HELPERS, ENUM_COMPARE_TO, EnumCompareTo, NoSig) DEFINE_METHOD(RUNTIME_HELPERS, ALLOC_TAILCALL_ARG_BUFFER, AllocTailCallArgBuffer, SM_Int_IntPtr_RetIntPtr) diff --git a/src/coreclr/src/vm/reflectioninvocation.cpp b/src/coreclr/src/vm/reflectioninvocation.cpp index 368e56096fc1f..be484f8157eac 100644 --- a/src/coreclr/src/vm/reflectioninvocation.cpp +++ b/src/coreclr/src/vm/reflectioninvocation.cpp @@ -561,6 +561,110 @@ FCIMPL2(Object*, RuntimeTypeHandle::CreateInstanceForGenericType, ReflectClassBa } FCIMPLEND + +/* + * Given a TypeHandle, returns the address of the NEWOBJ helper function that creates + * a zero-inited instance of this type. If NEWOBJ is not supported on this TypeHandle, + * throws an exception. If TypeHandle is a value type, the NEWOBJ helper will create + * a boxed zero-inited instance of the value type. + */ + void QCALLTYPE RuntimeTypeHandle::GetNewobjHelperFnPtr( + QCall::TypeHandle pTypeHandle, + PCODE* ppNewobjHelper, + MethodTable** ppMT, + BOOL fUnwrapNullable, + BOOL fAllowCom) +{ + CONTRACTL{ + QCALL_CHECK; + PRECONDITION(CheckPointer(ppNewobjHelper)); + PRECONDITION(CheckPointer(ppMT)); + PRECONDITION(*ppNewobjHelper == NULL); + PRECONDITION(*ppMT == NULL); + } + CONTRACTL_END; + + BEGIN_QCALL; + + TypeHandle typeHandle = pTypeHandle.AsTypeHandle(); + + // Don't allow arrays, pointers, byrefs, or function pointers. + if (typeHandle.IsTypeDesc() || typeHandle.IsArray()) + { + COMPlusThrow(kArgumentException, W("Argument_InvalidValue")); + } + + MethodTable* pMT = typeHandle.AsMethodTable(); + PREFIX_ASSUME(pMT != NULL); + + // Don't allow creating instances of void or delegates + if (pMT == MscorlibBinder::GetElementType(ELEMENT_TYPE_VOID) || pMT->IsDelegate()) + { + COMPlusThrow(kArgumentException, W("Argument_InvalidValue")); + } + + // Don't allow string or string-like (variable length) types. + if (pMT->HasComponentSize()) + { + COMPlusThrow(kArgumentException, W("Argument_NoUninitializedStrings")); + } + + // Don't allow abstract classes or interface types + if (pMT->IsAbstract()) { + COMPlusThrow(kMemberAccessException, W("Acc_CreateAbst")); + } + + // Don't allow open generics or generics instantiated over __Canon + if (pMT->ContainsGenericVariables()) { + COMPlusThrow(kMemberAccessException, W("Acc_CreateGeneric")); + } + if (pMT->IsSharedByGenericInstantiations()) { + COMPlusThrow(kNotSupportedException, W("NotSupported_Type")); + } + + // Don't allow ref structs + if (pMT->IsByRefLike()) { + COMPlusThrow(kNotSupportedException, W("NotSupported_ByRefLike")); + } + + // Never allow the allocation of an unitialized ContextBoundObject derived type, these must always be created with a paired + // transparent proxy or the jit will get confused. + +#ifdef FEATURE_COMINTEROP + // Unless caller allows, do not allow allocation of uninitialized RCWs (COM objects). + // If the caller allows this, getNewHelperStatic will return an appropriate allocator. + if (!fAllowCom && pMT->IsComObjectType()) + { + COMPlusThrow(kNotSupportedException, W("NotSupported_ManagedActivation")); + } +#endif // FEATURE_COMINTEROP + + // If the caller passed Nullable but asked us to unwrap nullable types, + // instead pretend they had passed the 'T' directly. + if (fUnwrapNullable && Nullable::IsNullableType(pMT)) + { + pMT = pMT->GetInstantiation()[0].GetMethodTable(); + } + + // Ensure the type's cctor has run + Assembly* pAssem = pMT->GetAssembly(); + if (!pMT->IsClassInited()) + { + pMT->CheckRestore(); + pMT->EnsureInstanceActive(); + pMT->CheckRunClassInitThrowing(); + } + + // And we're done! + PCODE pNewobjFn = (PCODE)CEEJitInfo::getHelperFtnStatic(CEEInfo::getNewHelperStatic(pMT)); + _ASSERTE(pNewobjFn != NULL); + + *ppNewobjHelper = pNewobjFn; + *ppMT = pMT; + + END_QCALL; +} + NOINLINE FC_BOOL_RET IsInstanceOfTypeHelper(OBJECTREF obj, REFLECTCLASSBASEREF refType) { FCALL_CONTRACT; @@ -2279,209 +2383,6 @@ lExit: ; } FCIMPLEND -//************************************************************************************************* -//************************************************************************************************* -//************************************************************************************************* -// ReflectionSerialization -//************************************************************************************************* -//************************************************************************************************* -//************************************************************************************************* -FCIMPL1(Object*, ReflectionSerialization::GetUninitializedObject, ReflectClassBaseObject* objTypeUNSAFE) { - FCALL_CONTRACT; - - OBJECTREF retVal = NULL; - REFLECTCLASSBASEREF objType = (REFLECTCLASSBASEREF) objTypeUNSAFE; - - HELPER_METHOD_FRAME_BEGIN_RET_NOPOLL(); - - TypeHandle type = objType->GetType(); - - // Don't allow arrays, pointers, byrefs or function pointers. - if (type.IsTypeDesc() || type.IsArray()) - COMPlusThrow(kArgumentException, W("Argument_InvalidValue")); - - MethodTable *pMT = type.AsMethodTable(); - PREFIX_ASSUME(pMT != NULL); - - //We don't allow unitialized Strings or Utf8Strings. - if (pMT == g_pStringClass -#ifdef FEATURE_UTF8STRING - || pMT == g_pUtf8StringClass -#endif // FEATURE_UTF8STRING - ) { - COMPlusThrow(kArgumentException, W("Argument_NoUninitializedStrings")); - } - - // if this is an abstract class or an interface type then we will - // fail this - if (pMT->IsAbstract()) { - COMPlusThrow(kMemberAccessException,W("Acc_CreateAbst")); - } - - if (pMT->ContainsGenericVariables()) { - COMPlusThrow(kMemberAccessException,W("Acc_CreateGeneric")); - } - - if (pMT->IsByRefLike()) { - COMPlusThrow(kNotSupportedException, W("NotSupported_ByRefLike")); - } - - // Never allow allocation of generics actually instantiated over __Canon - if (pMT->IsSharedByGenericInstantiations()) { - COMPlusThrow(kNotSupportedException, W("NotSupported_Type")); - } - - // Never allow the allocation of an unitialized ContextBoundObject derived type, these must always be created with a paired - // transparent proxy or the jit will get confused. - -#ifdef FEATURE_COMINTEROP - // Also do not allow allocation of uninitialized RCWs (COM objects). - if (pMT->IsComObjectType()) - COMPlusThrow(kNotSupportedException, W("NotSupported_ManagedActivation")); -#endif // FEATURE_COMINTEROP - - // If it is a nullable, return the underlying type instead. - if (Nullable::IsNullableType(pMT)) - pMT = pMT->GetInstantiation()[0].GetMethodTable(); - - retVal = pMT->Allocate(); - - HELPER_METHOD_FRAME_END(); - return OBJECTREFToObject(retVal); -} -FCIMPLEND - -FCIMPL1(void*, ReflectionSerialization::GetNewobjHelper, ReflectClassBaseObject* objTypeUNSAFE) { - FCALL_CONTRACT; - - void* retVal = NULL; - REFLECTCLASSBASEREF objType = (REFLECTCLASSBASEREF)objTypeUNSAFE; - - HELPER_METHOD_FRAME_BEGIN_RET_NOPOLL(); - - TypeHandle type = objType->GetType(); - - // Don't allow arrays, pointers, byrefs or function pointers. - if (type.IsTypeDesc() || type.IsArray()) - COMPlusThrow(kArgumentException, W("Argument_InvalidValue")); - - MethodTable* pMT = type.AsMethodTable(); - PREFIX_ASSUME(pMT != NULL); - - //We don't allow unitialized Strings or Utf8Strings. - if (pMT->HasComponentSize()) - { - COMPlusThrow(kArgumentException, W("Argument_NoUninitializedStrings")); - } - - // if this is an abstract class or an interface type then we will - // fail this - if (pMT->IsAbstract()) { - COMPlusThrow(kMemberAccessException, W("Acc_CreateAbst")); - } - - if (pMT->ContainsGenericVariables()) { - COMPlusThrow(kMemberAccessException, W("Acc_CreateGeneric")); - } - - if (pMT->IsByRefLike()) { - COMPlusThrow(kNotSupportedException, W("NotSupported_ByRefLike")); - } - - // Never allow allocation of generics actually instantiated over __Canon - if (pMT->IsSharedByGenericInstantiations()) { - COMPlusThrow(kNotSupportedException, W("NotSupported_Type")); - } - - // Never allow the allocation of an unitialized ContextBoundObject derived type, these must always be created with a paired - // transparent proxy or the jit will get confused. - -#ifdef FEATURE_COMINTEROP - // Also do not allow allocation of uninitialized RCWs (COM objects). - if (pMT->IsComObjectType()) - COMPlusThrow(kNotSupportedException, W("NotSupported_ManagedActivation")); -#endif // FEATURE_COMINTEROP - - Assembly* pAssem = pMT->GetAssembly(); - - if (!pMT->IsClassInited()) - { - pMT->CheckRestore(); - pMT->EnsureInstanceActive(); - pMT->CheckRunClassInitThrowing(); - } - - // TODO: Don't use a void* cast below. - - retVal = (void*)CEEJitInfo::getHelperFtnStatic(CEEInfo::getNewHelperStatic(pMT)); - - HELPER_METHOD_FRAME_END(); - return retVal; -} -FCIMPLEND - -FCIMPL1(FC_BOOL_RET, ReflectionSerialization::IsFastInstantiable, ReflectClassBaseObject* objTypeUNSAFE) { - FCALL_CONTRACT; - - BOOL retVal = FALSE; - REFLECTCLASSBASEREF objType = (REFLECTCLASSBASEREF)objTypeUNSAFE; - - HELPER_METHOD_FRAME_BEGIN_RET_NOPOLL(); - - TypeHandle type = objType->GetType(); - - // Don't allow arrays, pointers, byrefs or function pointers. - if (type.IsTypeDesc() || type.IsArray()) - goto Return; - - MethodTable* pMT = type.AsMethodTable(); - PREFIX_ASSUME(pMT != NULL); - - //We don't allow unitialized Strings or Utf8Strings. - if (pMT->HasComponentSize()) - { - goto Return; - } - - // if this is an abstract class or an interface type then we will - // fail this - if (pMT->IsAbstract()) { - goto Return; - } - - if (pMT->ContainsGenericVariables()) { - goto Return; - } - - if (pMT->IsByRefLike()) { - goto Return; - } - - // Never allow allocation of generics actually instantiated over __Canon - if (pMT->IsSharedByGenericInstantiations()) { - goto Return; - } - - // Never allow the allocation of an unitialized ContextBoundObject derived type, these must always be created with a paired - // transparent proxy or the jit will get confused. - -#ifdef FEATURE_COMINTEROP - // Also do not allow allocation of uninitialized RCWs (COM objects). - if (pMT->IsComObjectType()) - goto Return; -#endif // FEATURE_COMINTEROP - - retVal = TRUE; // all checks succeeded - -Return: - - do { /* dummy required for macro below to compile */ } while (0); - - HELPER_METHOD_FRAME_END(); - FC_RETURN_BOOL(retVal); -} -FCIMPLEND - //************************************************************************************************* //************************************************************************************************* //************************************************************************************************* diff --git a/src/coreclr/src/vm/reflectioninvocation.h b/src/coreclr/src/vm/reflectioninvocation.h index 4052a685858cd..3a3d91ac53764 100644 --- a/src/coreclr/src/vm/reflectioninvocation.h +++ b/src/coreclr/src/vm/reflectioninvocation.h @@ -75,13 +75,6 @@ class ReflectionInvocation { static FCDECL3(Object*, AllocateValueType, ReflectClassBaseObject *targetType, Object *valueUNSAFE, CLR_BOOL fForceTypeChange); }; -class ReflectionSerialization { -public: - static FCDECL1(Object*, GetUninitializedObject, ReflectClassBaseObject* objTypeUNSAFE); - static FCDECL1(void*, GetNewobjHelper, ReflectClassBaseObject* objTypeUNSAFE); - static FCDECL1(FC_BOOL_RET, IsFastInstantiable, ReflectClassBaseObject* objTypeUNSAFE); -}; - class ReflectionEnum { public: static FCDECL1(Object *, InternalGetEnumUnderlyingType, ReflectClassBaseObject *target); diff --git a/src/coreclr/src/vm/runtimehandles.cpp b/src/coreclr/src/vm/runtimehandles.cpp index 2a879151dee5a..5705350009435 100644 --- a/src/coreclr/src/vm/runtimehandles.cpp +++ b/src/coreclr/src/vm/runtimehandles.cpp @@ -638,44 +638,6 @@ FCIMPL2(MethodDesc *, RuntimeTypeHandle::GetMethodAt, ReflectClassBaseObject *pT } FCIMPLEND -FCIMPL1(MethodTable *, RuntimeTypeHandle::GetMethodTable, ReflectClassBaseObject *pTypeUNSAFE) { - CONTRACTL { - FCALL_CHECK; - } - CONTRACTL_END; - - REFLECTCLASSBASEREF refType = (REFLECTCLASSBASEREF)ObjectToOBJECTREF(pTypeUNSAFE); - - if (refType == NULL) - FCThrowRes(kArgumentNullException, W("Arg_InvalidHandle")); - - TypeHandle typeHandle = refType->GetType(); - return typeHandle.GetMethodTable(); -} -FCIMPLEND - -FCIMPL1(MethodDesc *, RuntimeTypeHandle::GetDefaultConstructor, ReflectClassBaseObject *pTypeUNSAFE) { - CONTRACTL { - FCALL_CHECK; - } - CONTRACTL_END; - - REFLECTCLASSBASEREF refType = (REFLECTCLASSBASEREF)ObjectToOBJECTREF(pTypeUNSAFE); - - if (refType == NULL) - FCThrowRes(kArgumentNullException, W("Arg_InvalidHandle")); - - TypeHandle typeHandle = refType->GetType(); - MethodDesc* pRetMethod = NULL; - - HELPER_METHOD_FRAME_BEGIN_RET_1(refType); - pRetMethod = typeHandle.GetMethodTable()->GetDefaultConstructor(); - HELPER_METHOD_FRAME_END(); - - return pRetMethod; -} -FCIMPLEND - FCIMPL3(FC_BOOL_RET, RuntimeTypeHandle::GetFields, ReflectClassBaseObject *pTypeUNSAFE, INT32 **result, INT32 *pCount) { CONTRACTL { FCALL_CHECK; diff --git a/src/coreclr/src/vm/runtimehandles.h b/src/coreclr/src/vm/runtimehandles.h index 4eba567ddd401..f8850fc5696f1 100644 --- a/src/coreclr/src/vm/runtimehandles.h +++ b/src/coreclr/src/vm/runtimehandles.h @@ -131,6 +131,9 @@ class RuntimeTypeHandle { MethodDesc** pConstructor, CLR_BOOL *pbHasNoDefaultCtor); + static + void QCALLTYPE GetNewobjHelperFnPtr(QCall::TypeHandle pTypeHandle, PCODE* ppNewobjHelper, MethodTable** ppMT, BOOL fUnwrapNullable, BOOL fAllowCom); + static void QCALLTYPE MakeByRef(QCall::TypeHandle pTypeHandle, QCall::ObjectHandleOnStack retType); @@ -235,8 +238,6 @@ class RuntimeTypeHandle { static FCDECL1(ReflectClassBaseObject*, GetElementType, ReflectClassBaseObject* pType); static FCDECL2(MethodDesc*, GetMethodAt, PTR_ReflectClassBaseObject pType, INT32 slot); - static FCDECL1(MethodTable*, GetMethodTable, PTR_ReflectClassBaseObject pType); - static FCDECL1(MethodDesc*, GetDefaultConstructor, PTR_ReflectClassBaseObject pType); static FCDECL1(INT32, GetNumVirtuals, ReflectClassBaseObject *pType); static diff --git a/src/libraries/System.Private.CoreLib/src/System/Activator.RuntimeType.cs b/src/libraries/System.Private.CoreLib/src/System/Activator.RuntimeType.cs index c08530b95a2a2..7bee274974df6 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Activator.RuntimeType.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Activator.RuntimeType.cs @@ -152,76 +152,6 @@ public static partial class Activator return (T)((RuntimeType)typeof(T)).CreateInstanceDefaultCtor(publicOnly: true, skipCheckThis: true, fillCache: true, wrapExceptions: true)!; } - public unsafe static Func CreateFactory() - { - // ObjectFactory ctor will perform correctness checks - ObjectFactory factory = new ObjectFactory(); - return factory.CreateInstance; - } - - public static unsafe Func CreateFactory(Type type, bool nonPublic) - { - if (type is null) - { - throw new ArgumentNullException(nameof(type)); - } - - if (!(type.UnderlyingSystemType is RuntimeType rt)) - { - throw new ArgumentException(SR.Arg_MustBeType, nameof(type)); - } - - type = null!; // just to make sure we don't use 'type' for the rest of the method - - if (rt.IsPointer || rt.IsByRef || rt.IsByRefLike || !RuntimeHelpers.IsFastInstantiable(rt)) - { - throw new ArgumentException( - paramName: nameof(type), - message: SR.NotSupported_Type); - } - - MethodTable* pMT = RuntimeTypeHandle.GetMethodTable(rt); - Debug.Assert(pMT != null); - - if (!pMT->HasDefaultConstructor) - { - // If no parameterless ctor exists, we can still fabricate an instance for value - // types, returning a boxed default(T). Unless the incoming type is a - // Nullable, at which point we'll return null instead of a "boxed null". - - if (pMT->IsValueType) - { - if (pMT->IsNullable) - { - return () => null; - } - else - { - ObjectFactory factory = ObjectFactory.CreateFactoryForValueTypeDefaultOfT(rt); - return factory.CreateInstance; - } - } - } - else - { - // If a parameterless ctor exists, perform visibility checks before linking to it. - - RuntimeMethodHandleInternal hCtor = RuntimeTypeHandle.GetDefaultConstructor(rt); - Debug.Assert(!hCtor.IsNullHandle()); - - if (nonPublic || (RuntimeMethodHandle.GetAttributes(hCtor) & MethodAttributes.MemberAccessMask) == MethodAttributes.Public) - { - ObjectFactory factory = new ObjectFactory(hCtor); - return factory.CreateInstance; - } - } - - // If we reached this point, no parameterless ctor was found, or the ctor - // was found but we can't link to it due to member access restrictions. - - throw new MissingMethodException(SR.Format(SR.Arg_NoDefCTor, rt)); - } - private static T CreateDefaultInstance() where T : struct => default; } } diff --git a/src/libraries/System.Runtime/ref/System.Runtime.cs b/src/libraries/System.Runtime/ref/System.Runtime.cs index beebc02131f09..81fe026049b11 100644 --- a/src/libraries/System.Runtime/ref/System.Runtime.cs +++ b/src/libraries/System.Runtime/ref/System.Runtime.cs @@ -67,8 +67,6 @@ public AccessViolationException(string? message, System.Exception? innerExceptio public delegate void Action(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7, T8 arg8, T9 arg9); public static partial class Activator { - public static System.Func CreateFactory(System.Type type, bool nonPublic) { throw null; } - public static System.Func CreateFactory() { throw null; } public static System.Runtime.Remoting.ObjectHandle? CreateInstance(string assemblyName, string typeName) { throw null; } public static System.Runtime.Remoting.ObjectHandle? CreateInstance(string assemblyName, string typeName, bool ignoreCase, System.Reflection.BindingFlags bindingAttr, System.Reflection.Binder? binder, object?[]? args, System.Globalization.CultureInfo? culture, object?[]? activationAttributes) { throw null; } public static System.Runtime.Remoting.ObjectHandle? CreateInstance(string assemblyName, string typeName, object?[]? activationAttributes) { throw null; } @@ -9363,7 +9361,6 @@ public static void ExecuteCodeWithGuaranteedCleanup(System.Runtime.CompilerServi public static object? GetObjectValue(object? obj) { throw null; } public static T[] GetSubArray(T[] array, System.Range range) { throw null; } public static object GetUninitializedObject([System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembersAttribute(System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.NonPublicConstructors | System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors)] System.Type type) { throw null; } - public static System.Func GetUninitializedObjectFactory(System.Type type) { throw null; } public static void InitializeArray(System.Array array, System.RuntimeFieldHandle fldHandle) { } public static bool IsReferenceOrContainsReferences() { throw null; } public static void PrepareConstrainedRegions() { } diff --git a/src/libraries/System.Runtime/tests/System.Runtime.Tests.csproj b/src/libraries/System.Runtime/tests/System.Runtime.Tests.csproj index b3f6feb8528eb..548ac964d8584 100644 --- a/src/libraries/System.Runtime/tests/System.Runtime.Tests.csproj +++ b/src/libraries/System.Runtime/tests/System.Runtime.Tests.csproj @@ -202,7 +202,6 @@ - diff --git a/src/libraries/System.Runtime/tests/System/Reflection/ObjectFactoryTests.cs b/src/libraries/System.Runtime/tests/System/Reflection/ObjectFactoryTests.cs deleted file mode 100644 index 430da045bce71..0000000000000 --- a/src/libraries/System.Runtime/tests/System/Reflection/ObjectFactoryTests.cs +++ /dev/null @@ -1,169 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System.Collections.Generic; -using System.Collections.Tests; -using System.Runtime.CompilerServices; -using System.Tests; -using System.Tests.Types; -using Xunit; -using Xunit.Sdk; - -namespace System.Reflection.Tests -{ - public unsafe class ObjectFactoryTests - { - // Types that should fail when used in the ObjectFactory - public static IEnumerable NegativeTypes() - { - foreach (Type type in EnumerateTestCases()) - { - yield return new object[] { type }; - } - - IEnumerable EnumerateTestCases() - { - yield return null; - yield return typeof(int*); // pointers are disallowed - yield return typeof(int).MakeByRef(); // byrefs are disallowed - yield return typeof(Span); // ref structs are disallowed - yield return typeof(Enum); // uninstantiable type - yield return typeof(ValueType); // uninstantiable type - yield return typeof(Array); // uninstantiable type - yield return typeof(IDisposable); // interfaces disallowed - yield return typeof(string); // variable length types disallowed - yield return typeof(int[]); // variable length types disallowed - yield return typeof(MyAbstractClass); // abstract types disallowed - yield return typeof(List<>); // open generic types disallowed - yield return CanonType; // canon type disallowed - yield return typeof(List<>).MakeGenericType(CanonType); // shared instantiation disallowed - yield return typeof(ClassWithoutParameterlessCtor); // parameterless ctor not found - yield return typeof(StaticClass); // static types disallowed - yield return typeof(delegate*); // function pointers disallowed - yield return typeof(List<>).GetGenericArguments(0); // type parameters disallowed - } - } - - [Theory] - [MemberData(nameof(NegativeTypes))] - public void Activator_CreateFactory_OfInvalidType_Throws(Type type) - { - Exception throwException = Assert.ThrowsAny(() => Activator.CreateFactory(type, nonPublic: true)); - - switch (throwException) - { - case ArgumentException: - case MissingMethodException: - case NotSupportedException: - break; - - default: - throw new XunitException($"Unexpected exception {throwException.GetType()} thrown."); - } - } - - [Theory] - [MemberData(nameof(NegativeTypes))] - public void RuntimeHelpers_CreateUninitializedObjectFactory_OfInvalidType_Throws(Type type) - { - Exception throwException = Assert.ThrowsAny(() => RuntimeHelpers.CreateUninitializedObjectFactory(type)); - - switch (throwException) - { - case ArgumentException: - case MissingMethodException: - case NotSupportedException: - break; - - default: - throw new XunitException($"Unexpected exception {throwException.GetType()} thrown."); - } - } - - [Fact] - public void Activator_CreateFactory_ReferenceTypeWithParameterlessCtor() - { - ClassWithPublicParameterlessCtor c1 = Activator.CreateFactory(typeof(ClassWithPublicParameterlessCtor), nonPublic: false)() as ClassWithPublicParameterlessCtor; - Assert.NotNull(c1); - Assert.True(c1.WasConstructorCalled); - - ClassWithPublicParameterlessCtor c2 = Activator.CreateFactory(typeof(ClassWithPublicParameterlessCtor), nonPublic: true)() as ClassWithPublicParameterlessCtor; - Assert.NotNull(c2); - Assert.True(c2.WasConstructorCalled); - - // if ctor not visible, shouldn't even get as far as creating the factory - Assert.Throws(() => Activator.CreateFactory(typeof(ClassWithoutParameterlessCtor), nonPublic: false)); - - ClassWithoutParameterlessCtor c4 = Activator.CreateFactory(typeof(ClassWithoutParameterlessCtor), nonPublic: true)() as ClassWithoutParameterlessCtor; - Assert.NotNull(c4); - Assert.True(c4.WasConstructorCalled); - } - - [Fact] - public void RuntimeHelpers_CreateUninitializedObjectFactory_ReferenceTypeWithParameterlessCtor() - { - ClassWithPublicParameterlessCtor c1 = RuntimeHelpers.CreateUninitializedObjectFactory(typeof(ClassWithPublicParameterlessCtor))() as ClassWithPublicParameterlessCtor; - Assert.NotNull(c1); - Assert.False(c1.WasConstructorCalled); - - ClassWithNonPublicParameterlessCtor c2 = RuntimeHelpers.CreateUninitializedObjectFactory(typeof(ClassWithNonPublicParameterlessCtor))() as ClassWithNonPublicParameterlessCtor; - Assert.NotNull(c2); - Assert.False(c2.WasConstructorCalled); - - ClassWithoutParameterlessCtor c3 = RuntimeHelpers.CreateUninitializedObjectFactory(typeof(ClassWithoutParameterlessCtor))() as ClassWithoutParameterlessCtor; - Assert.NotNull(c3); - Assert.False(c3.WasConstructorCalled); - } - - // TODO Unit tests: - // When T is value type with custom ctor - // When T is value type without custom ctor - // When T is Nullable: Activator should return null, RuntimeHelpers should return boxed default(U) - // Activator.CreateFactory helper - - public static Type CanonType - { - get - { - Type type = typeof(object).Assembly.GetType("System.__Canon"); - Assert.NotNull(type); - return type; - } - } - - public abstract class MyAbstractClass { } - - public class ClassWithoutParameterlessCtor - { - public bool WasConstructorCalled; - - public ClassWithoutParameterlessCtor(int i) - { - WasConstructorCalled = true; - } - } - - public static class StaticClass { } - - public class ClassWithPublicParameterlessCtor - { - public bool WasConstructorCalled; - - public ClassWithPublicParameterlessCtor() - { - WasConstructorCalled = true; - } - } - - public class ClassWithNonPublicParameterlessCtor - { - public bool WasConstructorCalled; - - internal ClassWithNonPublicParameterlessCtor() - { - WasConstructorCalled = true; - } - } - } -} diff --git a/src/libraries/System.Runtime/tests/System/Runtime/CompilerServices/RuntimeHelpersTests.cs b/src/libraries/System.Runtime/tests/System/Runtime/CompilerServices/RuntimeHelpersTests.cs index 2f0f05b749b7e..73e979198afbf 100644 --- a/src/libraries/System.Runtime/tests/System/Runtime/CompilerServices/RuntimeHelpersTests.cs +++ b/src/libraries/System.Runtime/tests/System/Runtime/CompilerServices/RuntimeHelpersTests.cs @@ -2,11 +2,10 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -using System; -using System.Reflection; using System.Collections; using System.Collections.Generic; -using System.Runtime.CompilerServices; +using System.IO; +using System.Reflection; using System.Runtime.InteropServices; using Xunit; @@ -193,16 +192,57 @@ private static void FillStack(int depth) } } - [Fact] - public static void GetUninitializedObject_InvalidArguments_ThrowsException() + public static IEnumerable GetUninitializedObject_NegativeTestCases() + { + // TODO: Test actual function pointer types when typeof(delegate*<...>) support is available + + Type comObjType = typeof(object).Assembly.GetType("System.__ComObject", throwOnError: true); + Type canonType = typeof(object).Assembly.GetType("System.__Canon", throwOnError: true); + + yield return new[] { typeof(string), typeof(ArgumentException) }; // variable-length type + yield return new[] { typeof(int[]), typeof(ArgumentException) }; // variable-length type + yield return new[] { typeof(int[,]), typeof(ArgumentException) }; // variable-length type + yield return new[] { Array.CreateInstance(typeof(int), new[] { 1 }, new [] { 1 }).GetType(), typeof(ArgumentException) }; // variable-length type (non-szarray) + yield return new[] { typeof(Array), typeof(MemberAccessException) }; // abstract type + yield return new[] { typeof(Enum), typeof(MemberAccessException) }; // abstract type + + yield return new[] { typeof(Stream), typeof(MemberAccessException) }; // abstract type + yield return new[] { typeof(IDisposable), typeof(MemberAccessException) }; // interface type + + yield return new[] { typeof(List<>), typeof(MemberAccessException) }; // open generic type + yield return new[] { typeof(List<>).GetGenericArguments()[0], typeof(ArgumentException) }; // 'T' placeholder typedesc + + yield return new[] { typeof(Delegate), typeof(MemberAccessException) }; // abstract type + yield return new[] { typeof(Action), typeof(ArgumentException) }; // delegate type + + yield return new[] { typeof(void), typeof(ArgumentException) }; // explicit block in place + yield return new[] { typeof(int).MakePointerType(), typeof(ArgumentException) }; // pointer typedesc + yield return new[] { typeof(int).MakeByRefType(), typeof(ArgumentException) }; // byref typedesc + + yield return new[] { typeof(ReadOnlySpan), typeof(NotSupportedException) }; // byref type + yield return new[] { typeof(ArgIterator), typeof(NotSupportedException) }; // byref type + yield return new[] { typeof(List<>).MakeGenericType(canonType), typeof(NotSupportedException) }; // shared by generic instantiations + + yield return new[] { comObjType, typeof(NotSupportedException) }; // COM type + if (PlatformDetection.IsWindows) + { + yield return new[] { typeof(WbemContext), typeof(NotSupportedException) }; // COM type + } + } + + // This type definition is lifted from System.Management, just for testing purposes + [ClassInterface((short)0x0000)] + [Guid("674B6698-EE92-11D0-AD71-00C04FD8FDFF")] + [ComImport] + internal class WbemContext { - AssertExtensions.Throws("type", () => RuntimeHelpers.GetUninitializedObject(null)); + } - AssertExtensions.Throws(null, () => RuntimeHelpers.GetUninitializedObject(typeof(string))); // special type - Assert.Throws(() => RuntimeHelpers.GetUninitializedObject(typeof(System.IO.Stream))); // abstract type - Assert.Throws(() => RuntimeHelpers.GetUninitializedObject(typeof(System.Collections.IEnumerable))); // interface - Assert.Throws(() => RuntimeHelpers.GetUninitializedObject(typeof(System.Collections.Generic.List<>))); // generic definition - Assert.Throws(() => RuntimeHelpers.GetUninitializedObject(typeof(TypedReference))); // byref-like type + [Theory] + [MemberData(nameof(GetUninitializedObject_NegativeTestCases))] + public static void GetUninitializedObject_InvalidArguments_ThrowsException(Type typeToInstantiate, Type expectedExceptionType) + { + Assert.Throws(expectedExceptionType, () => RuntimeHelpers.GetUninitializedObject(typeToInstantiate)); } [Fact] @@ -212,11 +252,19 @@ public static void GetUninitializedObject_DoesNotRunConstructor() Assert.Equal(0, ((ObjectWithDefaultCtor)RuntimeHelpers.GetUninitializedObject(typeof(ObjectWithDefaultCtor))).Value); } + [Fact] + public static void GetUninitializedObject_Struct() + { + object o = RuntimeHelpers.GetUninitializedObject(typeof(Guid)); + Assert.Equal(Guid.Empty, Assert.IsType(o)); + } + [Fact] public static void GetUninitializedObject_Nullable() { // Nullable returns the underlying type instead - Assert.Equal(typeof(int), RuntimeHelpers.GetUninitializedObject(typeof(Nullable)).GetType()); + object o = RuntimeHelpers.GetUninitializedObject(typeof(int?)); + Assert.Equal(0, Assert.IsType(o)); } private class ObjectWithDefaultCtor From c5ae79f3c344ab695efd933edbe6d3139c3a4784 Mon Sep 17 00:00:00 2001 From: Levi Broderick Date: Tue, 7 Jul 2020 22:47:11 -0700 Subject: [PATCH 03/64] Re-enable analyzers in project --- .../src/System.Private.CoreLib/System.Private.CoreLib.csproj | 3 +-- .../System.Private.CoreLib/src/System/RuntimeType.CoreCLR.cs | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/src/coreclr/src/System.Private.CoreLib/System.Private.CoreLib.csproj b/src/coreclr/src/System.Private.CoreLib/System.Private.CoreLib.csproj index 2de93d7df6f66..7d77fd31d77b9 100644 --- a/src/coreclr/src/System.Private.CoreLib/System.Private.CoreLib.csproj +++ b/src/coreclr/src/System.Private.CoreLib/System.Private.CoreLib.csproj @@ -59,8 +59,7 @@ <_FullFrameworkReferenceAssemblyPaths>$(MSBuildThisFileDirectory)/Documentation true $(OutputPath)$(MSBuildProjectName).xml - - false + true diff --git a/src/coreclr/src/System.Private.CoreLib/src/System/RuntimeType.CoreCLR.cs b/src/coreclr/src/System.Private.CoreLib/src/System/RuntimeType.CoreCLR.cs index 4e9612313c3d6..3e6c887e78d89 100644 --- a/src/coreclr/src/System.Private.CoreLib/src/System/RuntimeType.CoreCLR.cs +++ b/src/coreclr/src/System.Private.CoreLib/src/System/RuntimeType.CoreCLR.cs @@ -3882,7 +3882,7 @@ internal ActivatorCache(RuntimeMethodHandleInternal rmh) // is implicitly public and no-ops. _ctorIsPublic = true; - _pfnCtor = &GC.KeepAlive; // no-op fn + _pfnCtor = &GC.KeepAlive; // a convenient no-op fn } else { From e09e91bbc4581d33b07ba70f8d808b2e1d8bc8ea Mon Sep 17 00:00:00 2001 From: Levi Broderick Date: Wed, 8 Jul 2020 00:18:37 -0700 Subject: [PATCH 04/64] Don't run COM tests on non-Windows --- .../System/Runtime/CompilerServices/RuntimeHelpersTests.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/libraries/System.Runtime/tests/System/Runtime/CompilerServices/RuntimeHelpersTests.cs b/src/libraries/System.Runtime/tests/System/Runtime/CompilerServices/RuntimeHelpersTests.cs index 73e979198afbf..5b3667da142ee 100644 --- a/src/libraries/System.Runtime/tests/System/Runtime/CompilerServices/RuntimeHelpersTests.cs +++ b/src/libraries/System.Runtime/tests/System/Runtime/CompilerServices/RuntimeHelpersTests.cs @@ -196,7 +196,6 @@ public static IEnumerable GetUninitializedObject_NegativeTestCases() { // TODO: Test actual function pointer types when typeof(delegate*<...>) support is available - Type comObjType = typeof(object).Assembly.GetType("System.__ComObject", throwOnError: true); Type canonType = typeof(object).Assembly.GetType("System.__Canon", throwOnError: true); yield return new[] { typeof(string), typeof(ArgumentException) }; // variable-length type @@ -223,9 +222,10 @@ public static IEnumerable GetUninitializedObject_NegativeTestCases() yield return new[] { typeof(ArgIterator), typeof(NotSupportedException) }; // byref type yield return new[] { typeof(List<>).MakeGenericType(canonType), typeof(NotSupportedException) }; // shared by generic instantiations - yield return new[] { comObjType, typeof(NotSupportedException) }; // COM type if (PlatformDetection.IsWindows) { + Type comObjType = typeof(object).Assembly.GetType("System.__ComObject", throwOnError: true); + yield return new[] { comObjType, typeof(NotSupportedException) }; // COM type yield return new[] { typeof(WbemContext), typeof(NotSupportedException) }; // COM type } } From 17a3bdebf65d548ea247b89d349d6fc828c83a9a Mon Sep 17 00:00:00 2001 From: Levi Broderick Date: Wed, 8 Jul 2020 10:42:29 -0700 Subject: [PATCH 05/64] PR feedback - Don't be so aggressive about running cctors - Some minor code cleanup - Don't swallow OOMs --- .../src/System/RuntimeHandles.cs | 9 +++- .../src/System/RuntimeType.CoreCLR.cs | 10 ++-- src/coreclr/src/vm/reflectioninvocation.cpp | 13 +++-- .../TestUtilities/System/PlatformDetection.cs | 1 + .../CompilerServices/RuntimeHelpersTests.cs | 51 +++++++++++++++++-- 5 files changed, 68 insertions(+), 16 deletions(-) diff --git a/src/coreclr/src/System.Private.CoreLib/src/System/RuntimeHandles.cs b/src/coreclr/src/System.Private.CoreLib/src/System/RuntimeHandles.cs index 501e96f9a1437..fdbba863d0e53 100644 --- a/src/coreclr/src/System.Private.CoreLib/src/System/RuntimeHandles.cs +++ b/src/coreclr/src/System.Private.CoreLib/src/System/RuntimeHandles.cs @@ -214,7 +214,14 @@ internal static bool HasElementType(RuntimeType type) /// MethodTable* corresponding to that type. If the type is closed over /// some T and is true, then returns the values for the 'T'. /// - internal static delegate* GetNewobjHelperFnPtr(RuntimeType type, out MethodTable* pMT, bool unwrapNullable, bool allowCom) + internal static delegate* GetNewobjHelperFnPtr( + // This API doesn't call any constructors, but the type needs to be seen as constructed. + // A type is seen as constructed if a constructor is kept. + // This obviously won't cover a type with no constructor. Reference types with no + // constructor are an academic problem. Valuetypes with no constructors are a problem, + // but IL Linker currently treats them as always implicitly boxed. + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.NonPublicConstructors)] RuntimeType type, + out MethodTable* pMT, bool unwrapNullable, bool allowCom) { Debug.Assert(type != null); diff --git a/src/coreclr/src/System.Private.CoreLib/src/System/RuntimeType.CoreCLR.cs b/src/coreclr/src/System.Private.CoreLib/src/System/RuntimeType.CoreCLR.cs index 3e6c887e78d89..7c890cc2b3f25 100644 --- a/src/coreclr/src/System.Private.CoreLib/src/System/RuntimeType.CoreCLR.cs +++ b/src/coreclr/src/System.Private.CoreLib/src/System/RuntimeType.CoreCLR.cs @@ -3971,24 +3971,26 @@ public void EnsureInitialized(RuntimeType type) Debug.Assert(cache._pfnNewobj != null); Debug.Assert(cache._pfnCtor != null); + Debug.Assert(cache._pMT != null); if (!cache._ctorIsPublic && publicOnly) { throw new MissingMethodException(SR.Format(SR.Arg_NoDefCTor, this)); } + object? obj = cache._pfnNewobj(cache._pMT); // allocation outside the try block (allow OOM to bubble up) + GC.KeepAlive(this); // can't allow the type to be collected before the object is created + try { - object? obj = cache._pfnNewobj(cache._pMT); - GC.KeepAlive(this); // can't allow the type to be collected before the object is created - cache._pfnCtor(obj); - return obj; } catch (Exception e) when (wrapExceptions) { throw new TargetInvocationException(e); } + + return obj; } if (!skipCheckThis) diff --git a/src/coreclr/src/vm/reflectioninvocation.cpp b/src/coreclr/src/vm/reflectioninvocation.cpp index be484f8157eac..ebafe5ec7d4b0 100644 --- a/src/coreclr/src/vm/reflectioninvocation.cpp +++ b/src/coreclr/src/vm/reflectioninvocation.cpp @@ -568,7 +568,7 @@ FCIMPLEND * throws an exception. If TypeHandle is a value type, the NEWOBJ helper will create * a boxed zero-inited instance of the value type. */ - void QCALLTYPE RuntimeTypeHandle::GetNewobjHelperFnPtr( +void QCALLTYPE RuntimeTypeHandle::GetNewobjHelperFnPtr( QCall::TypeHandle pTypeHandle, PCODE* ppNewobjHelper, MethodTable** ppMT, @@ -597,6 +597,8 @@ FCIMPLEND MethodTable* pMT = typeHandle.AsMethodTable(); PREFIX_ASSUME(pMT != NULL); + pMT->EnsureInstanceActive(); + // Don't allow creating instances of void or delegates if (pMT == MscorlibBinder::GetElementType(ELEMENT_TYPE_VOID) || pMT->IsDelegate()) { @@ -646,13 +648,10 @@ FCIMPLEND pMT = pMT->GetInstantiation()[0].GetMethodTable(); } - // Ensure the type's cctor has run - Assembly* pAssem = pMT->GetAssembly(); - if (!pMT->IsClassInited()) + // Run the type's cctor if needed (if not marked beforefieldinit) + if (pMT->HasPreciseInitCctors()) { - pMT->CheckRestore(); - pMT->EnsureInstanceActive(); - pMT->CheckRunClassInitThrowing(); + pMT->CheckRunClassInitAsIfConstructingThrowing(); } // And we're done! diff --git a/src/libraries/Common/tests/TestUtilities/System/PlatformDetection.cs b/src/libraries/Common/tests/TestUtilities/System/PlatformDetection.cs index f22afa8b6a05c..d9292d7351bc2 100644 --- a/src/libraries/Common/tests/TestUtilities/System/PlatformDetection.cs +++ b/src/libraries/Common/tests/TestUtilities/System/PlatformDetection.cs @@ -69,6 +69,7 @@ public static bool IsDrawingSupported } public static bool IsInContainer => GetIsInContainer(); + public static bool SupportsComInterop => IsWindows && IsNetCore; // matches definitions in clr.featuredefines.props public static bool SupportsSsl3 => GetSsl3Support(); public static bool SupportsSsl2 => IsWindows && !PlatformDetection.IsWindows10Version1607OrGreater; diff --git a/src/libraries/System.Runtime/tests/System/Runtime/CompilerServices/RuntimeHelpersTests.cs b/src/libraries/System.Runtime/tests/System/Runtime/CompilerServices/RuntimeHelpersTests.cs index 5b3667da142ee..bdfd23323170e 100644 --- a/src/libraries/System.Runtime/tests/System/Runtime/CompilerServices/RuntimeHelpersTests.cs +++ b/src/libraries/System.Runtime/tests/System/Runtime/CompilerServices/RuntimeHelpersTests.cs @@ -196,8 +196,6 @@ public static IEnumerable GetUninitializedObject_NegativeTestCases() { // TODO: Test actual function pointer types when typeof(delegate*<...>) support is available - Type canonType = typeof(object).Assembly.GetType("System.__Canon", throwOnError: true); - yield return new[] { typeof(string), typeof(ArgumentException) }; // variable-length type yield return new[] { typeof(int[]), typeof(ArgumentException) }; // variable-length type yield return new[] { typeof(int[,]), typeof(ArgumentException) }; // variable-length type @@ -220,9 +218,14 @@ public static IEnumerable GetUninitializedObject_NegativeTestCases() yield return new[] { typeof(ReadOnlySpan), typeof(NotSupportedException) }; // byref type yield return new[] { typeof(ArgIterator), typeof(NotSupportedException) }; // byref type - yield return new[] { typeof(List<>).MakeGenericType(canonType), typeof(NotSupportedException) }; // shared by generic instantiations - if (PlatformDetection.IsWindows) + if (PlatformDetection.IsNetCore) + { + Type canonType = typeof(object).Assembly.GetType("System.__Canon", throwOnError: true); + yield return new[] { typeof(List<>).MakeGenericType(canonType), typeof(NotSupportedException) }; // shared by generic instantiations + } + + if (PlatformDetection.SupportsComInterop) { Type comObjType = typeof(object).Assembly.GetType("System.__ComObject", throwOnError: true); yield return new[] { comObjType, typeof(NotSupportedException) }; // COM type @@ -238,6 +241,46 @@ internal class WbemContext { } + internal class ClassWithBeforeFieldInitCctor + { + private static readonly int _theInt = GetInt(); + + private static int GetInt() + { + AppDomain.CurrentDomain.SetData("ClassWithBeforeFieldInitCctor_CctorRan", true); + return 0; + } + } + + internal class ClassWithNormalCctor + { + private static readonly int _theInt; + + static ClassWithNormalCctor() + { + AppDomain.CurrentDomain.SetData("ClassWithNormalCctor_CctorRan", true); + _theInt = 0; + } + } + + [Fact] + public static void GetUninitalizedObject_DoesNotRunBeforeFieldInitCctors() + { + object o = RuntimeHelpers.GetUninitializedObject(typeof(ClassWithBeforeFieldInitCctor)); + Assert.IsType(o); + + Assert.Null(AppDomain.CurrentDomain.GetData("ClassWithBeforeFieldInitCctor_CctorRan")); + } + + [Fact] + public static void GetUninitalizedObject_RunsNormalStaticCtors() + { + object o = RuntimeHelpers.GetUninitializedObject(typeof(ClassWithNormalCctor)); + Assert.IsType(o); + + Assert.Equal(true, AppDomain.CurrentDomain.GetData("ClassWithNormalCctor_CctorRan")); + } + [Theory] [MemberData(nameof(GetUninitializedObject_NegativeTestCases))] public static void GetUninitializedObject_InvalidArguments_ThrowsException(Type typeToInstantiate, Type expectedExceptionType) From eb368eb6a6f6f37b17eaca7b3804b1639b761bb8 Mon Sep 17 00:00:00 2001 From: Levi Broderick Date: Tue, 17 Nov 2020 15:38:49 -0800 Subject: [PATCH 06/64] Cleanup + PR feedback --- .../System/Runtime/CompilerServices/RuntimeHelpers.CoreCLR.cs | 4 +++- .../System.Private.CoreLib/src/System/RuntimeType.CoreCLR.cs | 3 +-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/coreclr/src/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.CoreCLR.cs b/src/coreclr/src/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.CoreCLR.cs index 6e481844c8b63..7e3a11ac3414a 100644 --- a/src/coreclr/src/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.CoreCLR.cs +++ b/src/coreclr/src/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.CoreCLR.cs @@ -136,7 +136,9 @@ public static int OffsetToStringData [MethodImpl(MethodImplOptions.InternalCall)] public static extern bool TryEnsureSufficientExecutionStack(); - private static unsafe object GetUninitializedObjectInternal(Type type) + private static unsafe object GetUninitializedObjectInternal( + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.NonPublicConstructors)] + Type type) { RuntimeType rt = (RuntimeType)type; Debug.Assert(rt != null); diff --git a/src/coreclr/src/System.Private.CoreLib/src/System/RuntimeType.CoreCLR.cs b/src/coreclr/src/System.Private.CoreLib/src/System/RuntimeType.CoreCLR.cs index 05d97361c89ca..9c8fd22edbe12 100644 --- a/src/coreclr/src/System.Private.CoreLib/src/System/RuntimeType.CoreCLR.cs +++ b/src/coreclr/src/System.Private.CoreLib/src/System/RuntimeType.CoreCLR.cs @@ -3999,6 +3999,7 @@ private void Initialize(RuntimeType type) delegate* pfnNewobj = RuntimeTypeHandle.GetNewobjHelperFnPtr(type, out _pMT, unwrapNullable: false, allowCom: true); if (_pMT->IsNullable) { + static object? GetNull(MethodTable* pMT) => null; pfnNewobj = &GetNull; // Activator.CreateInstance(typeof(Nullable)) => null } @@ -4014,8 +4015,6 @@ public void EnsureInitialized(RuntimeType type) Initialize(type); } } - - private static object? GetNull(MethodTable* pMT) => null; } /// From faeef8c3f12eda4532418a20955352a33638989c Mon Sep 17 00:00:00 2001 From: Levi Broderick Date: Wed, 18 Nov 2020 11:36:24 -0800 Subject: [PATCH 07/64] Remove 'unwrapNullable' parameter --- .../Runtime/CompilerServices/RuntimeHelpers.CoreCLR.cs | 4 ++-- .../System.Private.CoreLib/src/System/RuntimeHandles.cs | 7 +++---- .../src/System/RuntimeType.CoreCLR.cs | 2 +- src/coreclr/src/vm/reflectioninvocation.cpp | 8 +++----- src/coreclr/src/vm/runtimehandles.h | 2 +- 5 files changed, 10 insertions(+), 13 deletions(-) diff --git a/src/coreclr/src/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.CoreCLR.cs b/src/coreclr/src/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.CoreCLR.cs index 7e3a11ac3414a..b371a8a35d995 100644 --- a/src/coreclr/src/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.CoreCLR.cs +++ b/src/coreclr/src/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.CoreCLR.cs @@ -143,8 +143,8 @@ private static unsafe object GetUninitializedObjectInternal( RuntimeType rt = (RuntimeType)type; Debug.Assert(rt != null); - // If somebody asks us to create a Nullable, create a T instead. - delegate* newobjHelper = RuntimeTypeHandle.GetNewobjHelperFnPtr(rt, out MethodTable* pMT, unwrapNullable: true, allowCom: false); + // If type is Nullable, get newobj for boxed T instead. + delegate* newobjHelper = RuntimeTypeHandle.GetNewobjHelperFnPtr(rt, out MethodTable* pMT, allowCom: false); Debug.Assert(newobjHelper != null); Debug.Assert(pMT != null); diff --git a/src/coreclr/src/System.Private.CoreLib/src/System/RuntimeHandles.cs b/src/coreclr/src/System.Private.CoreLib/src/System/RuntimeHandles.cs index 094ca31acea7f..4ebf752facf93 100644 --- a/src/coreclr/src/System.Private.CoreLib/src/System/RuntimeHandles.cs +++ b/src/coreclr/src/System.Private.CoreLib/src/System/RuntimeHandles.cs @@ -208,7 +208,7 @@ internal static bool HasElementType(RuntimeType type) /// /// Given a RuntimeType, returns both the address of the JIT's newobj helper for that type and the /// MethodTable* corresponding to that type. If the type is closed over - /// some T and is true, then returns the values for the 'T'. + /// some T, then returns the newobj helper and MethodTable* for the 'T'. /// internal static delegate* GetNewobjHelperFnPtr( // This API doesn't call any constructors, but the type needs to be seen as constructed. @@ -217,7 +217,7 @@ internal static bool HasElementType(RuntimeType type) // constructor are an academic problem. Valuetypes with no constructors are a problem, // but IL Linker currently treats them as always implicitly boxed. [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.NonPublicConstructors)] RuntimeType type, - out MethodTable* pMT, bool unwrapNullable, bool allowCom) + out MethodTable* pMT, bool allowCom) { Debug.Assert(type != null); @@ -228,7 +228,6 @@ internal static bool HasElementType(RuntimeType type) new QCallTypeHandle(ref type), &pNewobjHelperTemp, &pMTTemp, - (unwrapNullable) ? Interop.BOOL.TRUE : Interop.BOOL.FALSE, (allowCom) ? Interop.BOOL.TRUE : Interop.BOOL.FALSE); pMT = pMTTemp; @@ -236,7 +235,7 @@ internal static bool HasElementType(RuntimeType type) } [DllImport(RuntimeHelpers.QCall, CharSet = CharSet.Unicode)] - private static extern void GetNewobjHelperFnPtr(QCallTypeHandle typeHandle, delegate** ppNewobjHelper, MethodTable** ppMT, Interop.BOOL fUnwrapNullable, Interop.BOOL fAllowCom); + private static extern void GetNewobjHelperFnPtr(QCallTypeHandle typeHandle, delegate** ppNewobjHelper, MethodTable** ppMT, Interop.BOOL fAllowCom); internal RuntimeType GetRuntimeType() { diff --git a/src/coreclr/src/System.Private.CoreLib/src/System/RuntimeType.CoreCLR.cs b/src/coreclr/src/System.Private.CoreLib/src/System/RuntimeType.CoreCLR.cs index 9c8fd22edbe12..e68e2e4d88aff 100644 --- a/src/coreclr/src/System.Private.CoreLib/src/System/RuntimeType.CoreCLR.cs +++ b/src/coreclr/src/System.Private.CoreLib/src/System/RuntimeType.CoreCLR.cs @@ -3996,7 +3996,7 @@ private void Initialize(RuntimeType type) // to the backing field, as setting the field marks initialization as complete. // Be sure to perform any last-minute checks *before* setting the backing field. - delegate* pfnNewobj = RuntimeTypeHandle.GetNewobjHelperFnPtr(type, out _pMT, unwrapNullable: false, allowCom: true); + delegate* pfnNewobj = RuntimeTypeHandle.GetNewobjHelperFnPtr(type, out _pMT, allowCom: true); if (_pMT->IsNullable) { static object? GetNull(MethodTable* pMT) => null; diff --git a/src/coreclr/src/vm/reflectioninvocation.cpp b/src/coreclr/src/vm/reflectioninvocation.cpp index d2c61b1281b6b..0503f884fe967 100644 --- a/src/coreclr/src/vm/reflectioninvocation.cpp +++ b/src/coreclr/src/vm/reflectioninvocation.cpp @@ -2186,7 +2186,6 @@ void QCALLTYPE RuntimeTypeHandle::GetNewobjHelperFnPtr( QCall::TypeHandle pTypeHandle, PCODE* ppNewobjHelper, MethodTable** ppMT, - BOOL fUnwrapNullable, BOOL fAllowCom) { CONTRACTL{ @@ -2203,7 +2202,7 @@ void QCALLTYPE RuntimeTypeHandle::GetNewobjHelperFnPtr( TypeHandle typeHandle = pTypeHandle.AsTypeHandle(); // Don't allow void, arrays, pointers, byrefs, or function pointers. - if (typeHandle.IsTypeDesc() || typeHandle.IsArray() || type.GetSignatureCorElementType() == ELEMENT_TYPE_VOID) + if (typeHandle.IsTypeDesc() || typeHandle.IsArray() || typeHandle.GetSignatureCorElementType() == ELEMENT_TYPE_VOID) { COMPlusThrow(kArgumentException, W("Argument_InvalidValue")); } @@ -2255,9 +2254,8 @@ void QCALLTYPE RuntimeTypeHandle::GetNewobjHelperFnPtr( } #endif // FEATURE_COMINTEROP - // If the caller passed Nullable but asked us to unwrap nullable types, - // instead pretend they had passed the 'T' directly. - if (fUnwrapNullable && Nullable::IsNullableType(pMT)) + // If the caller passed Nullable, instead pretend they had passed the 'T' directly. + if (Nullable::IsNullableType(pMT)) { pMT = pMT->GetInstantiation()[0].GetMethodTable(); } diff --git a/src/coreclr/src/vm/runtimehandles.h b/src/coreclr/src/vm/runtimehandles.h index 59ba9ad3d9984..43a88cf4f0448 100644 --- a/src/coreclr/src/vm/runtimehandles.h +++ b/src/coreclr/src/vm/runtimehandles.h @@ -131,7 +131,7 @@ class RuntimeTypeHandle { CLR_BOOL *pbHasNoDefaultCtor); static - void QCALLTYPE GetNewobjHelperFnPtr(QCall::TypeHandle pTypeHandle, PCODE* ppNewobjHelper, MethodTable** ppMT, BOOL fUnwrapNullable, BOOL fAllowCom); + void QCALLTYPE GetNewobjHelperFnPtr(QCall::TypeHandle pTypeHandle, PCODE* ppNewobjHelper, MethodTable** ppMT, BOOL fAllowCom); static void QCALLTYPE MakeByRef(QCall::TypeHandle pTypeHandle, QCall::ObjectHandleOnStack retType); From bdb403aa52c10346fbf30bed21c384828e5b8628 Mon Sep 17 00:00:00 2001 From: Levi Broderick Date: Wed, 18 Nov 2020 13:44:55 -0800 Subject: [PATCH 08/64] Quick GetDefaultCtor helper --- .../RuntimeHelpers.CoreCLR.cs | 13 ++++++++ src/coreclr/src/vm/ecalllist.h | 1 + src/coreclr/src/vm/reflectioninvocation.cpp | 31 +++++++++++++++++++ src/coreclr/src/vm/runtimehandles.h | 3 ++ 4 files changed, 48 insertions(+) diff --git a/src/coreclr/src/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.CoreCLR.cs b/src/coreclr/src/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.CoreCLR.cs index b371a8a35d995..a6894e38d8f3e 100644 --- a/src/coreclr/src/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.CoreCLR.cs +++ b/src/coreclr/src/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.CoreCLR.cs @@ -378,6 +378,8 @@ internal unsafe struct MethodTable private uint Flags; [FieldOffset(4)] public uint BaseSize; + [FieldOffset(8)] + public ushort Flags2; [FieldOffset(0x0e)] public ushort InterfaceCount; [FieldOffset(ParentMethodTableOffset)] @@ -403,6 +405,9 @@ internal unsafe struct MethodTable | 0x00400000 // enum_flag_ICastable; | 0x00200000;// enum_flag_IDynamicInterfaceCastable; + // WFLAGS2_ENUM + private const ushort enum_flag_HasCctor = 0x0400; + private const int DebugClassNamePtr = // adjust for debug_m_szClassName #if DEBUG #if TARGET_64BIT @@ -461,6 +466,14 @@ public bool HasDefaultConstructor } } + public bool HasClassConstructor + { + get + { + return (Flags2 & enum_flag_HasCctor) != 0; + } + } + public bool IsComObject { get diff --git a/src/coreclr/src/vm/ecalllist.h b/src/coreclr/src/vm/ecalllist.h index 40707612a9899..59620c52322ac 100644 --- a/src/coreclr/src/vm/ecalllist.h +++ b/src/coreclr/src/vm/ecalllist.h @@ -241,6 +241,7 @@ FCFuncStart(gCOMTypeHandleFuncs) FCFuncElement("SatisfiesConstraints", RuntimeTypeHandle::SatisfiesConstraints) FCFuncElement("Allocate", RuntimeTypeHandle::Allocate) //for A.CI QCFuncElement("GetNewobjHelperFnPtr", RuntimeTypeHandle::GetNewobjHelperFnPtr) + QCFuncElement("GetDefaultCtor", RuntimeTypeHandle::GetDefaultCtor) FCFuncElement("CompareCanonicalHandles", RuntimeTypeHandle::CompareCanonicalHandles) FCIntrinsic("GetValueInternal", RuntimeTypeHandle::GetValueInternal, CORINFO_INTRINSIC_RTH_GetValueInternal) FCFuncElement("IsEquivalentTo", RuntimeTypeHandle::IsEquivalentTo) diff --git a/src/coreclr/src/vm/reflectioninvocation.cpp b/src/coreclr/src/vm/reflectioninvocation.cpp index 0503f884fe967..afc50af0aab93 100644 --- a/src/coreclr/src/vm/reflectioninvocation.cpp +++ b/src/coreclr/src/vm/reflectioninvocation.cpp @@ -2276,6 +2276,37 @@ void QCALLTYPE RuntimeTypeHandle::GetNewobjHelperFnPtr( END_QCALL; } +/* + * Given a TypeHandle, returns the MethodDesc* for the default (parameterless) ctor, + * or nullptr if the parameterless ctor doesn't exist. For reference types, the parameterless + * ctor has a managed (object @this) -> void calling convention. For value types, + * the parameterless ctor has a managed (ref T @this) -> void calling convention. + * The returned MethodDesc* is appropriately instantiated over any necessary generic args. + */ +MethodDesc* QCALLTYPE RuntimeTypeHandle::GetDefaultCtor( + QCall::TypeHandle pTypeHandle) +{ + QCALL_CONTRACT; + + MethodDesc* pMethodDesc = NULL; + + BEGIN_QCALL; + + TypeHandle typeHandle = pTypeHandle.AsTypeHandle(); + MethodTable* pMT = typeHandle.AsMethodTable(); + PREFIX_ASSUME(pMT != NULL); + + pMT->EnsureInstanceActive(); + if (pMT->HasDefaultConstructor()) + { + pMethodDesc = pMT->GetDefaultConstructor(); + } + + END_QCALL; + + return pMethodDesc; +} + //************************************************************************************************* //************************************************************************************************* //************************************************************************************************* diff --git a/src/coreclr/src/vm/runtimehandles.h b/src/coreclr/src/vm/runtimehandles.h index 43a88cf4f0448..0a97d81a95411 100644 --- a/src/coreclr/src/vm/runtimehandles.h +++ b/src/coreclr/src/vm/runtimehandles.h @@ -133,6 +133,9 @@ class RuntimeTypeHandle { static void QCALLTYPE GetNewobjHelperFnPtr(QCall::TypeHandle pTypeHandle, PCODE* ppNewobjHelper, MethodTable** ppMT, BOOL fAllowCom); + static + MethodDesc* QCALLTYPE GetDefaultCtor(QCall::TypeHandle pTypeHandle); + static void QCALLTYPE MakeByRef(QCall::TypeHandle pTypeHandle, QCall::ObjectHandleOnStack retType); From c173fda52501cff55520e67935dbf6727b8b8fc7 Mon Sep 17 00:00:00 2001 From: Levi Broderick Date: Wed, 18 Nov 2020 15:57:47 -0800 Subject: [PATCH 09/64] Remove unmanaged Allocate / CreateInstance --- .../Generic/ArraySortHelper.CoreCLR.cs | 4 +- .../src/System/Reflection/CustomAttribute.cs | 2 +- .../RuntimeHelpers.CoreCLR.cs | 12 +- .../InteropServices/Marshal.CoreCLR.cs | 2 +- .../src/System/RuntimeHandles.cs | 30 ++- .../src/System/RuntimeType.CoreCLR.cs | 235 ++++++++++------- src/coreclr/src/vm/ecalllist.h | 3 +- src/coreclr/src/vm/reflectioninvocation.cpp | 238 +++++------------- src/coreclr/src/vm/runtimehandles.h | 4 +- .../src/System/Activator.RuntimeType.cs | 4 +- 10 files changed, 247 insertions(+), 287 deletions(-) diff --git a/src/coreclr/src/System.Private.CoreLib/src/System/Collections/Generic/ArraySortHelper.CoreCLR.cs b/src/coreclr/src/System.Private.CoreLib/src/System/Collections/Generic/ArraySortHelper.CoreCLR.cs index 5e7dd7ddc4534..be5fc2874c56c 100644 --- a/src/coreclr/src/System.Private.CoreLib/src/System/Collections/Generic/ArraySortHelper.CoreCLR.cs +++ b/src/coreclr/src/System.Private.CoreLib/src/System/Collections/Generic/ArraySortHelper.CoreCLR.cs @@ -27,7 +27,7 @@ private static IArraySortHelper CreateArraySortHelper() if (typeof(IComparable).IsAssignableFrom(typeof(T))) { - defaultArraySortHelper = (IArraySortHelper)RuntimeTypeHandle.Allocate(typeof(GenericArraySortHelper).TypeHandle.Instantiate(new Type[] { typeof(T) })); + defaultArraySortHelper = (IArraySortHelper)typeof(GenericArraySortHelper).TypeHandle.Instantiate(new Type[] { typeof(T) }).CreateInstanceDefaultCtor(publicOnly: false, wrapExceptions: false)!; } else { @@ -62,7 +62,7 @@ private static IArraySortHelper CreateArraySortHelper() if (typeof(IComparable).IsAssignableFrom(typeof(TKey))) { - defaultArraySortHelper = (IArraySortHelper)RuntimeTypeHandle.Allocate(typeof(GenericArraySortHelper).TypeHandle.Instantiate(new Type[] { typeof(TKey), typeof(TValue) })); + defaultArraySortHelper = (IArraySortHelper)typeof(GenericArraySortHelper).TypeHandle.Instantiate(new Type[] { typeof(TKey), typeof(TValue) }).CreateInstanceDefaultCtor(publicOnly: false, wrapExceptions: false)!; } else { diff --git a/src/coreclr/src/System.Private.CoreLib/src/System/Reflection/CustomAttribute.cs b/src/coreclr/src/System.Private.CoreLib/src/System/Reflection/CustomAttribute.cs index 41534ab065183..33df3dfc35af8 100644 --- a/src/coreclr/src/System.Private.CoreLib/src/System/Reflection/CustomAttribute.cs +++ b/src/coreclr/src/System.Private.CoreLib/src/System/Reflection/CustomAttribute.cs @@ -1228,7 +1228,7 @@ private static void AddCustomAttributes( } else { - attribute = attributeType.CreateInstanceDefaultCtor(publicOnly: false, skipCheckThis: true, fillCache: true, wrapExceptions: false)!; + attribute = attributeType.CreateInstanceDefaultCtor(publicOnly: false, wrapExceptions: false)!; // It is allowed by the ECMA spec to have an empty signature blob int blobLen = (int)((byte*)blobEnd - (byte*)blobStart); diff --git a/src/coreclr/src/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.CoreCLR.cs b/src/coreclr/src/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.CoreCLR.cs index a6894e38d8f3e..11238c3499e0d 100644 --- a/src/coreclr/src/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.CoreCLR.cs +++ b/src/coreclr/src/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.CoreCLR.cs @@ -144,7 +144,7 @@ private static unsafe object GetUninitializedObjectInternal( Debug.Assert(rt != null); // If type is Nullable, get newobj for boxed T instead. - delegate* newobjHelper = RuntimeTypeHandle.GetNewobjHelperFnPtr(rt, out MethodTable* pMT, allowCom: false); + delegate* newobjHelper = RuntimeTypeHandle.GetNewobjHelperFnPtr(rt, out MethodTable* pMT, unwrapNullable: true); Debug.Assert(newobjHelper != null); Debug.Assert(pMT != null); @@ -380,6 +380,8 @@ internal unsafe struct MethodTable public uint BaseSize; [FieldOffset(8)] public ushort Flags2; + [FieldOffset(0x0c)] + public ushort VirtualCount; [FieldOffset(0x0e)] public ushort InterfaceCount; [FieldOffset(ParentMethodTableOffset)] @@ -528,6 +530,14 @@ public int MultiDimensionalArrayRank return (int)((BaseSize - (uint)(3 * sizeof(IntPtr))) / (uint)(2 * sizeof(int))); } } + + // Gets the slot where the parameterless instance ctor is kept. + // Caller must check 'HasDefaultConstructor' first. + public int GetDefaultConstructorSlot() + { + Debug.Assert(HasDefaultConstructor); + return VirtualCount + (HasClassConstructor ? 1 : 0); + } } // Helper structs used for tail calls via helper. diff --git a/src/coreclr/src/System.Private.CoreLib/src/System/Runtime/InteropServices/Marshal.CoreCLR.cs b/src/coreclr/src/System.Private.CoreLib/src/System/Runtime/InteropServices/Marshal.CoreCLR.cs index 8bf969b604081..cfa743db019e2 100644 --- a/src/coreclr/src/System.Private.CoreLib/src/System/Runtime/InteropServices/Marshal.CoreCLR.cs +++ b/src/coreclr/src/System.Private.CoreLib/src/System/Runtime/InteropServices/Marshal.CoreCLR.cs @@ -200,7 +200,7 @@ private static void PrelinkCore(MethodInfo m) private static object PtrToStructureHelper(IntPtr ptr, Type structureType) { var rt = (RuntimeType)structureType; - object structure = rt.CreateInstanceDefaultCtor(publicOnly: false, skipCheckThis: false, fillCache: false, wrapExceptions: true)!; + object structure = rt.CreateInstanceDefaultCtor(publicOnly: false, wrapExceptions: true)!; PtrToStructureHelper(ptr, structure, allowValueClasses: true); return structure; } diff --git a/src/coreclr/src/System.Private.CoreLib/src/System/RuntimeHandles.cs b/src/coreclr/src/System.Private.CoreLib/src/System/RuntimeHandles.cs index 4ebf752facf93..ee5117cf8fcd1 100644 --- a/src/coreclr/src/System.Private.CoreLib/src/System/RuntimeHandles.cs +++ b/src/coreclr/src/System.Private.CoreLib/src/System/RuntimeHandles.cs @@ -209,6 +209,7 @@ internal static bool HasElementType(RuntimeType type) /// Given a RuntimeType, returns both the address of the JIT's newobj helper for that type and the /// MethodTable* corresponding to that type. If the type is closed over /// some T, then returns the newobj helper and MethodTable* for the 'T'. + /// Return value signature is managed calli (MethodTable* pMT) -> object. /// internal static delegate* GetNewobjHelperFnPtr( // This API doesn't call any constructors, but the type needs to be seen as constructed. @@ -217,7 +218,7 @@ internal static bool HasElementType(RuntimeType type) // constructor are an academic problem. Valuetypes with no constructors are a problem, // but IL Linker currently treats them as always implicitly boxed. [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.NonPublicConstructors)] RuntimeType type, - out MethodTable* pMT, bool allowCom) + out MethodTable* pMT, bool unwrapNullable) { Debug.Assert(type != null); @@ -228,14 +229,37 @@ internal static bool HasElementType(RuntimeType type) new QCallTypeHandle(ref type), &pNewobjHelperTemp, &pMTTemp, - (allowCom) ? Interop.BOOL.TRUE : Interop.BOOL.FALSE); + unwrapNullable ? Interop.BOOL.TRUE : Interop.BOOL.FALSE); pMT = pMTTemp; return pNewobjHelperTemp; } [DllImport(RuntimeHelpers.QCall, CharSet = CharSet.Unicode)] - private static extern void GetNewobjHelperFnPtr(QCallTypeHandle typeHandle, delegate** ppNewobjHelper, MethodTable** ppMT, Interop.BOOL fAllowCom); + private static extern void GetNewobjHelperFnPtr(QCallTypeHandle typeHandle, delegate** ppNewobjHelper, MethodTable** ppMT, Interop.BOOL fUnwrapNullable); + + /// + /// Returns the parameterless ctor for this type as a managed calli-invokable address. + /// For reference types, signature is (object @this) -> void. + /// For value types, signature is (ref T @thisUnboxed) -> void. + /// Returns null if no parameterless ctor is defined. + /// + internal static IntPtr GetDefaultCtorFnPtr( + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.NonPublicConstructors)] RuntimeType type) + { + Debug.Assert(type != null); + + return GetDefaultCtor(new QCallTypeHandle(ref type)); + } + + [DllImport(RuntimeHelpers.QCall, CharSet = CharSet.Unicode)] + private static extern IntPtr GetDefaultCtor(QCallTypeHandle typeHandle); + + /// + /// Instantiates a RCW for a COM object. + /// + [MethodImpl(MethodImplOptions.InternalCall)] + internal static extern object CreateComInstance(RuntimeType type); internal RuntimeType GetRuntimeType() { diff --git a/src/coreclr/src/System.Private.CoreLib/src/System/RuntimeType.CoreCLR.cs b/src/coreclr/src/System.Private.CoreLib/src/System/RuntimeType.CoreCLR.cs index e68e2e4d88aff..8f6f892858bdf 100644 --- a/src/coreclr/src/System.Private.CoreLib/src/System/RuntimeType.CoreCLR.cs +++ b/src/coreclr/src/System.Private.CoreLib/src/System/RuntimeType.CoreCLR.cs @@ -3883,7 +3883,7 @@ private void CreateInstanceCheckThis() if (args.Length == 0 && (bindingAttr & BindingFlags.Public) != 0 && (bindingAttr & BindingFlags.Instance) != 0 && (IsGenericCOMObjectImpl() || IsValueType)) { - instance = CreateInstanceDefaultCtor(publicOnly, skipCheckThis: false, fillCache: true, wrapExceptions); + instance = CreateInstanceDefaultCtor(publicOnly, wrapExceptions); } else { @@ -3953,91 +3953,146 @@ private void CreateInstanceCheckThis() // the cache entry private sealed unsafe class ActivatorCache { - internal volatile delegate* _pfnNewobj; - internal readonly delegate* _pfnCtor; - internal MethodTable* _pMT; - internal readonly bool _ctorIsPublic; + // The managed calli to the newobj routine, plus its first argument. + // First argument is normally a MethodTable* unless we're going through one of our stubs. + private readonly delegate* _pfnNewobj; + private readonly void* _newobjState; - internal ActivatorCache(RuntimeMethodHandleInternal rmh) + // The managed calli to the parameterless ctor, plus a state object. + // State object depends on the stub being called. + private readonly delegate* _pfnCtorStub; + private readonly void* _ctorStubState; + +#if DEBUG + private readonly RuntimeType _originalRT; +#endif + + internal ActivatorCache(RuntimeType rt) { - if (rmh.IsNullHandle()) + Debug.Assert(rt != null); + +#if DEBUG + _originalRT = rt; +#endif + + if (rt.IsCOMObject) { - // Value type with no explicit constructor, which means the "default" ctor - // is implicitly public and no-ops. + // COM objects go through a special activation procedure coordinated + // by the unmanaged runtime. We'll create a stub to that function. + + static object? ComNewobjStub(void* state) + { + RuntimeType rt = GetTypeFromHandleUnsafe((IntPtr)state); + Debug.Assert(rt != null); + Debug.Assert(rt.IsCOMObject); - _ctorIsPublic = true; - _pfnCtor = &GC.KeepAlive; // a convenient no-op fn + return RuntimeTypeHandle.CreateComInstance(rt); + } + _pfnNewobj = &ComNewobjStub; + _newobjState = (void*)rt.TypeHandle.Value; + + _pfnCtorStub = &CtorNoopStub; + _ctorStubState = null; + + CtorIsPublic = true; // COM type ctors are always inherently public } else { - // Reference type with a parameterless ctor. + _pfnNewobj = (delegate*)RuntimeTypeHandle.GetNewobjHelperFnPtr(rt, out MethodTable* pMT, unwrapNullable: false); + _ctorStubState = pMT; - _ctorIsPublic = (RuntimeMethodHandle.GetAttributes(rmh) & MethodAttributes.Public) != 0; - _pfnCtor = (delegate*)RuntimeMethodHandle.GetFunctionPointer(rmh); - } + if (pMT->IsValueType) + { + if (pMT->IsNullable) + { + // Activator.CreateInstance returns null given typeof(Nullable). - Debug.Assert(_pfnCtor != null); - } + static object? ReturnNull(void* state) => null; + _pfnNewobj = &ReturnNull; + _newobjState = null; - private void Initialize(RuntimeType type) - { - Debug.Assert(type != null); + _pfnCtorStub = &CtorNoopStub; + _ctorStubState = null; + + CtorIsPublic = true; // Returning null is always inherently public + } + else if (pMT->HasDefaultConstructor) + { + // ValueType with explicit parameterless ctor typed as (ref T) -> void. + // We'll pass the actual ctor address as the state object, then create + // an unboxing stub so that we can pass the boxed value to it. - // If we reached this point, we already know that the construction information - // can be cached, so all of the runtime reflection checks succeeded. We just - // need to special-case Nullable (to return null). - // - // No synchronization is needed in this method since we have marked the _pfnNewobj - // field as volatile, and if there's multi-threaded access all threads will agree - // on the values to write anyway. - // - // !! IMPORTANT !! - // Don't assign the function pointer return value of GetNewobjHelperFnPtr directly - // to the backing field, as setting the field marks initialization as complete. - // Be sure to perform any last-minute checks *before* setting the backing field. + static void ValueTypeUnboxingStub(object? @this, void* state) + { + ((delegate*)state)(ref @this!.GetRawData()); + } + _pfnCtorStub = &ValueTypeUnboxingStub; + _ctorStubState = (void*)RuntimeTypeHandle.GetDefaultCtorFnPtr(rt); - delegate* pfnNewobj = RuntimeTypeHandle.GetNewobjHelperFnPtr(type, out _pMT, allowCom: true); - if (_pMT->IsNullable) - { - static object? GetNull(MethodTable* pMT) => null; - pfnNewobj = &GetNull; // Activator.CreateInstance(typeof(Nullable)) => null - } + CtorIsPublic = (RuntimeMethodHandle.GetAttributes(RuntimeTypeHandle.GetMethodAt(rt, pMT->GetDefaultConstructorSlot())) & MethodAttributes.Public) != 0; + } + else + { + // ValueType with no explicit parameterless ctor; assume ctor returns default(T) - Debug.Assert(pfnNewobj != null); - _pfnNewobj = pfnNewobj; // setting this field marks the instance as fully initialized - } + _pfnCtorStub = &CtorNoopStub; + _ctorStubState = null; - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void EnsureInitialized(RuntimeType type) - { - if (_pfnNewobj == null) // use this field's value as the indicator of whether initialization is finished - { - Initialize(type); + CtorIsPublic = true; // returning default(T) is always inherently public + } + } + else + { + // Reference type - we can't proceed unless there's a default ctor we can call. + + Debug.Assert(rt.IsClass); + + if (!pMT->HasDefaultConstructor) + { + throw new MissingMethodException(SR.Format(SR.Arg_NoDefCTor, rt)); + } + + // Reference type with explicit parameterless ctor typed as (object) -> void. + // We'll pass the actual ctor address as the state object. + + static void ReferenceTypeStub(object? @this, void* state) + { + ((delegate*)state)(@this!); + } + _pfnCtorStub = &ReferenceTypeStub; + _ctorStubState = (void*)RuntimeTypeHandle.GetDefaultCtorFnPtr(rt); + + CtorIsPublic = (RuntimeMethodHandle.GetAttributes(RuntimeTypeHandle.GetMethodAt(rt, pMT->GetDefaultConstructorSlot())) & MethodAttributes.Public) != 0; + } } + + Debug.Assert(_pfnNewobj != null); + Debug.Assert(_pfnCtorStub != null); } - } - /// - /// The slow path of CreateInstanceDefaultCtor - /// - private object? CreateInstanceDefaultCtorSlow(bool publicOnly, bool wrapExceptions, bool fillCache) - { - RuntimeMethodHandleInternal runtimeCtor = default; - bool canBeCached = false; - bool hasNoDefaultCtor = false; + internal bool CtorIsPublic { get; } - object instance = RuntimeTypeHandle.CreateInstance(this, publicOnly, wrapExceptions, ref canBeCached, ref runtimeCtor, ref hasNoDefaultCtor); - if (hasNoDefaultCtor) - { - throw new MissingMethodException(SR.Format(SR.Arg_NoDefCTor, this)); - } + private static void CtorNoopStub(object? @this, void* state) { } - if (canBeCached && fillCache) + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal object? CreateUninitializedObject(RuntimeType rt) { - GenericCache = new ActivatorCache(runtimeCtor); + // We don't use RuntimeType, but we force the caller to pass it so + // that we can keep it alive on their behalf. Once the object is + // constructed, we no longer need the reference to the type instance, + // as the object itself will keep the type alive. + +#if DEBUG + Debug.Assert(rt == _originalRT, "Caller passed the wrong RuntimeType to this routine."); +#endif + + object? retVal = _pfnNewobj(_newobjState); + GC.KeepAlive(rt); + return retVal; } - return instance; + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal void CallCtorOverUninitializedObject(object? newObj) => _pfnCtorStub(newObj, _ctorStubState); } /// @@ -4045,41 +4100,37 @@ public void EnsureInitialized(RuntimeType type) /// [DebuggerStepThrough] [DebuggerHidden] - internal unsafe object? CreateInstanceDefaultCtor(bool publicOnly, bool skipCheckThis, bool fillCache, bool wrapExceptions) + internal object? CreateInstanceDefaultCtor(bool publicOnly, bool wrapExceptions) { - // Call the cached factory if it exists - if (GenericCache is ActivatorCache cache) - { - cache.EnsureInitialized(this); - - Debug.Assert(cache._pfnNewobj != null); - Debug.Assert(cache._pfnCtor != null); - Debug.Assert(cache._pMT != null); + // Get or create the cached factory. Creating the cache will fail if one + // of our invariant checks fails; e.g., no appropriate ctor found. - if (!cache._ctorIsPublic && publicOnly) - { - throw new MissingMethodException(SR.Format(SR.Arg_NoDefCTor, this)); - } + if (GenericCache is not ActivatorCache cache) + { + cache = new ActivatorCache(this); + GenericCache = cache; + } - object? obj = cache._pfnNewobj(cache._pMT); // allocation outside the try block (allow OOM to bubble up) - GC.KeepAlive(this); // can't allow the type to be collected before the object is created + if (publicOnly & !cache.CtorIsPublic) + { + throw new MissingMethodException(SR.Format(SR.Arg_NoDefCTor, this)); + } - try - { - cache._pfnCtor(obj); - } - catch (Exception e) when (wrapExceptions) - { - throw new TargetInvocationException(e); - } + // Compat: allocation always takes place outside the try block so that OOMs + // bubble up to the caller; the ctor invocation is within the try block so + // that it can be wrapped in TIE if needed. - return obj; + object? obj = cache.CreateUninitializedObject(this); + try + { + cache.CallCtorOverUninitializedObject(obj); + } + catch (Exception e) when (wrapExceptions) + { + throw new TargetInvocationException(e); } - if (!skipCheckThis) - CreateInstanceCheckThis(); - - return CreateInstanceDefaultCtorSlow(publicOnly, wrapExceptions, fillCache); + return obj; } internal void InvalidateCachedNestedType() => Cache.InvalidateCachedNestedType(); diff --git a/src/coreclr/src/vm/ecalllist.h b/src/coreclr/src/vm/ecalllist.h index 59620c52322ac..5f9e5ecd9dda7 100644 --- a/src/coreclr/src/vm/ecalllist.h +++ b/src/coreclr/src/vm/ecalllist.h @@ -189,7 +189,6 @@ FCFuncStart(gSystem_RuntimeType) FCFuncEnd() FCFuncStart(gCOMTypeHandleFuncs) - FCFuncElement("CreateInstance", RuntimeTypeHandle::CreateInstance) FCFuncElement("CreateInstanceForAnotherGenericParameter", RuntimeTypeHandle::CreateInstanceForGenericType) QCFuncElement("GetGCHandle", RuntimeTypeHandle::GetGCHandle) QCFuncElement("FreeGCHandle", RuntimeTypeHandle::FreeGCHandle) @@ -239,9 +238,9 @@ FCFuncStart(gCOMTypeHandleFuncs) FCFuncElement("IsGenericTypeDefinition", RuntimeTypeHandle::IsGenericTypeDefinition) FCFuncElement("ContainsGenericVariables", RuntimeTypeHandle::ContainsGenericVariables) FCFuncElement("SatisfiesConstraints", RuntimeTypeHandle::SatisfiesConstraints) - FCFuncElement("Allocate", RuntimeTypeHandle::Allocate) //for A.CI QCFuncElement("GetNewobjHelperFnPtr", RuntimeTypeHandle::GetNewobjHelperFnPtr) QCFuncElement("GetDefaultCtor", RuntimeTypeHandle::GetDefaultCtor) + FCFuncElement("CreateComInstance", RuntimeTypeHandle::CreateComInstance) FCFuncElement("CompareCanonicalHandles", RuntimeTypeHandle::CompareCanonicalHandles) FCIntrinsic("GetValueInternal", RuntimeTypeHandle::GetValueInternal, CORINFO_INTRINSIC_RTH_GetValueInternal) FCFuncElement("IsEquivalentTo", RuntimeTypeHandle::IsEquivalentTo) diff --git a/src/coreclr/src/vm/reflectioninvocation.cpp b/src/coreclr/src/vm/reflectioninvocation.cpp index afc50af0aab93..86e3df6919571 100644 --- a/src/coreclr/src/vm/reflectioninvocation.cpp +++ b/src/coreclr/src/vm/reflectioninvocation.cpp @@ -338,183 +338,6 @@ FCIMPL7(void, RuntimeFieldHandle::SetValue, ReflectFieldObject *pFieldUNSAFE, Ob } FCIMPLEND -//A.CI work -FCIMPL1(Object*, RuntimeTypeHandle::Allocate, ReflectClassBaseObject* pTypeUNSAFE) -{ - CONTRACTL { - FCALL_CHECK; - PRECONDITION(CheckPointer(pTypeUNSAFE)); - } - CONTRACTL_END - - REFLECTCLASSBASEREF refType = (REFLECTCLASSBASEREF)ObjectToOBJECTREF(pTypeUNSAFE); - TypeHandle type = refType->GetType(); - - // Handle the nullable special case - if (Nullable::IsNullableType(type)) { - return OBJECTREFToObject(Nullable::BoxedNullableNull(type)); - } - - OBJECTREF rv = NULL; - HELPER_METHOD_FRAME_BEGIN_RET_1(refType); - rv = AllocateObject(type.GetMethodTable()); - HELPER_METHOD_FRAME_END(); - return OBJECTREFToObject(rv); - -}//Allocate -FCIMPLEND - -FCIMPL6(Object*, RuntimeTypeHandle::CreateInstance, ReflectClassBaseObject* refThisUNSAFE, - CLR_BOOL publicOnly, - CLR_BOOL wrapExceptions, - CLR_BOOL* pbCanBeCached, - MethodDesc** pConstructor, - CLR_BOOL* pbHasNoDefaultCtor) { - CONTRACTL { - FCALL_CHECK; - PRECONDITION(CheckPointer(refThisUNSAFE)); - PRECONDITION(CheckPointer(pbCanBeCached)); - PRECONDITION(CheckPointer(pConstructor)); - PRECONDITION(CheckPointer(pbHasNoDefaultCtor)); - PRECONDITION(*pbCanBeCached == false); - PRECONDITION(*pConstructor == NULL); - PRECONDITION(*pbHasNoDefaultCtor == false); - } - CONTRACTL_END; - - if (refThisUNSAFE == NULL) - FCThrow(kNullReferenceException); - - MethodDesc* pMeth; - - OBJECTREF rv = NULL; - REFLECTCLASSBASEREF refThis = (REFLECTCLASSBASEREF)ObjectToOBJECTREF(refThisUNSAFE); - TypeHandle thisTH = refThis->GetType(); - - Assembly *pAssem = thisTH.GetAssembly(); - - HELPER_METHOD_FRAME_BEGIN_RET_2(rv, refThis); - - MethodTable* pVMT; - - // Get the type information associated with refThis - if (thisTH.IsNull() || thisTH.IsTypeDesc()) { - *pbHasNoDefaultCtor = true; - goto DoneCreateInstance; - } - - pVMT = thisTH.AsMethodTable(); - - pVMT->EnsureInstanceActive(); - -#ifdef FEATURE_COMINTEROP - // If this is __ComObject then create the underlying COM object. - if (IsComObjectClass(refThis->GetType())) { -#ifdef FEATURE_COMINTEROP_UNMANAGED_ACTIVATION - SyncBlock* pSyncBlock = refThis->GetSyncBlock(); - - void* pClassFactory = (void*)pSyncBlock->GetInteropInfo()->GetComClassFactory(); - if (!pClassFactory) - COMPlusThrow(kInvalidComObjectException, IDS_EE_NO_BACKING_CLASS_FACTORY); - - // create an instance of the Com Object - rv = ((ComClassFactory*)pClassFactory)->CreateInstance(NULL); - -#else // FEATURE_COMINTEROP_UNMANAGED_ACTIVATION - - COMPlusThrow(kInvalidComObjectException, IDS_EE_NO_BACKING_CLASS_FACTORY); - -#endif // FEATURE_COMINTEROP_UNMANAGED_ACTIVATION - } - else -#endif // FEATURE_COMINTEROP - { - // if this is an abstract class then we will fail this - if (pVMT->IsAbstract()) { - if (pVMT->IsInterface()) - COMPlusThrow(kMissingMethodException,W("Acc_CreateInterface")); - else - COMPlusThrow(kMissingMethodException,W("Acc_CreateAbst")); - } - else if (pVMT->ContainsGenericVariables()) { - COMPlusThrow(kArgumentException,W("Acc_CreateGeneric")); - } - - if (pVMT->IsByRefLike()) - COMPlusThrow(kNotSupportedException, W("NotSupported_ByRefLike")); - - if (pVMT->IsSharedByGenericInstantiations()) - COMPlusThrow(kNotSupportedException, W("NotSupported_Type")); - - if (!pVMT->HasDefaultConstructor()) - { - // We didn't find the parameterless constructor, - // if this is a Value class we can simply allocate one and return it - - if (!pVMT->IsValueType()) { - *pbHasNoDefaultCtor = true; - goto DoneCreateInstance; - } - - // Handle the nullable special case - if (Nullable::IsNullableType(thisTH)) { - rv = Nullable::BoxedNullableNull(thisTH); - } - else - rv = pVMT->Allocate(); - - *pbCanBeCached = true; - } - else // !pVMT->HasDefaultConstructor() - { - pMeth = pVMT->GetDefaultConstructor(); - - // Validate the method can be called by this caller - DWORD attr = pMeth->GetAttrs(); - - if (!IsMdPublic(attr) && publicOnly) { - *pbHasNoDefaultCtor = true; - goto DoneCreateInstance; - } - - // We've got the class, lets allocate it and call the constructor - OBJECTREF o; - - o = AllocateObject(pVMT); - GCPROTECT_BEGIN(o); - - MethodDescCallSite ctor(pMeth, &o); - - // Copy "this" pointer - ARG_SLOT arg; - if (pVMT->IsValueType()) - arg = PtrToArgSlot(o->UnBox()); - else - arg = ObjToArgSlot(o); - - // Call the method - TryCallMethod(&ctor, &arg, wrapExceptions); - - rv = o; - GCPROTECT_END(); - - // No need to set these if they cannot be cached. In particular, if the type is a value type with a custom - // parameterless constructor, don't allow caching and have subsequent calls come back here to allocate an object and - // call the constructor. - if (!pVMT->IsValueType()) - { - *pbCanBeCached = true; - *pConstructor = pMeth; - } - } - } -DoneCreateInstance: - ; - HELPER_METHOD_FRAME_END(); - return OBJECTREFToObject(rv); -} -FCIMPLEND - FCIMPL2(Object*, RuntimeTypeHandle::CreateInstanceForGenericType, ReflectClassBaseObject* pTypeUNSAFE, ReflectClassBaseObject* pParameterTypeUNSAFE) { FCALL_CONTRACT; @@ -2180,13 +2003,15 @@ FCIMPLEND * Given a TypeHandle, returns the address of the NEWOBJ helper function that creates * a zero-inited instance of this type. If NEWOBJ is not supported on this TypeHandle, * throws an exception. If TypeHandle is a value type, the NEWOBJ helper will create - * a boxed zero-inited instance of the value type. + * a boxed zero-inited instance of the value type. If fUnwrapNullable is specified, + * then if the input type handle is Nullable we'll return the newobj helper and + * MethodTable* for the underlying T. */ void QCALLTYPE RuntimeTypeHandle::GetNewobjHelperFnPtr( QCall::TypeHandle pTypeHandle, PCODE* ppNewobjHelper, MethodTable** ppMT, - BOOL fAllowCom) + BOOL fUnwrapNullable) { CONTRACTL{ QCALL_CHECK; @@ -2248,14 +2073,15 @@ void QCALLTYPE RuntimeTypeHandle::GetNewobjHelperFnPtr( #ifdef FEATURE_COMINTEROP // Unless caller allows, do not allow allocation of uninitialized RCWs (COM objects). // If the caller allows this, getNewHelperStatic will return an appropriate allocator. - if (!fAllowCom && pMT->IsComObjectType()) + if (pMT->IsComObjectType()) { COMPlusThrow(kNotSupportedException, W("NotSupported_ManagedActivation")); } #endif // FEATURE_COMINTEROP - // If the caller passed Nullable, instead pretend they had passed the 'T' directly. - if (Nullable::IsNullableType(pMT)) + // If the caller passed Nullable and wanted nullables unwrapped, + // instead pretend they had passed the 'T' directly. + if (fUnwrapNullable && Nullable::IsNullableType(pMT)) { pMT = pMT->GetInstantiation()[0].GetMethodTable(); } @@ -2307,6 +2133,54 @@ MethodDesc* QCALLTYPE RuntimeTypeHandle::GetDefaultCtor( return pMethodDesc; } +FCIMPL1(Object*, RuntimeTypeHandle::CreateComInstance, ReflectClassBaseObject* refThisUNSAFE) +{ + CONTRACTL{ + FCALL_CHECK; + PRECONDITION(CheckPointer(refThisUNSAFE)); + } + CONTRACTL_END; + + if (refThisUNSAFE == NULL) + FCThrow(kNullReferenceException); + + BOOL activationSucceeded = FALSE; + OBJECTREF rv = NULL; + REFLECTCLASSBASEREF refThis = (REFLECTCLASSBASEREF)ObjectToOBJECTREF(refThisUNSAFE); + TypeHandle thisTH = refThis->GetType(); + + HELPER_METHOD_FRAME_BEGIN_RET_2(rv, refThis); + + MethodTable* pMT = thisTH.GetMethodTable(); + pMT->EnsureInstanceActive(); + +#ifdef FEATURE_COMINTEROP +#ifdef FEATURE_COMINTEROP_UNMANAGED_ACTIVATION + // If this is __ComObject then create the underlying COM object. + if (IsComObjectClass(thisTH)) + { + SyncBlock* pSyncBlock = refThis->GetSyncBlock(); + + void* pClassFactory = (void*)pSyncBlock->GetInteropInfo()->GetComClassFactory(); + if (pClassFactory) + { + rv = ((ComClassFactory*)pClassFactory)->CreateInstance(NULL); + activationSucceeded = TRUE; + } + } +#endif // FEATURE_COMINTEROP_UNMANAGED_ACTIVATION +#endif // FEATURE_COMINTEROP + + if (!activationSucceeded) + { + COMPlusThrow(kInvalidComObjectException, IDS_EE_NO_BACKING_CLASS_FACTORY); + } + + HELPER_METHOD_FRAME_END(); + return OBJECTREFToObject(rv); +} +FCIMPLEND + //************************************************************************************************* //************************************************************************************************* //************************************************************************************************* diff --git a/src/coreclr/src/vm/runtimehandles.h b/src/coreclr/src/vm/runtimehandles.h index 0a97d81a95411..15d15b545f2c5 100644 --- a/src/coreclr/src/vm/runtimehandles.h +++ b/src/coreclr/src/vm/runtimehandles.h @@ -131,11 +131,13 @@ class RuntimeTypeHandle { CLR_BOOL *pbHasNoDefaultCtor); static - void QCALLTYPE GetNewobjHelperFnPtr(QCall::TypeHandle pTypeHandle, PCODE* ppNewobjHelper, MethodTable** ppMT, BOOL fAllowCom); + void QCALLTYPE GetNewobjHelperFnPtr(QCall::TypeHandle pTypeHandle, PCODE* ppNewobjHelper, MethodTable** ppMT, BOOL fUnwrapNullable); static MethodDesc* QCALLTYPE GetDefaultCtor(QCall::TypeHandle pTypeHandle); + static FCDECL1(Object*, CreateComInstance, ReflectClassBaseObject* refThisUNSAFE); + static void QCALLTYPE MakeByRef(QCall::TypeHandle pTypeHandle, QCall::ObjectHandleOnStack retType); diff --git a/src/libraries/System.Private.CoreLib/src/System/Activator.RuntimeType.cs b/src/libraries/System.Private.CoreLib/src/System/Activator.RuntimeType.cs index 043ec1fd275d5..1008e9e186720 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Activator.RuntimeType.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Activator.RuntimeType.cs @@ -94,7 +94,7 @@ public static partial class Activator throw new ArgumentNullException(nameof(type)); if (type.UnderlyingSystemType is RuntimeType rt) - return rt.CreateInstanceDefaultCtor(publicOnly: !nonPublic, skipCheckThis: false, fillCache: true, wrapExceptions: wrapExceptions); + return rt.CreateInstanceDefaultCtor(publicOnly: !nonPublic, wrapExceptions: wrapExceptions); throw new ArgumentException(SR.Arg_MustBeType, nameof(type)); } @@ -150,7 +150,7 @@ public static partial class Activator [System.Runtime.CompilerServices.Intrinsic] public static T CreateInstance<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)]T>() { - return (T)((RuntimeType)typeof(T)).CreateInstanceDefaultCtor(publicOnly: true, skipCheckThis: true, fillCache: true, wrapExceptions: true)!; + return (T)((RuntimeType)typeof(T)).CreateInstanceDefaultCtor(publicOnly: true, wrapExceptions: true)!; } private static T CreateDefaultInstance() where T : struct => default; From 5c52acd35582be0572d22d42a5418a0cf8ac3fdf Mon Sep 17 00:00:00 2001 From: Levi Broderick Date: Wed, 18 Nov 2020 16:45:55 -0800 Subject: [PATCH 10/64] Fix GetDefaultConstructor signature --- .../RuntimeHelpers.CoreCLR.cs | 10 ---- .../src/System/RuntimeHandles.cs | 8 +-- .../src/System/RuntimeType.CoreCLR.cs | 57 +++++++++---------- 3 files changed, 32 insertions(+), 43 deletions(-) diff --git a/src/coreclr/src/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.CoreCLR.cs b/src/coreclr/src/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.CoreCLR.cs index 11238c3499e0d..63ba09e5dff0b 100644 --- a/src/coreclr/src/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.CoreCLR.cs +++ b/src/coreclr/src/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.CoreCLR.cs @@ -380,8 +380,6 @@ internal unsafe struct MethodTable public uint BaseSize; [FieldOffset(8)] public ushort Flags2; - [FieldOffset(0x0c)] - public ushort VirtualCount; [FieldOffset(0x0e)] public ushort InterfaceCount; [FieldOffset(ParentMethodTableOffset)] @@ -530,14 +528,6 @@ public int MultiDimensionalArrayRank return (int)((BaseSize - (uint)(3 * sizeof(IntPtr))) / (uint)(2 * sizeof(int))); } } - - // Gets the slot where the parameterless instance ctor is kept. - // Caller must check 'HasDefaultConstructor' first. - public int GetDefaultConstructorSlot() - { - Debug.Assert(HasDefaultConstructor); - return VirtualCount + (HasClassConstructor ? 1 : 0); - } } // Helper structs used for tail calls via helper. diff --git a/src/coreclr/src/System.Private.CoreLib/src/System/RuntimeHandles.cs b/src/coreclr/src/System.Private.CoreLib/src/System/RuntimeHandles.cs index ee5117cf8fcd1..02ce9933b99a0 100644 --- a/src/coreclr/src/System.Private.CoreLib/src/System/RuntimeHandles.cs +++ b/src/coreclr/src/System.Private.CoreLib/src/System/RuntimeHandles.cs @@ -239,12 +239,12 @@ internal static bool HasElementType(RuntimeType type) private static extern void GetNewobjHelperFnPtr(QCallTypeHandle typeHandle, delegate** ppNewobjHelper, MethodTable** ppMT, Interop.BOOL fUnwrapNullable); /// - /// Returns the parameterless ctor for this type as a managed calli-invokable address. + /// Returns the MethodDesc* for this type's parameterless instance ctor. /// For reference types, signature is (object @this) -> void. /// For value types, signature is (ref T @thisUnboxed) -> void. - /// Returns null if no parameterless ctor is defined. + /// Returns nullptr if no parameterless ctor is defined. /// - internal static IntPtr GetDefaultCtorFnPtr( + internal static RuntimeMethodHandleInternal GetDefaultConstructor( [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.NonPublicConstructors)] RuntimeType type) { Debug.Assert(type != null); @@ -253,7 +253,7 @@ internal static IntPtr GetDefaultCtorFnPtr( } [DllImport(RuntimeHelpers.QCall, CharSet = CharSet.Unicode)] - private static extern IntPtr GetDefaultCtor(QCallTypeHandle typeHandle); + private static extern RuntimeMethodHandleInternal GetDefaultCtor(QCallTypeHandle typeHandle); /// /// Instantiates a RCW for a COM object. diff --git a/src/coreclr/src/System.Private.CoreLib/src/System/RuntimeType.CoreCLR.cs b/src/coreclr/src/System.Private.CoreLib/src/System/RuntimeType.CoreCLR.cs index 8f6f892858bdf..6f3086c5bf414 100644 --- a/src/coreclr/src/System.Private.CoreLib/src/System/RuntimeType.CoreCLR.cs +++ b/src/coreclr/src/System.Private.CoreLib/src/System/RuntimeType.CoreCLR.cs @@ -3955,13 +3955,13 @@ private sealed unsafe class ActivatorCache { // The managed calli to the newobj routine, plus its first argument. // First argument is normally a MethodTable* unless we're going through one of our stubs. - private readonly delegate* _pfnNewobj; - private readonly void* _newobjState; + private readonly delegate* _pfnNewobj; + private readonly IntPtr _newobjState; // The managed calli to the parameterless ctor, plus a state object. // State object depends on the stub being called. - private readonly delegate* _pfnCtorStub; - private readonly void* _ctorStubState; + private readonly delegate* _pfnCtorStub; + private readonly IntPtr _ctorStubState; #if DEBUG private readonly RuntimeType _originalRT; @@ -3975,31 +3975,33 @@ internal ActivatorCache(RuntimeType rt) _originalRT = rt; #endif + RuntimeMethodHandleInternal defaultCtorRMH = default; + if (rt.IsCOMObject) { // COM objects go through a special activation procedure coordinated // by the unmanaged runtime. We'll create a stub to that function. - static object? ComNewobjStub(void* state) + static object? ComNewobjStub(IntPtr state) { - RuntimeType rt = GetTypeFromHandleUnsafe((IntPtr)state); + RuntimeType rt = GetTypeFromHandleUnsafe(state); // state is a RuntimeType handle Debug.Assert(rt != null); Debug.Assert(rt.IsCOMObject); return RuntimeTypeHandle.CreateComInstance(rt); } _pfnNewobj = &ComNewobjStub; - _newobjState = (void*)rt.TypeHandle.Value; + _newobjState = rt.TypeHandle.Value; _pfnCtorStub = &CtorNoopStub; - _ctorStubState = null; - - CtorIsPublic = true; // COM type ctors are always inherently public + _ctorStubState = IntPtr.Zero; } else { - _pfnNewobj = (delegate*)RuntimeTypeHandle.GetNewobjHelperFnPtr(rt, out MethodTable* pMT, unwrapNullable: false); - _ctorStubState = pMT; + _pfnNewobj = (delegate*)RuntimeTypeHandle.GetNewobjHelperFnPtr(rt, out MethodTable* pMT, unwrapNullable: false); + _ctorStubState = (IntPtr)pMT; + + defaultCtorRMH = RuntimeTypeHandle.GetDefaultConstructor(rt); if (pMT->IsValueType) { @@ -4007,14 +4009,12 @@ internal ActivatorCache(RuntimeType rt) { // Activator.CreateInstance returns null given typeof(Nullable). - static object? ReturnNull(void* state) => null; + static object? ReturnNull(IntPtr state) => null; _pfnNewobj = &ReturnNull; - _newobjState = null; + _newobjState = IntPtr.Zero; _pfnCtorStub = &CtorNoopStub; - _ctorStubState = null; - - CtorIsPublic = true; // Returning null is always inherently public + _ctorStubState = IntPtr.Zero; } else if (pMT->HasDefaultConstructor) { @@ -4022,23 +4022,19 @@ internal ActivatorCache(RuntimeType rt) // We'll pass the actual ctor address as the state object, then create // an unboxing stub so that we can pass the boxed value to it. - static void ValueTypeUnboxingStub(object? @this, void* state) + static void ValueTypeUnboxingStub(object? @this, IntPtr state) { ((delegate*)state)(ref @this!.GetRawData()); } _pfnCtorStub = &ValueTypeUnboxingStub; - _ctorStubState = (void*)RuntimeTypeHandle.GetDefaultCtorFnPtr(rt); - - CtorIsPublic = (RuntimeMethodHandle.GetAttributes(RuntimeTypeHandle.GetMethodAt(rt, pMT->GetDefaultConstructorSlot())) & MethodAttributes.Public) != 0; + _ctorStubState = RuntimeMethodHandle.GetFunctionPointer(defaultCtorRMH); } else { // ValueType with no explicit parameterless ctor; assume ctor returns default(T) _pfnCtorStub = &CtorNoopStub; - _ctorStubState = null; - - CtorIsPublic = true; // returning default(T) is always inherently public + _ctorStubState = IntPtr.Zero; } } else @@ -4055,24 +4051,27 @@ static void ValueTypeUnboxingStub(object? @this, void* state) // Reference type with explicit parameterless ctor typed as (object) -> void. // We'll pass the actual ctor address as the state object. - static void ReferenceTypeStub(object? @this, void* state) + static void ReferenceTypeStub(object? @this, IntPtr state) { ((delegate*)state)(@this!); } _pfnCtorStub = &ReferenceTypeStub; - _ctorStubState = (void*)RuntimeTypeHandle.GetDefaultCtorFnPtr(rt); - - CtorIsPublic = (RuntimeMethodHandle.GetAttributes(RuntimeTypeHandle.GetMethodAt(rt, pMT->GetDefaultConstructorSlot())) & MethodAttributes.Public) != 0; + _ctorStubState = RuntimeMethodHandle.GetFunctionPointer(defaultCtorRMH); } } + if (!defaultCtorRMH.IsNullHandle()) + { + CtorIsPublic = (RuntimeMethodHandle.GetAttributes(defaultCtorRMH) & MethodAttributes.Public) != 0; + } + Debug.Assert(_pfnNewobj != null); Debug.Assert(_pfnCtorStub != null); } internal bool CtorIsPublic { get; } - private static void CtorNoopStub(object? @this, void* state) { } + private static void CtorNoopStub(object? @this, IntPtr state) { } [MethodImpl(MethodImplOptions.AggressiveInlining)] internal object? CreateUninitializedObject(RuntimeType rt) From a1c362ce5a232534139ba94dd8b3d9f29aa83c28 Mon Sep 17 00:00:00 2001 From: Levi Broderick Date: Wed, 18 Nov 2020 17:12:37 -0800 Subject: [PATCH 11/64] Fix AV in allocator --- .../src/System/RuntimeType.CoreCLR.cs | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/src/coreclr/src/System.Private.CoreLib/src/System/RuntimeType.CoreCLR.cs b/src/coreclr/src/System.Private.CoreLib/src/System/RuntimeType.CoreCLR.cs index 6f3086c5bf414..ccce518781de3 100644 --- a/src/coreclr/src/System.Private.CoreLib/src/System/RuntimeType.CoreCLR.cs +++ b/src/coreclr/src/System.Private.CoreLib/src/System/RuntimeType.CoreCLR.cs @@ -3967,7 +3967,7 @@ private sealed unsafe class ActivatorCache private readonly RuntimeType _originalRT; #endif - internal ActivatorCache(RuntimeType rt) + internal ActivatorCache([DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.NonPublicConstructors)] RuntimeType rt) { Debug.Assert(rt != null); @@ -3999,7 +3999,7 @@ internal ActivatorCache(RuntimeType rt) else { _pfnNewobj = (delegate*)RuntimeTypeHandle.GetNewobjHelperFnPtr(rt, out MethodTable* pMT, unwrapNullable: false); - _ctorStubState = (IntPtr)pMT; + _newobjState = (IntPtr)pMT; defaultCtorRMH = RuntimeTypeHandle.GetDefaultConstructor(rt); @@ -4060,7 +4060,11 @@ static void ReferenceTypeStub(object? @this, IntPtr state) } } - if (!defaultCtorRMH.IsNullHandle()) + if (defaultCtorRMH.IsNullHandle()) + { + CtorIsPublic = true; // implicit parameterless ctor is always considered public + } + else { CtorIsPublic = (RuntimeMethodHandle.GetAttributes(defaultCtorRMH) & MethodAttributes.Public) != 0; } @@ -4099,6 +4103,10 @@ private static void CtorNoopStub(object? @this, IntPtr state) { } /// [DebuggerStepThrough] [DebuggerHidden] + [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2082:UnrecognizedReflectionPattern", + Justification = "Implementation detail of Activator that linker intrinsically recognizes")] + [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2085:UnrecognizedReflectionPattern", + Justification = "Implementation detail of Activator that linker intrinsically recognizes")] internal object? CreateInstanceDefaultCtor(bool publicOnly, bool wrapExceptions) { // Get or create the cached factory. Creating the cache will fail if one From 3ce9c412f81f07007ebdfa921e4b87f658d4fd8b Mon Sep 17 00:00:00 2001 From: Levi Broderick Date: Wed, 18 Nov 2020 18:29:01 -0800 Subject: [PATCH 12/64] Fix indentation --- src/coreclr/src/vm/reflectioninvocation.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/coreclr/src/vm/reflectioninvocation.cpp b/src/coreclr/src/vm/reflectioninvocation.cpp index 86e3df6919571..e6ebae5e8e48d 100644 --- a/src/coreclr/src/vm/reflectioninvocation.cpp +++ b/src/coreclr/src/vm/reflectioninvocation.cpp @@ -2008,10 +2008,10 @@ FCIMPLEND * MethodTable* for the underlying T. */ void QCALLTYPE RuntimeTypeHandle::GetNewobjHelperFnPtr( - QCall::TypeHandle pTypeHandle, - PCODE* ppNewobjHelper, - MethodTable** ppMT, - BOOL fUnwrapNullable) + QCall::TypeHandle pTypeHandle, + PCODE* ppNewobjHelper, + MethodTable** ppMT, + BOOL fUnwrapNullable) { CONTRACTL{ QCALL_CHECK; From 904b25dbb4b569a6acc96a44f6e09e80d06d93c4 Mon Sep 17 00:00:00 2001 From: Levi Broderick Date: Wed, 18 Nov 2020 20:14:22 -0800 Subject: [PATCH 13/64] Fix COM instantiation --- src/coreclr/src/vm/reflectioninvocation.cpp | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/src/coreclr/src/vm/reflectioninvocation.cpp b/src/coreclr/src/vm/reflectioninvocation.cpp index e6ebae5e8e48d..7f6e06ec4a438 100644 --- a/src/coreclr/src/vm/reflectioninvocation.cpp +++ b/src/coreclr/src/vm/reflectioninvocation.cpp @@ -2133,6 +2133,10 @@ MethodDesc* QCALLTYPE RuntimeTypeHandle::GetDefaultCtor( return pMethodDesc; } +/* + * Given a TypeHandle which represents a RCW type, create a RCW instance + * and return it to the caller. Fails if the provided type isn't a RCW type. + */ FCIMPL1(Object*, RuntimeTypeHandle::CreateComInstance, ReflectClassBaseObject* refThisUNSAFE) { CONTRACTL{ @@ -2157,16 +2161,10 @@ FCIMPL1(Object*, RuntimeTypeHandle::CreateComInstance, ReflectClassBaseObject* r #ifdef FEATURE_COMINTEROP #ifdef FEATURE_COMINTEROP_UNMANAGED_ACTIVATION // If this is __ComObject then create the underlying COM object. - if (IsComObjectClass(thisTH)) + if (pMT->IsComObjectType()) { - SyncBlock* pSyncBlock = refThis->GetSyncBlock(); - - void* pClassFactory = (void*)pSyncBlock->GetInteropInfo()->GetComClassFactory(); - if (pClassFactory) - { - rv = ((ComClassFactory*)pClassFactory)->CreateInstance(NULL); - activationSucceeded = TRUE; - } + rv = AllocateObject(pMT, true /* fHandleCom */); + activationSucceeded = TRUE; } #endif // FEATURE_COMINTEROP_UNMANAGED_ACTIVATION #endif // FEATURE_COMINTEROP From 1f6434ac0811bdcd50bf44347ba0fd1ccfa75c0d Mon Sep 17 00:00:00 2001 From: Levi Broderick Date: Wed, 18 Nov 2020 20:30:27 -0800 Subject: [PATCH 14/64] Remove COM allocator special-case --- .../src/System/RuntimeHandles.cs | 6 - .../src/System/RuntimeType.CoreCLR.cs | 104 ++++++++---------- src/coreclr/src/vm/ecalllist.h | 1 - src/coreclr/src/vm/reflectioninvocation.cpp | 52 +-------- src/coreclr/src/vm/runtimehandles.h | 2 - 5 files changed, 49 insertions(+), 116 deletions(-) diff --git a/src/coreclr/src/System.Private.CoreLib/src/System/RuntimeHandles.cs b/src/coreclr/src/System.Private.CoreLib/src/System/RuntimeHandles.cs index 02ce9933b99a0..1f7cf1a4ba4ba 100644 --- a/src/coreclr/src/System.Private.CoreLib/src/System/RuntimeHandles.cs +++ b/src/coreclr/src/System.Private.CoreLib/src/System/RuntimeHandles.cs @@ -255,12 +255,6 @@ internal static RuntimeMethodHandleInternal GetDefaultConstructor( [DllImport(RuntimeHelpers.QCall, CharSet = CharSet.Unicode)] private static extern RuntimeMethodHandleInternal GetDefaultCtor(QCallTypeHandle typeHandle); - /// - /// Instantiates a RCW for a COM object. - /// - [MethodImpl(MethodImplOptions.InternalCall)] - internal static extern object CreateComInstance(RuntimeType type); - internal RuntimeType GetRuntimeType() { return m_type; diff --git a/src/coreclr/src/System.Private.CoreLib/src/System/RuntimeType.CoreCLR.cs b/src/coreclr/src/System.Private.CoreLib/src/System/RuntimeType.CoreCLR.cs index ccce518781de3..3f904ed1baaa3 100644 --- a/src/coreclr/src/System.Private.CoreLib/src/System/RuntimeType.CoreCLR.cs +++ b/src/coreclr/src/System.Private.CoreLib/src/System/RuntimeType.CoreCLR.cs @@ -3953,10 +3953,9 @@ private void CreateInstanceCheckThis() // the cache entry private sealed unsafe class ActivatorCache { - // The managed calli to the newobj routine, plus its first argument. - // First argument is normally a MethodTable* unless we're going through one of our stubs. - private readonly delegate* _pfnNewobj; - private readonly IntPtr _newobjState; + // The managed calli to the newobj routine, plus its first argument (MethodTable*). + private readonly delegate* _pfnNewobj; + private readonly MethodTable* _pMT; // The managed calli to the parameterless ctor, plus a state object. // State object depends on the stub being called. @@ -3975,75 +3974,64 @@ internal ActivatorCache([DynamicallyAccessedMembers(DynamicallyAccessedMemberTyp _originalRT = rt; #endif - RuntimeMethodHandleInternal defaultCtorRMH = default; + _pfnCtorStub = &CtorNoopStub; // by default, no ctor is run + _ctorStubState = IntPtr.Zero; - if (rt.IsCOMObject) + _pfnNewobj = RuntimeTypeHandle.GetNewobjHelperFnPtr(rt, out _pMT, unwrapNullable: false); + + RuntimeMethodHandleInternal defaultCtorRMH = RuntimeTypeHandle.GetDefaultConstructor(rt); + + if (_pMT->IsValueType) { - // COM objects go through a special activation procedure coordinated - // by the unmanaged runtime. We'll create a stub to that function. + if (_pMT->IsNullable) + { + // Activator.CreateInstance returns null given typeof(Nullable). + + static object? ReturnNull(MethodTable* pMT) => null; + _pfnNewobj = &ReturnNull; + _pMT = null; - static object? ComNewobjStub(IntPtr state) + _pfnCtorStub = &CtorNoopStub; + _ctorStubState = IntPtr.Zero; + } + else if (_pMT->HasDefaultConstructor) { - RuntimeType rt = GetTypeFromHandleUnsafe(state); // state is a RuntimeType handle - Debug.Assert(rt != null); - Debug.Assert(rt.IsCOMObject); + // ValueType with explicit parameterless ctor typed as (ref T) -> void. + // We'll pass the actual ctor address as the state object, then create + // an unboxing stub so that we can pass the boxed value to it. - return RuntimeTypeHandle.CreateComInstance(rt); + static void ValueTypeUnboxingStub(object? @this, IntPtr state) + { + ((delegate*)state)(ref @this!.GetRawData()); + } + _pfnCtorStub = &ValueTypeUnboxingStub; + _ctorStubState = RuntimeMethodHandle.GetFunctionPointer(defaultCtorRMH); } - _pfnNewobj = &ComNewobjStub; - _newobjState = rt.TypeHandle.Value; + else + { + // ValueType with no explicit parameterless ctor; assume ctor returns default(T) - _pfnCtorStub = &CtorNoopStub; - _ctorStubState = IntPtr.Zero; + _pfnCtorStub = &CtorNoopStub; + _ctorStubState = IntPtr.Zero; + } } else { - _pfnNewobj = (delegate*)RuntimeTypeHandle.GetNewobjHelperFnPtr(rt, out MethodTable* pMT, unwrapNullable: false); - _newobjState = (IntPtr)pMT; + // Reference type - we can't proceed unless there's a default ctor we can call. - defaultCtorRMH = RuntimeTypeHandle.GetDefaultConstructor(rt); + Debug.Assert(rt.IsClass); - if (pMT->IsValueType) + if (_pMT->IsComObject) { - if (pMT->IsNullable) - { - // Activator.CreateInstance returns null given typeof(Nullable). - - static object? ReturnNull(IntPtr state) => null; - _pfnNewobj = &ReturnNull; - _newobjState = IntPtr.Zero; + // COM RCW types go through the runtime's special allocator, but no ctor + // ever gets run over them. We won't overwrite the _pfnCtorStub field. - _pfnCtorStub = &CtorNoopStub; - _ctorStubState = IntPtr.Zero; - } - else if (pMT->HasDefaultConstructor) - { - // ValueType with explicit parameterless ctor typed as (ref T) -> void. - // We'll pass the actual ctor address as the state object, then create - // an unboxing stub so that we can pass the boxed value to it. - - static void ValueTypeUnboxingStub(object? @this, IntPtr state) - { - ((delegate*)state)(ref @this!.GetRawData()); - } - _pfnCtorStub = &ValueTypeUnboxingStub; - _ctorStubState = RuntimeMethodHandle.GetFunctionPointer(defaultCtorRMH); - } - else - { - // ValueType with no explicit parameterless ctor; assume ctor returns default(T) - - _pfnCtorStub = &CtorNoopStub; - _ctorStubState = IntPtr.Zero; - } + Debug.Assert(!rt.IsGenericCOMObjectImpl(), "__ComObject base class should've been blocked."); + defaultCtorRMH = default; // ignore any parameterless ctor } else { - // Reference type - we can't proceed unless there's a default ctor we can call. - - Debug.Assert(rt.IsClass); - - if (!pMT->HasDefaultConstructor) + if (!_pMT->HasDefaultConstructor) { throw new MissingMethodException(SR.Format(SR.Arg_NoDefCTor, rt)); } @@ -4070,7 +4058,7 @@ static void ReferenceTypeStub(object? @this, IntPtr state) } Debug.Assert(_pfnNewobj != null); - Debug.Assert(_pfnCtorStub != null); + Debug.Assert(_pfnCtorStub != null); // we use null singleton pattern if no ctor call is necessary } internal bool CtorIsPublic { get; } @@ -4089,7 +4077,7 @@ private static void CtorNoopStub(object? @this, IntPtr state) { } Debug.Assert(rt == _originalRT, "Caller passed the wrong RuntimeType to this routine."); #endif - object? retVal = _pfnNewobj(_newobjState); + object? retVal = _pfnNewobj(_pMT); GC.KeepAlive(rt); return retVal; } diff --git a/src/coreclr/src/vm/ecalllist.h b/src/coreclr/src/vm/ecalllist.h index 5f9e5ecd9dda7..2be4a6cc47eed 100644 --- a/src/coreclr/src/vm/ecalllist.h +++ b/src/coreclr/src/vm/ecalllist.h @@ -240,7 +240,6 @@ FCFuncStart(gCOMTypeHandleFuncs) FCFuncElement("SatisfiesConstraints", RuntimeTypeHandle::SatisfiesConstraints) QCFuncElement("GetNewobjHelperFnPtr", RuntimeTypeHandle::GetNewobjHelperFnPtr) QCFuncElement("GetDefaultCtor", RuntimeTypeHandle::GetDefaultCtor) - FCFuncElement("CreateComInstance", RuntimeTypeHandle::CreateComInstance) FCFuncElement("CompareCanonicalHandles", RuntimeTypeHandle::CompareCanonicalHandles) FCIntrinsic("GetValueInternal", RuntimeTypeHandle::GetValueInternal, CORINFO_INTRINSIC_RTH_GetValueInternal) FCFuncElement("IsEquivalentTo", RuntimeTypeHandle::IsEquivalentTo) diff --git a/src/coreclr/src/vm/reflectioninvocation.cpp b/src/coreclr/src/vm/reflectioninvocation.cpp index 7f6e06ec4a438..c8681497c1e9e 100644 --- a/src/coreclr/src/vm/reflectioninvocation.cpp +++ b/src/coreclr/src/vm/reflectioninvocation.cpp @@ -2071,9 +2071,9 @@ void QCALLTYPE RuntimeTypeHandle::GetNewobjHelperFnPtr( // transparent proxy or the jit will get confused. #ifdef FEATURE_COMINTEROP - // Unless caller allows, do not allow allocation of uninitialized RCWs (COM objects). - // If the caller allows this, getNewHelperStatic will return an appropriate allocator. - if (pMT->IsComObjectType()) + // Never allow instantiation of the __ComObject base type, only RCWs. + // In a COM-enabled runitme, getNewHelperStatic will return an RCW-aware allocator. + if (IsComObjectClass(typeHandle)) { COMPlusThrow(kNotSupportedException, W("NotSupported_ManagedActivation")); } @@ -2133,52 +2133,6 @@ MethodDesc* QCALLTYPE RuntimeTypeHandle::GetDefaultCtor( return pMethodDesc; } -/* - * Given a TypeHandle which represents a RCW type, create a RCW instance - * and return it to the caller. Fails if the provided type isn't a RCW type. - */ -FCIMPL1(Object*, RuntimeTypeHandle::CreateComInstance, ReflectClassBaseObject* refThisUNSAFE) -{ - CONTRACTL{ - FCALL_CHECK; - PRECONDITION(CheckPointer(refThisUNSAFE)); - } - CONTRACTL_END; - - if (refThisUNSAFE == NULL) - FCThrow(kNullReferenceException); - - BOOL activationSucceeded = FALSE; - OBJECTREF rv = NULL; - REFLECTCLASSBASEREF refThis = (REFLECTCLASSBASEREF)ObjectToOBJECTREF(refThisUNSAFE); - TypeHandle thisTH = refThis->GetType(); - - HELPER_METHOD_FRAME_BEGIN_RET_2(rv, refThis); - - MethodTable* pMT = thisTH.GetMethodTable(); - pMT->EnsureInstanceActive(); - -#ifdef FEATURE_COMINTEROP -#ifdef FEATURE_COMINTEROP_UNMANAGED_ACTIVATION - // If this is __ComObject then create the underlying COM object. - if (pMT->IsComObjectType()) - { - rv = AllocateObject(pMT, true /* fHandleCom */); - activationSucceeded = TRUE; - } -#endif // FEATURE_COMINTEROP_UNMANAGED_ACTIVATION -#endif // FEATURE_COMINTEROP - - if (!activationSucceeded) - { - COMPlusThrow(kInvalidComObjectException, IDS_EE_NO_BACKING_CLASS_FACTORY); - } - - HELPER_METHOD_FRAME_END(); - return OBJECTREFToObject(rv); -} -FCIMPLEND - //************************************************************************************************* //************************************************************************************************* //************************************************************************************************* diff --git a/src/coreclr/src/vm/runtimehandles.h b/src/coreclr/src/vm/runtimehandles.h index 15d15b545f2c5..a175107a3b4d2 100644 --- a/src/coreclr/src/vm/runtimehandles.h +++ b/src/coreclr/src/vm/runtimehandles.h @@ -136,8 +136,6 @@ class RuntimeTypeHandle { static MethodDesc* QCALLTYPE GetDefaultCtor(QCall::TypeHandle pTypeHandle); - static FCDECL1(Object*, CreateComInstance, ReflectClassBaseObject* refThisUNSAFE); - static void QCALLTYPE MakeByRef(QCall::TypeHandle pTypeHandle, QCall::ObjectHandleOnStack retType); From c1ecb070a51b9c0abe6ad511b0b8f4c72ebcb6ec Mon Sep 17 00:00:00 2001 From: Levi Broderick Date: Wed, 18 Nov 2020 21:14:18 -0800 Subject: [PATCH 15/64] Fix mono build break; quick code cleanup --- .../CompilerServices/RuntimeHelpers.CoreCLR.cs | 13 ------------- .../src/System/RuntimeType.CoreCLR.cs | 15 +++++++++++++++ .../src/System/Activator.RuntimeType.cs | 4 ++-- 3 files changed, 17 insertions(+), 15 deletions(-) diff --git a/src/coreclr/src/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.CoreCLR.cs b/src/coreclr/src/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.CoreCLR.cs index 63ba09e5dff0b..aafbf652d3753 100644 --- a/src/coreclr/src/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.CoreCLR.cs +++ b/src/coreclr/src/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.CoreCLR.cs @@ -378,8 +378,6 @@ internal unsafe struct MethodTable private uint Flags; [FieldOffset(4)] public uint BaseSize; - [FieldOffset(8)] - public ushort Flags2; [FieldOffset(0x0e)] public ushort InterfaceCount; [FieldOffset(ParentMethodTableOffset)] @@ -405,9 +403,6 @@ internal unsafe struct MethodTable | 0x00400000 // enum_flag_ICastable; | 0x00200000;// enum_flag_IDynamicInterfaceCastable; - // WFLAGS2_ENUM - private const ushort enum_flag_HasCctor = 0x0400; - private const int DebugClassNamePtr = // adjust for debug_m_szClassName #if DEBUG #if TARGET_64BIT @@ -466,14 +461,6 @@ public bool HasDefaultConstructor } } - public bool HasClassConstructor - { - get - { - return (Flags2 & enum_flag_HasCctor) != 0; - } - } - public bool IsComObject { get diff --git a/src/coreclr/src/System.Private.CoreLib/src/System/RuntimeType.CoreCLR.cs b/src/coreclr/src/System.Private.CoreLib/src/System/RuntimeType.CoreCLR.cs index 3f904ed1baaa3..f61090f8f553e 100644 --- a/src/coreclr/src/System.Private.CoreLib/src/System/RuntimeType.CoreCLR.cs +++ b/src/coreclr/src/System.Private.CoreLib/src/System/RuntimeType.CoreCLR.cs @@ -4086,6 +4086,21 @@ private static void CtorNoopStub(object? @this, IntPtr state) { } internal void CallCtorOverUninitializedObject(object? newObj) => _pfnCtorStub(newObj, _ctorStubState); } + // This method mimics the overload present in the mono codebase. It allows shared source callers + // to target this overload and work across both runtimes. CoreCLR ignores the 'skipCheckThis' and + // 'fillCache' parameters. + [DebuggerStepThrough] + [DebuggerHidden] + [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2082:UnrecognizedReflectionPattern", + Justification = "Implementation detail of Activator that linker intrinsically recognizes")] + [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2085:UnrecognizedReflectionPattern", + Justification = "Implementation detail of Activator that linker intrinsically recognizes")] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal object? CreateInstanceDefaultCtor(bool publicOnly, bool skipCheckThis, bool fillCache, bool wrapExceptions) + { + return CreateInstanceDefaultCtor(publicOnly, wrapExceptions); + } + /// /// Helper to invoke the default (parameterless) constructor. /// diff --git a/src/libraries/System.Private.CoreLib/src/System/Activator.RuntimeType.cs b/src/libraries/System.Private.CoreLib/src/System/Activator.RuntimeType.cs index 1008e9e186720..043ec1fd275d5 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Activator.RuntimeType.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Activator.RuntimeType.cs @@ -94,7 +94,7 @@ public static partial class Activator throw new ArgumentNullException(nameof(type)); if (type.UnderlyingSystemType is RuntimeType rt) - return rt.CreateInstanceDefaultCtor(publicOnly: !nonPublic, wrapExceptions: wrapExceptions); + return rt.CreateInstanceDefaultCtor(publicOnly: !nonPublic, skipCheckThis: false, fillCache: true, wrapExceptions: wrapExceptions); throw new ArgumentException(SR.Arg_MustBeType, nameof(type)); } @@ -150,7 +150,7 @@ public static partial class Activator [System.Runtime.CompilerServices.Intrinsic] public static T CreateInstance<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)]T>() { - return (T)((RuntimeType)typeof(T)).CreateInstanceDefaultCtor(publicOnly: true, wrapExceptions: true)!; + return (T)((RuntimeType)typeof(T)).CreateInstanceDefaultCtor(publicOnly: true, skipCheckThis: true, fillCache: true, wrapExceptions: true)!; } private static T CreateDefaultInstance() where T : struct => default; From a071262fa2c5abed750cbe610458230e236e0f78 Mon Sep 17 00:00:00 2001 From: Levi Broderick Date: Thu, 19 Nov 2020 13:19:22 -0800 Subject: [PATCH 16/64] Rename GetNewobjHelperFnPtr -> GetAllocatorFtn --- .../src/System/RuntimeHandles.cs | 13 ++++++------ src/coreclr/src/vm/ecalllist.h | 2 +- src/coreclr/src/vm/reflectioninvocation.cpp | 20 ++++++++++++------- src/coreclr/src/vm/runtimehandles.h | 2 +- 4 files changed, 21 insertions(+), 16 deletions(-) diff --git a/src/coreclr/src/System.Private.CoreLib/src/System/RuntimeHandles.cs b/src/coreclr/src/System.Private.CoreLib/src/System/RuntimeHandles.cs index 1f7cf1a4ba4ba..ac0c89a9507e2 100644 --- a/src/coreclr/src/System.Private.CoreLib/src/System/RuntimeHandles.cs +++ b/src/coreclr/src/System.Private.CoreLib/src/System/RuntimeHandles.cs @@ -207,9 +207,8 @@ internal static bool HasElementType(RuntimeType type) /// /// Given a RuntimeType, returns both the address of the JIT's newobj helper for that type and the - /// MethodTable* corresponding to that type. If the type is closed over - /// some T, then returns the newobj helper and MethodTable* for the 'T'. - /// Return value signature is managed calli (MethodTable* pMT) -> object. + /// MethodTable* corresponding to that type. Return value signature is + /// managed calli (MethodTable* pMT) -> object. /// internal static delegate* GetNewobjHelperFnPtr( // This API doesn't call any constructors, but the type needs to be seen as constructed. @@ -218,25 +217,25 @@ internal static bool HasElementType(RuntimeType type) // constructor are an academic problem. Valuetypes with no constructors are a problem, // but IL Linker currently treats them as always implicitly boxed. [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.NonPublicConstructors)] RuntimeType type, - out MethodTable* pMT, bool unwrapNullable) + out MethodTable* pMT, bool forGetUninitializedInstance) { Debug.Assert(type != null); delegate* pNewobjHelperTemp = null; MethodTable* pMTTemp = null; - GetNewobjHelperFnPtr( + GetAllocatorFtn( new QCallTypeHandle(ref type), &pNewobjHelperTemp, &pMTTemp, - unwrapNullable ? Interop.BOOL.TRUE : Interop.BOOL.FALSE); + forGetUninitializedInstance ? Interop.BOOL.TRUE : Interop.BOOL.FALSE); pMT = pMTTemp; return pNewobjHelperTemp; } [DllImport(RuntimeHelpers.QCall, CharSet = CharSet.Unicode)] - private static extern void GetNewobjHelperFnPtr(QCallTypeHandle typeHandle, delegate** ppNewobjHelper, MethodTable** ppMT, Interop.BOOL fUnwrapNullable); + private static extern void GetAllocatorFtn(QCallTypeHandle typeHandle, delegate** ppNewobjHelper, MethodTable** ppMT, Interop.BOOL fGetUninitializedInstance); /// /// Returns the MethodDesc* for this type's parameterless instance ctor. diff --git a/src/coreclr/src/vm/ecalllist.h b/src/coreclr/src/vm/ecalllist.h index 2be4a6cc47eed..17ea5afe1e07b 100644 --- a/src/coreclr/src/vm/ecalllist.h +++ b/src/coreclr/src/vm/ecalllist.h @@ -238,7 +238,7 @@ FCFuncStart(gCOMTypeHandleFuncs) FCFuncElement("IsGenericTypeDefinition", RuntimeTypeHandle::IsGenericTypeDefinition) FCFuncElement("ContainsGenericVariables", RuntimeTypeHandle::ContainsGenericVariables) FCFuncElement("SatisfiesConstraints", RuntimeTypeHandle::SatisfiesConstraints) - QCFuncElement("GetNewobjHelperFnPtr", RuntimeTypeHandle::GetNewobjHelperFnPtr) + QCFuncElement("GetAllocatorFtn", RuntimeTypeHandle::GetAllocatorFtn) QCFuncElement("GetDefaultCtor", RuntimeTypeHandle::GetDefaultCtor) FCFuncElement("CompareCanonicalHandles", RuntimeTypeHandle::CompareCanonicalHandles) FCIntrinsic("GetValueInternal", RuntimeTypeHandle::GetValueInternal, CORINFO_INTRINSIC_RTH_GetValueInternal) diff --git a/src/coreclr/src/vm/reflectioninvocation.cpp b/src/coreclr/src/vm/reflectioninvocation.cpp index c8681497c1e9e..b9c16537e3695 100644 --- a/src/coreclr/src/vm/reflectioninvocation.cpp +++ b/src/coreclr/src/vm/reflectioninvocation.cpp @@ -2007,11 +2007,11 @@ FCIMPLEND * then if the input type handle is Nullable we'll return the newobj helper and * MethodTable* for the underlying T. */ -void QCALLTYPE RuntimeTypeHandle::GetNewobjHelperFnPtr( +void QCALLTYPE RuntimeTypeHandle::GetAllocatorFtn( QCall::TypeHandle pTypeHandle, PCODE* ppNewobjHelper, MethodTable** ppMT, - BOOL fUnwrapNullable) + BOOL fGetUninitializedInstance) { CONTRACTL{ QCALL_CHECK; @@ -2026,8 +2026,14 @@ void QCALLTYPE RuntimeTypeHandle::GetNewobjHelperFnPtr( TypeHandle typeHandle = pTypeHandle.AsTypeHandle(); - // Don't allow void, arrays, pointers, byrefs, or function pointers. - if (typeHandle.IsTypeDesc() || typeHandle.IsArray() || typeHandle.GetSignatureCorElementType() == ELEMENT_TYPE_VOID) + // Don't allow void + if (typeHandle.GetSignatureCorElementType() == ELEMENT_TYPE_VOID) + { + COMPlusThrow(kArgumentException, W("Argument_InvalidValue")); + } + + // Don't allow arrays, pointers, byrefs, or function pointers + if (typeHandle.IsTypeDesc() || typeHandle.IsArray()) { COMPlusThrow(kArgumentException, W("Argument_InvalidValue")); } @@ -2079,9 +2085,9 @@ void QCALLTYPE RuntimeTypeHandle::GetNewobjHelperFnPtr( } #endif // FEATURE_COMINTEROP - // If the caller passed Nullable and wanted nullables unwrapped, - // instead pretend they had passed the 'T' directly. - if (fUnwrapNullable && Nullable::IsNullableType(pMT)) + // If the caller is GetUninitializedInstance, they'll want a boxed T instead of a boxed Nullable. + // Other callers will get the MethodTable* corresponding to Nullable. + if (fGetUninitializedInstance && Nullable::IsNullableType(pMT)) { pMT = pMT->GetInstantiation()[0].GetMethodTable(); } diff --git a/src/coreclr/src/vm/runtimehandles.h b/src/coreclr/src/vm/runtimehandles.h index a175107a3b4d2..ef9f8a7a632aa 100644 --- a/src/coreclr/src/vm/runtimehandles.h +++ b/src/coreclr/src/vm/runtimehandles.h @@ -131,7 +131,7 @@ class RuntimeTypeHandle { CLR_BOOL *pbHasNoDefaultCtor); static - void QCALLTYPE GetNewobjHelperFnPtr(QCall::TypeHandle pTypeHandle, PCODE* ppNewobjHelper, MethodTable** ppMT, BOOL fUnwrapNullable); + void QCALLTYPE GetAllocatorFtn(QCall::TypeHandle pTypeHandle, PCODE* ppNewobjHelper, MethodTable** ppMT, BOOL fGetUninitializedInstance); static MethodDesc* QCALLTYPE GetDefaultCtor(QCall::TypeHandle pTypeHandle); From dc417fdfd6cc8b6e8f3206a525afc3c839b4ae93 Mon Sep 17 00:00:00 2001 From: Levi Broderick Date: Thu, 19 Nov 2020 14:04:50 -0800 Subject: [PATCH 17/64] Cleanup in the allocator routines --- .../RuntimeHelpers.CoreCLR.cs | 9 ++- .../src/System/RuntimeHandles.cs | 12 +-- .../src/System/RuntimeType.CoreCLR.cs | 78 ++++++++----------- src/coreclr/src/vm/reflectioninvocation.cpp | 11 +-- src/coreclr/src/vm/runtimehandles.h | 2 +- 5 files changed, 51 insertions(+), 61 deletions(-) diff --git a/src/coreclr/src/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.CoreCLR.cs b/src/coreclr/src/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.CoreCLR.cs index aafbf652d3753..0526bd8028da4 100644 --- a/src/coreclr/src/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.CoreCLR.cs +++ b/src/coreclr/src/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.CoreCLR.cs @@ -143,12 +143,13 @@ private static unsafe object GetUninitializedObjectInternal( RuntimeType rt = (RuntimeType)type; Debug.Assert(rt != null); - // If type is Nullable, get newobj for boxed T instead. - delegate* newobjHelper = RuntimeTypeHandle.GetNewobjHelperFnPtr(rt, out MethodTable* pMT, unwrapNullable: true); - Debug.Assert(newobjHelper != null); + // If type is Nullable, returns the allocator and MethodTable* for the underlying T. + delegate* pfnAllocator = RuntimeTypeHandle.GetAllocatorFtn(rt, out MethodTable* pMT, forGetUninitializedObject: true); + Debug.Assert(pfnAllocator != null); Debug.Assert(pMT != null); + Debug.Assert(!pMT->IsNullable, "Should've unwrapped any Nullable input."); - object retVal = newobjHelper(pMT); + object retVal = pfnAllocator(pMT); GC.KeepAlive(rt); // don't allow the type to be collected before the object is instantiated return retVal; diff --git a/src/coreclr/src/System.Private.CoreLib/src/System/RuntimeHandles.cs b/src/coreclr/src/System.Private.CoreLib/src/System/RuntimeHandles.cs index ac0c89a9507e2..8c490b3ad9eff 100644 --- a/src/coreclr/src/System.Private.CoreLib/src/System/RuntimeHandles.cs +++ b/src/coreclr/src/System.Private.CoreLib/src/System/RuntimeHandles.cs @@ -206,18 +206,18 @@ internal static bool HasElementType(RuntimeType type) internal static extern object CreateInstanceForAnotherGenericParameter(RuntimeType type, RuntimeType genericParameter); /// - /// Given a RuntimeType, returns both the address of the JIT's newobj helper for that type and the - /// MethodTable* corresponding to that type. Return value signature is + /// Given a RuntimeType, returns both the address of the JIT's newobj allocator helper for + /// that type and the MethodTable* corresponding to that type. Return value signature is /// managed calli (MethodTable* pMT) -> object. /// - internal static delegate* GetNewobjHelperFnPtr( + internal static delegate* GetAllocatorFtn( // This API doesn't call any constructors, but the type needs to be seen as constructed. // A type is seen as constructed if a constructor is kept. // This obviously won't cover a type with no constructor. Reference types with no // constructor are an academic problem. Valuetypes with no constructors are a problem, // but IL Linker currently treats them as always implicitly boxed. [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.NonPublicConstructors)] RuntimeType type, - out MethodTable* pMT, bool forGetUninitializedInstance) + out MethodTable* pMT, bool forGetUninitializedObject) { Debug.Assert(type != null); @@ -228,14 +228,14 @@ internal static bool HasElementType(RuntimeType type) new QCallTypeHandle(ref type), &pNewobjHelperTemp, &pMTTemp, - forGetUninitializedInstance ? Interop.BOOL.TRUE : Interop.BOOL.FALSE); + forGetUninitializedObject ? Interop.BOOL.TRUE : Interop.BOOL.FALSE); pMT = pMTTemp; return pNewobjHelperTemp; } [DllImport(RuntimeHelpers.QCall, CharSet = CharSet.Unicode)] - private static extern void GetAllocatorFtn(QCallTypeHandle typeHandle, delegate** ppNewobjHelper, MethodTable** ppMT, Interop.BOOL fGetUninitializedInstance); + private static extern void GetAllocatorFtn(QCallTypeHandle typeHandle, delegate** ppNewobjHelper, MethodTable** ppMT, Interop.BOOL fGetUninitializedObject); /// /// Returns the MethodDesc* for this type's parameterless instance ctor. diff --git a/src/coreclr/src/System.Private.CoreLib/src/System/RuntimeType.CoreCLR.cs b/src/coreclr/src/System.Private.CoreLib/src/System/RuntimeType.CoreCLR.cs index f61090f8f553e..7080e6a4299f5 100644 --- a/src/coreclr/src/System.Private.CoreLib/src/System/RuntimeType.CoreCLR.cs +++ b/src/coreclr/src/System.Private.CoreLib/src/System/RuntimeType.CoreCLR.cs @@ -3953,14 +3953,13 @@ private void CreateInstanceCheckThis() // the cache entry private sealed unsafe class ActivatorCache { - // The managed calli to the newobj routine, plus its first argument (MethodTable*). - private readonly delegate* _pfnNewobj; + // The managed calli to the newobj allocator, plus its first argument (MethodTable*). + private readonly delegate* _pfnAllocator; private readonly MethodTable* _pMT; - // The managed calli to the parameterless ctor, plus a state object. - // State object depends on the stub being called. - private readonly delegate* _pfnCtorStub; - private readonly IntPtr _ctorStubState; + // The managed calli to the parameterless ctor, taking "this" (as object) as its first argument. + // For value type ctors, we'll point to a special unboxing stub. + private readonly delegate* _pfnCtor; #if DEBUG private readonly RuntimeType _originalRT; @@ -3974,12 +3973,12 @@ internal ActivatorCache([DynamicallyAccessedMembers(DynamicallyAccessedMemberTyp _originalRT = rt; #endif - _pfnCtorStub = &CtorNoopStub; // by default, no ctor is run - _ctorStubState = IntPtr.Zero; + _pfnAllocator = RuntimeTypeHandle.GetAllocatorFtn(rt, out _pMT, forGetUninitializedObject: false); - _pfnNewobj = RuntimeTypeHandle.GetNewobjHelperFnPtr(rt, out _pMT, unwrapNullable: false); + static void CtorNoopStub(object? uninitializedObject) { } + _pfnCtor = &CtorNoopStub; - RuntimeMethodHandleInternal defaultCtorRMH = RuntimeTypeHandle.GetDefaultConstructor(rt); + RuntimeMethodHandleInternal defaultCtorRMH = RuntimeTypeHandle.GetDefaultConstructor(rt); // could be null if (_pMT->IsValueType) { @@ -3988,31 +3987,30 @@ internal ActivatorCache([DynamicallyAccessedMembers(DynamicallyAccessedMemberTyp // Activator.CreateInstance returns null given typeof(Nullable). static object? ReturnNull(MethodTable* pMT) => null; - _pfnNewobj = &ReturnNull; - _pMT = null; - - _pfnCtorStub = &CtorNoopStub; - _ctorStubState = IntPtr.Zero; + _pfnAllocator = &ReturnNull; } else if (_pMT->HasDefaultConstructor) { // ValueType with explicit parameterless ctor typed as (ref T) -> void. - // We'll pass the actual ctor address as the state object, then create - // an unboxing stub so that we can pass the boxed value to it. + // We'll point the ctor at our unboxing stub. It's not terribly efficient, + // but value types almost never have explicit parameterless ctors, so + // this shouldn't be a problem in practice. - static void ValueTypeUnboxingStub(object? @this, IntPtr state) + static void CtorUnboxValueTypeStub(object? uninitializedObject) { - ((delegate*)state)(ref @this!.GetRawData()); + Debug.Assert(uninitializedObject != null); + Debug.Assert(RuntimeHelpers.GetMethodTable(uninitializedObject)->IsValueType); + + RuntimeType rt = (RuntimeType)uninitializedObject.GetType(); + RuntimeMethodHandleInternal rmh = RuntimeTypeHandle.GetDefaultConstructor(rt); + IntPtr pfnCtor = RuntimeMethodHandle.GetFunctionPointer(rmh); + ((delegate*)pfnCtor)(ref uninitializedObject!.GetRawData()); } - _pfnCtorStub = &ValueTypeUnboxingStub; - _ctorStubState = RuntimeMethodHandle.GetFunctionPointer(defaultCtorRMH); + _pfnCtor = &CtorUnboxValueTypeStub; } else { // ValueType with no explicit parameterless ctor; assume ctor returns default(T) - - _pfnCtorStub = &CtorNoopStub; - _ctorStubState = IntPtr.Zero; } } else @@ -4029,22 +4027,13 @@ static void ValueTypeUnboxingStub(object? @this, IntPtr state) Debug.Assert(!rt.IsGenericCOMObjectImpl(), "__ComObject base class should've been blocked."); defaultCtorRMH = default; // ignore any parameterless ctor } + else if (!_pMT->HasDefaultConstructor) + { + throw new MissingMethodException(SR.Format(SR.Arg_NoDefCTor, rt)); + } else { - if (!_pMT->HasDefaultConstructor) - { - throw new MissingMethodException(SR.Format(SR.Arg_NoDefCTor, rt)); - } - - // Reference type with explicit parameterless ctor typed as (object) -> void. - // We'll pass the actual ctor address as the state object. - - static void ReferenceTypeStub(object? @this, IntPtr state) - { - ((delegate*)state)(@this!); - } - _pfnCtorStub = &ReferenceTypeStub; - _ctorStubState = RuntimeMethodHandle.GetFunctionPointer(defaultCtorRMH); + // Reference type with explicit parameterless ctor } } @@ -4057,14 +4046,13 @@ static void ReferenceTypeStub(object? @this, IntPtr state) CtorIsPublic = (RuntimeMethodHandle.GetAttributes(defaultCtorRMH) & MethodAttributes.Public) != 0; } - Debug.Assert(_pfnNewobj != null); - Debug.Assert(_pfnCtorStub != null); // we use null singleton pattern if no ctor call is necessary + Debug.Assert(_pfnAllocator != null); + Debug.Assert(_pMT != null); + Debug.Assert(_pfnCtor != null); // we use null singleton pattern if no ctor call is necessary } internal bool CtorIsPublic { get; } - private static void CtorNoopStub(object? @this, IntPtr state) { } - [MethodImpl(MethodImplOptions.AggressiveInlining)] internal object? CreateUninitializedObject(RuntimeType rt) { @@ -4077,13 +4065,13 @@ private static void CtorNoopStub(object? @this, IntPtr state) { } Debug.Assert(rt == _originalRT, "Caller passed the wrong RuntimeType to this routine."); #endif - object? retVal = _pfnNewobj(_pMT); + object? retVal = _pfnAllocator(_pMT); GC.KeepAlive(rt); return retVal; } [MethodImpl(MethodImplOptions.AggressiveInlining)] - internal void CallCtorOverUninitializedObject(object? newObj) => _pfnCtorStub(newObj, _ctorStubState); + internal void CallConstructor(object? uninitializedObject) => _pfnCtor(uninitializedObject); } // This method mimics the overload present in the mono codebase. It allows shared source callers @@ -4133,7 +4121,7 @@ private static void CtorNoopStub(object? @this, IntPtr state) { } object? obj = cache.CreateUninitializedObject(this); try { - cache.CallCtorOverUninitializedObject(obj); + cache.CallConstructor(obj); } catch (Exception e) when (wrapExceptions) { diff --git a/src/coreclr/src/vm/reflectioninvocation.cpp b/src/coreclr/src/vm/reflectioninvocation.cpp index b9c16537e3695..18781566291b8 100644 --- a/src/coreclr/src/vm/reflectioninvocation.cpp +++ b/src/coreclr/src/vm/reflectioninvocation.cpp @@ -2003,15 +2003,16 @@ FCIMPLEND * Given a TypeHandle, returns the address of the NEWOBJ helper function that creates * a zero-inited instance of this type. If NEWOBJ is not supported on this TypeHandle, * throws an exception. If TypeHandle is a value type, the NEWOBJ helper will create - * a boxed zero-inited instance of the value type. If fUnwrapNullable is specified, - * then if the input type handle is Nullable we'll return the newobj helper and - * MethodTable* for the underlying T. + * a boxed zero-inited instance of the value type. The "fGetUninitializedObject" + * parameter dictates whether the caller is RuntimeHelpers.GetUninitializedObject or + * Activator.CreateInstance, which have different behavior w.r.t. what exceptions are + * thrown on failure and how nullables are handled. */ void QCALLTYPE RuntimeTypeHandle::GetAllocatorFtn( QCall::TypeHandle pTypeHandle, PCODE* ppNewobjHelper, MethodTable** ppMT, - BOOL fGetUninitializedInstance) + BOOL fGetUninitializedObject) { CONTRACTL{ QCALL_CHECK; @@ -2087,7 +2088,7 @@ void QCALLTYPE RuntimeTypeHandle::GetAllocatorFtn( // If the caller is GetUninitializedInstance, they'll want a boxed T instead of a boxed Nullable. // Other callers will get the MethodTable* corresponding to Nullable. - if (fGetUninitializedInstance && Nullable::IsNullableType(pMT)) + if (fGetUninitializedObject && Nullable::IsNullableType(pMT)) { pMT = pMT->GetInstantiation()[0].GetMethodTable(); } diff --git a/src/coreclr/src/vm/runtimehandles.h b/src/coreclr/src/vm/runtimehandles.h index ef9f8a7a632aa..fe6c6f7db8e86 100644 --- a/src/coreclr/src/vm/runtimehandles.h +++ b/src/coreclr/src/vm/runtimehandles.h @@ -131,7 +131,7 @@ class RuntimeTypeHandle { CLR_BOOL *pbHasNoDefaultCtor); static - void QCALLTYPE GetAllocatorFtn(QCall::TypeHandle pTypeHandle, PCODE* ppNewobjHelper, MethodTable** ppMT, BOOL fGetUninitializedInstance); + void QCALLTYPE GetAllocatorFtn(QCall::TypeHandle pTypeHandle, PCODE* ppNewobjHelper, MethodTable** ppMT, BOOL fGetUninitializedObject); static MethodDesc* QCALLTYPE GetDefaultCtor(QCall::TypeHandle pTypeHandle); From 6904495141ffc6692afdb5decce0d908c7dacc3f Mon Sep 17 00:00:00 2001 From: Levi Broderick Date: Thu, 19 Nov 2020 14:25:41 -0800 Subject: [PATCH 18/64] Fix linker failures, add asserts --- .../src/System/RuntimeType.CoreCLR.cs | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/coreclr/src/System.Private.CoreLib/src/System/RuntimeType.CoreCLR.cs b/src/coreclr/src/System.Private.CoreLib/src/System/RuntimeType.CoreCLR.cs index 7080e6a4299f5..f66f731450b82 100644 --- a/src/coreclr/src/System.Private.CoreLib/src/System/RuntimeType.CoreCLR.cs +++ b/src/coreclr/src/System.Private.CoreLib/src/System/RuntimeType.CoreCLR.cs @@ -3996,6 +3996,8 @@ static void CtorNoopStub(object? uninitializedObject) { } // but value types almost never have explicit parameterless ctors, so // this shouldn't be a problem in practice. + [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2072:UnrecognizedReflectionPattern", + Justification = "The linker requirements are already satisfied by the enclosing method.")] static void CtorUnboxValueTypeStub(object? uninitializedObject) { Debug.Assert(uninitializedObject != null); @@ -4011,6 +4013,8 @@ static void CtorUnboxValueTypeStub(object? uninitializedObject) else { // ValueType with no explicit parameterless ctor; assume ctor returns default(T) + + Debug.Assert(defaultCtorRMH.IsNullHandle()); } } else @@ -4034,6 +4038,8 @@ static void CtorUnboxValueTypeStub(object? uninitializedObject) else { // Reference type with explicit parameterless ctor + + Debug.Assert(!defaultCtorRMH.IsNullHandle()); } } @@ -4109,7 +4115,7 @@ static void CtorUnboxValueTypeStub(object? uninitializedObject) GenericCache = cache; } - if (publicOnly & !cache.CtorIsPublic) + if (!cache.CtorIsPublic && publicOnly) { throw new MissingMethodException(SR.Format(SR.Arg_NoDefCTor, this)); } From 05c35a82452d1c1ca12024a689f95fc4ad62efc1 Mon Sep 17 00:00:00 2001 From: Levi Broderick Date: Thu, 19 Nov 2020 14:28:16 -0800 Subject: [PATCH 19/64] Remove unwanted CreateInstanceDefaultCtor overload --- .../src/System/RuntimeType.CoreCLR.cs | 15 --------------- .../src/System/Activator.RuntimeType.cs | 4 ++-- 2 files changed, 2 insertions(+), 17 deletions(-) diff --git a/src/coreclr/src/System.Private.CoreLib/src/System/RuntimeType.CoreCLR.cs b/src/coreclr/src/System.Private.CoreLib/src/System/RuntimeType.CoreCLR.cs index f66f731450b82..465594760e8ad 100644 --- a/src/coreclr/src/System.Private.CoreLib/src/System/RuntimeType.CoreCLR.cs +++ b/src/coreclr/src/System.Private.CoreLib/src/System/RuntimeType.CoreCLR.cs @@ -4080,21 +4080,6 @@ static void CtorUnboxValueTypeStub(object? uninitializedObject) internal void CallConstructor(object? uninitializedObject) => _pfnCtor(uninitializedObject); } - // This method mimics the overload present in the mono codebase. It allows shared source callers - // to target this overload and work across both runtimes. CoreCLR ignores the 'skipCheckThis' and - // 'fillCache' parameters. - [DebuggerStepThrough] - [DebuggerHidden] - [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2082:UnrecognizedReflectionPattern", - Justification = "Implementation detail of Activator that linker intrinsically recognizes")] - [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2085:UnrecognizedReflectionPattern", - Justification = "Implementation detail of Activator that linker intrinsically recognizes")] - [MethodImpl(MethodImplOptions.AggressiveInlining)] - internal object? CreateInstanceDefaultCtor(bool publicOnly, bool skipCheckThis, bool fillCache, bool wrapExceptions) - { - return CreateInstanceDefaultCtor(publicOnly, wrapExceptions); - } - /// /// Helper to invoke the default (parameterless) constructor. /// diff --git a/src/libraries/System.Private.CoreLib/src/System/Activator.RuntimeType.cs b/src/libraries/System.Private.CoreLib/src/System/Activator.RuntimeType.cs index 043ec1fd275d5..1008e9e186720 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Activator.RuntimeType.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Activator.RuntimeType.cs @@ -94,7 +94,7 @@ public static partial class Activator throw new ArgumentNullException(nameof(type)); if (type.UnderlyingSystemType is RuntimeType rt) - return rt.CreateInstanceDefaultCtor(publicOnly: !nonPublic, skipCheckThis: false, fillCache: true, wrapExceptions: wrapExceptions); + return rt.CreateInstanceDefaultCtor(publicOnly: !nonPublic, wrapExceptions: wrapExceptions); throw new ArgumentException(SR.Arg_MustBeType, nameof(type)); } @@ -150,7 +150,7 @@ public static partial class Activator [System.Runtime.CompilerServices.Intrinsic] public static T CreateInstance<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)]T>() { - return (T)((RuntimeType)typeof(T)).CreateInstanceDefaultCtor(publicOnly: true, skipCheckThis: true, fillCache: true, wrapExceptions: true)!; + return (T)((RuntimeType)typeof(T)).CreateInstanceDefaultCtor(publicOnly: true, wrapExceptions: true)!; } private static T CreateDefaultInstance() where T : struct => default; From 3d8515acab710dcf9b50279eb4ae6a0fb502e99c Mon Sep 17 00:00:00 2001 From: Levi Broderick Date: Thu, 19 Nov 2020 14:39:37 -0800 Subject: [PATCH 20/64] Use WeakRef in ActivatorCache --- .../src/System/RuntimeType.CoreCLR.cs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/coreclr/src/System.Private.CoreLib/src/System/RuntimeType.CoreCLR.cs b/src/coreclr/src/System.Private.CoreLib/src/System/RuntimeType.CoreCLR.cs index 465594760e8ad..0618e2db62023 100644 --- a/src/coreclr/src/System.Private.CoreLib/src/System/RuntimeType.CoreCLR.cs +++ b/src/coreclr/src/System.Private.CoreLib/src/System/RuntimeType.CoreCLR.cs @@ -3962,7 +3962,7 @@ private sealed unsafe class ActivatorCache private readonly delegate* _pfnCtor; #if DEBUG - private readonly RuntimeType _originalRT; + private readonly WeakReference _originalRuntimeType; // don't prevent the RT from being collected #endif internal ActivatorCache([DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.NonPublicConstructors)] RuntimeType rt) @@ -3970,7 +3970,7 @@ internal ActivatorCache([DynamicallyAccessedMembers(DynamicallyAccessedMemberTyp Debug.Assert(rt != null); #if DEBUG - _originalRT = rt; + _originalRuntimeType = new WeakReference(rt); #endif _pfnAllocator = RuntimeTypeHandle.GetAllocatorFtn(rt, out _pMT, forGetUninitializedObject: false); @@ -4068,7 +4068,8 @@ static void CtorUnboxValueTypeStub(object? uninitializedObject) // as the object itself will keep the type alive. #if DEBUG - Debug.Assert(rt == _originalRT, "Caller passed the wrong RuntimeType to this routine."); + Debug.Assert(_originalRuntimeType.TryGetTarget(out RuntimeType? originalRT) && originalRT == rt, + "Caller passed the wrong RuntimeType to this routine."); #endif object? retVal = _pfnAllocator(_pMT); From 5acef41a31828ec529ab0b673004b59cd50d391b Mon Sep 17 00:00:00 2001 From: Levi Broderick Date: Thu, 19 Nov 2020 17:29:47 -0800 Subject: [PATCH 21/64] Reintroduce COM specialization --- .../System.Private.CoreLib.csproj | 1 + .../src/System/ActivatorCache.CoreCLR.cs | 169 ++++++++++++++++++ .../src/System/RuntimeHandles.cs | 26 ++- .../src/System/RuntimeType.CoreCLR.cs | 131 -------------- src/coreclr/src/vm/ecalllist.h | 1 + src/coreclr/src/vm/reflectioninvocation.cpp | 49 ++++- src/coreclr/src/vm/runtimehandles.h | 10 +- 7 files changed, 240 insertions(+), 147 deletions(-) create mode 100644 src/coreclr/src/System.Private.CoreLib/src/System/ActivatorCache.CoreCLR.cs diff --git a/src/coreclr/src/System.Private.CoreLib/System.Private.CoreLib.csproj b/src/coreclr/src/System.Private.CoreLib/System.Private.CoreLib.csproj index 4de4ac0aa5860..c5bf5c9f45c20 100644 --- a/src/coreclr/src/System.Private.CoreLib/System.Private.CoreLib.csproj +++ b/src/coreclr/src/System.Private.CoreLib/System.Private.CoreLib.csproj @@ -111,6 +111,7 @@ + diff --git a/src/coreclr/src/System.Private.CoreLib/src/System/ActivatorCache.CoreCLR.cs b/src/coreclr/src/System.Private.CoreLib/src/System/ActivatorCache.CoreCLR.cs new file mode 100644 index 0000000000000..9c991fceb057d --- /dev/null +++ b/src/coreclr/src/System.Private.CoreLib/src/System/ActivatorCache.CoreCLR.cs @@ -0,0 +1,169 @@ +// 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.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +namespace System +{ + internal sealed partial class RuntimeType + { + /// + /// A cache which allows optimizing , + /// , and related APIs. + /// + private sealed unsafe class ActivatorCache + { + // The managed calli to the newobj allocator, plus its first argument (MethodTable*). + // In the case of the COM allocator, first arg is ComClassFactory*, not MethodTable*. + private readonly delegate* _pfnAllocator; + private readonly IntPtr _allocatorFirstArg; + + // The managed calli to the parameterless ctor, taking "this" (as object) as its first argument. + // For value type ctors, we'll point to a special unboxing stub. + private readonly delegate* _pfnCtor; + +#if DEBUG + private readonly WeakReference _originalRuntimeType; // don't prevent the RT from being collected +#endif + + internal ActivatorCache([DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.NonPublicConstructors)] RuntimeType rt) + { + Debug.Assert(rt != null); + +#if DEBUG + _originalRuntimeType = new WeakReference(rt); +#endif + + _pfnAllocator = (delegate*)RuntimeTypeHandle.GetAllocatorFtn(rt, out MethodTable* pMT, forGetUninitializedObject: false); + _allocatorFirstArg = (IntPtr)pMT; + + static void CtorNoopStub(object? uninitializedObject) { } + _pfnCtor = &CtorNoopStub; + + RuntimeMethodHandleInternal defaultCtorRMH = RuntimeTypeHandle.GetDefaultConstructor(rt); // could be null + + if (pMT->IsValueType) + { + if (pMT->IsNullable) + { + // Activator.CreateInstance returns null given typeof(Nullable). + + static object? ReturnNull(IntPtr _) => null; + _pfnAllocator = &ReturnNull; + } + else if (pMT->HasDefaultConstructor) + { + // ValueType with explicit parameterless ctor typed as (ref T) -> void. + // We'll point the ctor at our unboxing stub. It's not terribly efficient, + // but value types almost never have explicit parameterless ctors, so + // this shouldn't be a problem in practice. + + [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2072:UnrecognizedReflectionPattern", + Justification = "The linker requirements are already satisfied by the enclosing method.")] + static void CtorUnboxValueTypeStub(object? uninitializedObject) + { + Debug.Assert(uninitializedObject != null); + Debug.Assert(RuntimeHelpers.GetMethodTable(uninitializedObject)->IsValueType); + + RuntimeType rt = (RuntimeType)uninitializedObject.GetType(); + RuntimeMethodHandleInternal rmh = RuntimeTypeHandle.GetDefaultConstructor(rt); + IntPtr pfnCtor = RuntimeMethodHandle.GetFunctionPointer(rmh); + ((delegate*)pfnCtor)(ref uninitializedObject!.GetRawData()); + } + _pfnCtor = &CtorUnboxValueTypeStub; + } + else + { + // ValueType with no explicit parameterless ctor; assume ctor returns default(T) + + Debug.Assert(defaultCtorRMH.IsNullHandle()); + } + } + else + { + // Reference type - we can't proceed unless there's a default ctor we can call. + + Debug.Assert(rt.IsClass); + + if (pMT->IsComObject) + { + if (rt.IsGenericCOMObjectImpl()) + { + // This is the __ComObject base type, which means that the MethodTable* we have + // doesn't contain CLSID information. The CLSID information is instead hanging + // off of the RuntimeType's sync block. We'll set the allocator to our stub, and + // instead of a MethodTable* we'll pass in the handle to the RuntimeType. The + // handles we create live for the lifetime of the app, but that's ok since it + // matches coreclr's internal implementation anyway (see GetComClassHelper). + + static object AllocateComObject(IntPtr runtimeTypeHandle) + { + RuntimeType rt = (RuntimeType)GCHandle.FromIntPtr(runtimeTypeHandle).Target!; + Debug.Assert(rt != null); + + return RuntimeTypeHandle.AllocateComObject(rt); + } + _pfnAllocator = &AllocateComObject; + _allocatorFirstArg = GCHandle.ToIntPtr(GCHandle.Alloc(rt)); + } + + // Neither __ComObject nor any derived type gets its parameterless ctor called. + // Activation is handled entirely by the allocator. + + defaultCtorRMH = default; + } + else if (!pMT->HasDefaultConstructor) + { + throw new MissingMethodException(SR.Format(SR.Arg_NoDefCTor, rt)); + } + else + { + // Reference type with explicit parameterless ctor + + Debug.Assert(!defaultCtorRMH.IsNullHandle()); + } + } + + if (defaultCtorRMH.IsNullHandle()) + { + CtorIsPublic = true; // implicit parameterless ctor is always considered public + } + else + { + CtorIsPublic = (RuntimeMethodHandle.GetAttributes(defaultCtorRMH) & MethodAttributes.Public) != 0; + } + + Debug.Assert(_pfnAllocator != null); + Debug.Assert(_allocatorFirstArg != IntPtr.Zero); + Debug.Assert(_pfnCtor != null); // we use null singleton pattern if no ctor call is necessary + } + + internal bool CtorIsPublic { get; } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal object? CreateUninitializedObject(RuntimeType rt) + { + // We don't use RuntimeType, but we force the caller to pass it so + // that we can keep it alive on their behalf. Once the object is + // constructed, we no longer need the reference to the type instance, + // as the object itself will keep the type alive. + +#if DEBUG + Debug.Assert(_originalRuntimeType.TryGetTarget(out RuntimeType? originalRT) && originalRT == rt, + "Caller passed the wrong RuntimeType to this routine."); +#endif + + object? retVal = _pfnAllocator(_allocatorFirstArg); + GC.KeepAlive(rt); + return retVal; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal void CallConstructor(object? uninitializedObject) => _pfnCtor(uninitializedObject); + } + } +} diff --git a/src/coreclr/src/System.Private.CoreLib/src/System/RuntimeHandles.cs b/src/coreclr/src/System.Private.CoreLib/src/System/RuntimeHandles.cs index 8c490b3ad9eff..c6b4e595668cc 100644 --- a/src/coreclr/src/System.Private.CoreLib/src/System/RuntimeHandles.cs +++ b/src/coreclr/src/System.Private.CoreLib/src/System/RuntimeHandles.cs @@ -199,9 +199,6 @@ internal static bool HasElementType(RuntimeType type) [MethodImpl(MethodImplOptions.InternalCall)] internal static extern object CreateInstance(RuntimeType type, bool publicOnly, bool wrapExceptions, ref bool canBeCached, ref RuntimeMethodHandleInternal ctor, ref bool hasNoDefaultCtor); - [MethodImpl(MethodImplOptions.InternalCall)] - internal static extern object Allocate(RuntimeType type); - [MethodImpl(MethodImplOptions.InternalCall)] internal static extern object CreateInstanceForAnotherGenericParameter(RuntimeType type, RuntimeType genericParameter); @@ -254,6 +251,29 @@ internal static RuntimeMethodHandleInternal GetDefaultConstructor( [DllImport(RuntimeHelpers.QCall, CharSet = CharSet.Unicode)] private static extern RuntimeMethodHandleInternal GetDefaultCtor(QCallTypeHandle typeHandle); + /// + /// Given a RuntimeType which represents __ComObject, activates the class and creates + /// a RCW around it. + /// + /// No CLSID present, or invalid CLSID. + internal static object AllocateComObject( + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.NonPublicConstructors)] RuntimeType type) + { + Debug.Assert(type != null); + + // n.b. use ObjectHandleOnStack instead of QCallTypeHandle since runtime needs the actual RuntimeType instance, + // not just its underlying TypeHandle. + + object activatedInstance = null!; + AllocateComObject(ObjectHandleOnStack.Create(ref type), ObjectHandleOnStack.Create(ref activatedInstance)); + + Debug.Assert(activatedInstance != null); + return activatedInstance; + } + + [DllImport(RuntimeHelpers.QCall, CharSet = CharSet.Unicode)] + private static extern void AllocateComObject(ObjectHandleOnStack runtimeType, ObjectHandleOnStack activatedInstance); + internal RuntimeType GetRuntimeType() { return m_type; diff --git a/src/coreclr/src/System.Private.CoreLib/src/System/RuntimeType.CoreCLR.cs b/src/coreclr/src/System.Private.CoreLib/src/System/RuntimeType.CoreCLR.cs index 0618e2db62023..8545a205015b1 100644 --- a/src/coreclr/src/System.Private.CoreLib/src/System/RuntimeType.CoreCLR.cs +++ b/src/coreclr/src/System.Private.CoreLib/src/System/RuntimeType.CoreCLR.cs @@ -3950,137 +3950,6 @@ private void CreateInstanceCheckThis() return instance; } - // the cache entry - private sealed unsafe class ActivatorCache - { - // The managed calli to the newobj allocator, plus its first argument (MethodTable*). - private readonly delegate* _pfnAllocator; - private readonly MethodTable* _pMT; - - // The managed calli to the parameterless ctor, taking "this" (as object) as its first argument. - // For value type ctors, we'll point to a special unboxing stub. - private readonly delegate* _pfnCtor; - -#if DEBUG - private readonly WeakReference _originalRuntimeType; // don't prevent the RT from being collected -#endif - - internal ActivatorCache([DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.NonPublicConstructors)] RuntimeType rt) - { - Debug.Assert(rt != null); - -#if DEBUG - _originalRuntimeType = new WeakReference(rt); -#endif - - _pfnAllocator = RuntimeTypeHandle.GetAllocatorFtn(rt, out _pMT, forGetUninitializedObject: false); - - static void CtorNoopStub(object? uninitializedObject) { } - _pfnCtor = &CtorNoopStub; - - RuntimeMethodHandleInternal defaultCtorRMH = RuntimeTypeHandle.GetDefaultConstructor(rt); // could be null - - if (_pMT->IsValueType) - { - if (_pMT->IsNullable) - { - // Activator.CreateInstance returns null given typeof(Nullable). - - static object? ReturnNull(MethodTable* pMT) => null; - _pfnAllocator = &ReturnNull; - } - else if (_pMT->HasDefaultConstructor) - { - // ValueType with explicit parameterless ctor typed as (ref T) -> void. - // We'll point the ctor at our unboxing stub. It's not terribly efficient, - // but value types almost never have explicit parameterless ctors, so - // this shouldn't be a problem in practice. - - [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2072:UnrecognizedReflectionPattern", - Justification = "The linker requirements are already satisfied by the enclosing method.")] - static void CtorUnboxValueTypeStub(object? uninitializedObject) - { - Debug.Assert(uninitializedObject != null); - Debug.Assert(RuntimeHelpers.GetMethodTable(uninitializedObject)->IsValueType); - - RuntimeType rt = (RuntimeType)uninitializedObject.GetType(); - RuntimeMethodHandleInternal rmh = RuntimeTypeHandle.GetDefaultConstructor(rt); - IntPtr pfnCtor = RuntimeMethodHandle.GetFunctionPointer(rmh); - ((delegate*)pfnCtor)(ref uninitializedObject!.GetRawData()); - } - _pfnCtor = &CtorUnboxValueTypeStub; - } - else - { - // ValueType with no explicit parameterless ctor; assume ctor returns default(T) - - Debug.Assert(defaultCtorRMH.IsNullHandle()); - } - } - else - { - // Reference type - we can't proceed unless there's a default ctor we can call. - - Debug.Assert(rt.IsClass); - - if (_pMT->IsComObject) - { - // COM RCW types go through the runtime's special allocator, but no ctor - // ever gets run over them. We won't overwrite the _pfnCtorStub field. - - Debug.Assert(!rt.IsGenericCOMObjectImpl(), "__ComObject base class should've been blocked."); - defaultCtorRMH = default; // ignore any parameterless ctor - } - else if (!_pMT->HasDefaultConstructor) - { - throw new MissingMethodException(SR.Format(SR.Arg_NoDefCTor, rt)); - } - else - { - // Reference type with explicit parameterless ctor - - Debug.Assert(!defaultCtorRMH.IsNullHandle()); - } - } - - if (defaultCtorRMH.IsNullHandle()) - { - CtorIsPublic = true; // implicit parameterless ctor is always considered public - } - else - { - CtorIsPublic = (RuntimeMethodHandle.GetAttributes(defaultCtorRMH) & MethodAttributes.Public) != 0; - } - - Debug.Assert(_pfnAllocator != null); - Debug.Assert(_pMT != null); - Debug.Assert(_pfnCtor != null); // we use null singleton pattern if no ctor call is necessary - } - - internal bool CtorIsPublic { get; } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - internal object? CreateUninitializedObject(RuntimeType rt) - { - // We don't use RuntimeType, but we force the caller to pass it so - // that we can keep it alive on their behalf. Once the object is - // constructed, we no longer need the reference to the type instance, - // as the object itself will keep the type alive. - -#if DEBUG - Debug.Assert(_originalRuntimeType.TryGetTarget(out RuntimeType? originalRT) && originalRT == rt, - "Caller passed the wrong RuntimeType to this routine."); -#endif - - object? retVal = _pfnAllocator(_pMT); - GC.KeepAlive(rt); - return retVal; - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - internal void CallConstructor(object? uninitializedObject) => _pfnCtor(uninitializedObject); - } - /// /// Helper to invoke the default (parameterless) constructor. /// diff --git a/src/coreclr/src/vm/ecalllist.h b/src/coreclr/src/vm/ecalllist.h index 17ea5afe1e07b..542198a1d8c11 100644 --- a/src/coreclr/src/vm/ecalllist.h +++ b/src/coreclr/src/vm/ecalllist.h @@ -240,6 +240,7 @@ FCFuncStart(gCOMTypeHandleFuncs) FCFuncElement("SatisfiesConstraints", RuntimeTypeHandle::SatisfiesConstraints) QCFuncElement("GetAllocatorFtn", RuntimeTypeHandle::GetAllocatorFtn) QCFuncElement("GetDefaultCtor", RuntimeTypeHandle::GetDefaultCtor) + QCFuncElement("AllocateComObject", RuntimeTypeHandle::AllocateComObject) FCFuncElement("CompareCanonicalHandles", RuntimeTypeHandle::CompareCanonicalHandles) FCIntrinsic("GetValueInternal", RuntimeTypeHandle::GetValueInternal, CORINFO_INTRINSIC_RTH_GetValueInternal) FCFuncElement("IsEquivalentTo", RuntimeTypeHandle::IsEquivalentTo) diff --git a/src/coreclr/src/vm/reflectioninvocation.cpp b/src/coreclr/src/vm/reflectioninvocation.cpp index 18781566291b8..0198c23cd7970 100644 --- a/src/coreclr/src/vm/reflectioninvocation.cpp +++ b/src/coreclr/src/vm/reflectioninvocation.cpp @@ -2078,12 +2078,9 @@ void QCALLTYPE RuntimeTypeHandle::GetAllocatorFtn( // transparent proxy or the jit will get confused. #ifdef FEATURE_COMINTEROP - // Never allow instantiation of the __ComObject base type, only RCWs. - // In a COM-enabled runitme, getNewHelperStatic will return an RCW-aware allocator. - if (IsComObjectClass(typeHandle)) - { - COMPlusThrow(kNotSupportedException, W("NotSupported_ManagedActivation")); - } + // COM allocation can involve the __ComObject base type (with attached CLSID) or an RCW type. + // We'll optimistically return a reference to the allocator, which will fail if there's not + // a legal CLSID associated with the requested type. #endif // FEATURE_COMINTEROP // If the caller is GetUninitializedInstance, they'll want a boxed T instead of a boxed Nullable. @@ -2140,6 +2137,46 @@ MethodDesc* QCALLTYPE RuntimeTypeHandle::GetDefaultCtor( return pMethodDesc; } +/* + * Given a RuntimeType that represents __ComObject, activates an instance of the + * COM object and returns a RCW around it. Throws if activation fails or if the + * RuntimeType isn't __ComObject. + */ +void QCALLTYPE RuntimeTypeHandle::AllocateComObject( + QCall::ObjectHandleOnStack refRuntimeType, + QCall::ObjectHandleOnStack retInstance) +{ + QCALL_CONTRACT; + + REFLECTCLASSBASEREF refThis = (REFLECTCLASSBASEREF)refRuntimeType.Get(); + bool allocated = false; + + BEGIN_QCALL; + +#ifdef FEATURE_COMINTEROP +#ifdef FEATURE_COMINTEROP_UNMANAGED_ACTIVATION + if (IsComObjectClass(refThis->GetType())) + { + SyncBlock* pSyncBlock = refThis->GetSyncBlock(); + + void* pClassFactory = (void*)pSyncBlock->GetInteropInfo()->GetComClassFactory(); + if (pClassFactory) + { + retInstance.Set(((ComClassFactory*)pClassFactory)->CreateInstance(NULL)); + allocated = true; + } + } +#endif // FEATURE_COMINTEROP_UNMANAGED_ACTIVATION +#endif // FEATURE_COMINTEROP + + if (!allocated) + { + COMPlusThrow(kInvalidComObjectException, IDS_EE_NO_BACKING_CLASS_FACTORY); + } + + END_QCALL; +} + //************************************************************************************************* //************************************************************************************************* //************************************************************************************************* diff --git a/src/coreclr/src/vm/runtimehandles.h b/src/coreclr/src/vm/runtimehandles.h index fe6c6f7db8e86..224a28b69599d 100644 --- a/src/coreclr/src/vm/runtimehandles.h +++ b/src/coreclr/src/vm/runtimehandles.h @@ -122,13 +122,6 @@ class RuntimeTypeHandle { public: // Static method on RuntimeTypeHandle - static FCDECL1(Object*, Allocate, ReflectClassBaseObject *refType) ; //A.CI work - static FCDECL6(Object*, CreateInstance, ReflectClassBaseObject* refThisUNSAFE, - CLR_BOOL publicOnly, - CLR_BOOL wrapExceptions, - CLR_BOOL *pbCanBeCached, - MethodDesc** pConstructor, - CLR_BOOL *pbHasNoDefaultCtor); static void QCALLTYPE GetAllocatorFtn(QCall::TypeHandle pTypeHandle, PCODE* ppNewobjHelper, MethodTable** ppMT, BOOL fGetUninitializedObject); @@ -136,6 +129,9 @@ class RuntimeTypeHandle { static MethodDesc* QCALLTYPE GetDefaultCtor(QCall::TypeHandle pTypeHandle); + static + void QCALLTYPE AllocateComObject(QCall::ObjectHandleOnStack refRuntimeType, QCall::ObjectHandleOnStack retInstance); + static void QCALLTYPE MakeByRef(QCall::TypeHandle pTypeHandle, QCall::ObjectHandleOnStack retType); From adfae42a39a83a25791dfe94b4e9825ed7cdc3fa Mon Sep 17 00:00:00 2001 From: Levi Broderick Date: Thu, 19 Nov 2020 17:48:33 -0800 Subject: [PATCH 22/64] Allow runtime to create unboxing stub on my behalf --- .../src/System/ActivatorCache.CoreCLR.cs | 39 ++++++------------- .../src/System/RuntimeHandles.cs | 10 +++-- src/coreclr/src/vm/methodtable.cpp | 4 +- src/coreclr/src/vm/methodtable.h | 2 +- src/coreclr/src/vm/reflectioninvocation.cpp | 8 ++-- src/coreclr/src/vm/runtimehandles.h | 2 +- 6 files changed, 27 insertions(+), 38 deletions(-) diff --git a/src/coreclr/src/System.Private.CoreLib/src/System/ActivatorCache.CoreCLR.cs b/src/coreclr/src/System.Private.CoreLib/src/System/ActivatorCache.CoreCLR.cs index 9c991fceb057d..25b7010c1b5e0 100644 --- a/src/coreclr/src/System.Private.CoreLib/src/System/ActivatorCache.CoreCLR.cs +++ b/src/coreclr/src/System.Private.CoreLib/src/System/ActivatorCache.CoreCLR.cs @@ -41,10 +41,7 @@ internal ActivatorCache([DynamicallyAccessedMembers(DynamicallyAccessedMemberTyp _pfnAllocator = (delegate*)RuntimeTypeHandle.GetAllocatorFtn(rt, out MethodTable* pMT, forGetUninitializedObject: false); _allocatorFirstArg = (IntPtr)pMT; - static void CtorNoopStub(object? uninitializedObject) { } - _pfnCtor = &CtorNoopStub; - - RuntimeMethodHandleInternal defaultCtorRMH = RuntimeTypeHandle.GetDefaultConstructor(rt); // could be null + RuntimeMethodHandleInternal ctorHandle = RuntimeMethodHandleInternal.EmptyHandle; // default nullptr if (pMT->IsValueType) { @@ -57,30 +54,14 @@ static void CtorNoopStub(object? uninitializedObject) { } } else if (pMT->HasDefaultConstructor) { - // ValueType with explicit parameterless ctor typed as (ref T) -> void. - // We'll point the ctor at our unboxing stub. It's not terribly efficient, - // but value types almost never have explicit parameterless ctors, so - // this shouldn't be a problem in practice. - - [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2072:UnrecognizedReflectionPattern", - Justification = "The linker requirements are already satisfied by the enclosing method.")] - static void CtorUnboxValueTypeStub(object? uninitializedObject) - { - Debug.Assert(uninitializedObject != null); - Debug.Assert(RuntimeHelpers.GetMethodTable(uninitializedObject)->IsValueType); + // Value type with an explicit default ctor; we'll ask the runtime to create + // an unboxing stub on our behalf. - RuntimeType rt = (RuntimeType)uninitializedObject.GetType(); - RuntimeMethodHandleInternal rmh = RuntimeTypeHandle.GetDefaultConstructor(rt); - IntPtr pfnCtor = RuntimeMethodHandle.GetFunctionPointer(rmh); - ((delegate*)pfnCtor)(ref uninitializedObject!.GetRawData()); - } - _pfnCtor = &CtorUnboxValueTypeStub; + ctorHandle = RuntimeTypeHandle.GetDefaultConstructor(rt, forceBoxedEntryPoint: true); } else { // ValueType with no explicit parameterless ctor; assume ctor returns default(T) - - Debug.Assert(defaultCtorRMH.IsNullHandle()); } } else @@ -114,7 +95,7 @@ static object AllocateComObject(IntPtr runtimeTypeHandle) // Neither __ComObject nor any derived type gets its parameterless ctor called. // Activation is handled entirely by the allocator. - defaultCtorRMH = default; + ctorHandle = default; } else if (!pMT->HasDefaultConstructor) { @@ -124,17 +105,21 @@ static object AllocateComObject(IntPtr runtimeTypeHandle) { // Reference type with explicit parameterless ctor - Debug.Assert(!defaultCtorRMH.IsNullHandle()); + ctorHandle = RuntimeTypeHandle.GetDefaultConstructor(rt, forceBoxedEntryPoint: false); + Debug.Assert(!ctorHandle.IsNullHandle()); } } - if (defaultCtorRMH.IsNullHandle()) + if (ctorHandle.IsNullHandle()) { + static void CtorNoopStub(object? uninitializedObject) { } + _pfnCtor = &CtorNoopStub; // we use null singleton pattern if no ctor call is necessary CtorIsPublic = true; // implicit parameterless ctor is always considered public } else { - CtorIsPublic = (RuntimeMethodHandle.GetAttributes(defaultCtorRMH) & MethodAttributes.Public) != 0; + _pfnCtor = (delegate*)RuntimeMethodHandle.GetFunctionPointer(ctorHandle); + CtorIsPublic = (RuntimeMethodHandle.GetAttributes(ctorHandle) & MethodAttributes.Public) != 0; } Debug.Assert(_pfnAllocator != null); diff --git a/src/coreclr/src/System.Private.CoreLib/src/System/RuntimeHandles.cs b/src/coreclr/src/System.Private.CoreLib/src/System/RuntimeHandles.cs index c6b4e595668cc..01b51b0cb3c60 100644 --- a/src/coreclr/src/System.Private.CoreLib/src/System/RuntimeHandles.cs +++ b/src/coreclr/src/System.Private.CoreLib/src/System/RuntimeHandles.cs @@ -237,19 +237,21 @@ internal static bool HasElementType(RuntimeType type) /// /// Returns the MethodDesc* for this type's parameterless instance ctor. /// For reference types, signature is (object @this) -> void. - /// For value types, signature is (ref T @thisUnboxed) -> void. + /// For value types, unboxed signature is (ref T @thisUnboxed) -> void. + /// For value types, forced boxed signature is (object @this) -> void. /// Returns nullptr if no parameterless ctor is defined. /// internal static RuntimeMethodHandleInternal GetDefaultConstructor( - [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.NonPublicConstructors)] RuntimeType type) + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.NonPublicConstructors)] RuntimeType type, + bool forceBoxedEntryPoint) { Debug.Assert(type != null); - return GetDefaultCtor(new QCallTypeHandle(ref type)); + return GetDefaultCtor(new QCallTypeHandle(ref type), (forceBoxedEntryPoint) ? Interop.BOOL.TRUE : Interop.BOOL.FALSE); } [DllImport(RuntimeHelpers.QCall, CharSet = CharSet.Unicode)] - private static extern RuntimeMethodHandleInternal GetDefaultCtor(QCallTypeHandle typeHandle); + private static extern RuntimeMethodHandleInternal GetDefaultCtor(QCallTypeHandle typeHandle, Interop.BOOL forceBoxedEntryPoint); /// /// Given a RuntimeType which represents __ComObject, activates the class and creates diff --git a/src/coreclr/src/vm/methodtable.cpp b/src/coreclr/src/vm/methodtable.cpp index 845c655cef9df..5c600590f8f75 100644 --- a/src/coreclr/src/vm/methodtable.cpp +++ b/src/coreclr/src/vm/methodtable.cpp @@ -9150,7 +9150,7 @@ BOOL MethodTable::HasExplicitOrImplicitPublicDefaultConstructor() } //========================================================================================== -MethodDesc *MethodTable::GetDefaultConstructor() +MethodDesc *MethodTable::GetDefaultConstructor(BOOL forceBoxedEntryPoint) { WRAPPER_NO_CONTRACT; _ASSERTE(HasDefaultConstructor()); @@ -9161,7 +9161,7 @@ MethodDesc *MethodTable::GetDefaultConstructor() // returns pCanonMD immediately. return MethodDesc::FindOrCreateAssociatedMethodDesc(pCanonMD, this, - FALSE /* no BoxedEntryPointStub */, + forceBoxedEntryPoint, Instantiation(), /* no method instantiation */ FALSE /* no allowInstParam */); } diff --git a/src/coreclr/src/vm/methodtable.h b/src/coreclr/src/vm/methodtable.h index 850a13d388935..4f05c6a1868c2 100644 --- a/src/coreclr/src/vm/methodtable.h +++ b/src/coreclr/src/vm/methodtable.h @@ -823,7 +823,7 @@ class MethodTable BOOL HasDefaultConstructor(); void SetHasDefaultConstructor(); WORD GetDefaultConstructorSlot(); - MethodDesc *GetDefaultConstructor(); + MethodDesc *GetDefaultConstructor(BOOL forceBoxedEntryPoint = FALSE); BOOL HasExplicitOrImplicitPublicDefaultConstructor(); diff --git a/src/coreclr/src/vm/reflectioninvocation.cpp b/src/coreclr/src/vm/reflectioninvocation.cpp index 0198c23cd7970..b2dbfb30841f6 100644 --- a/src/coreclr/src/vm/reflectioninvocation.cpp +++ b/src/coreclr/src/vm/reflectioninvocation.cpp @@ -2110,11 +2110,13 @@ void QCALLTYPE RuntimeTypeHandle::GetAllocatorFtn( * Given a TypeHandle, returns the MethodDesc* for the default (parameterless) ctor, * or nullptr if the parameterless ctor doesn't exist. For reference types, the parameterless * ctor has a managed (object @this) -> void calling convention. For value types, - * the parameterless ctor has a managed (ref T @this) -> void calling convention. + * the parameterless ctor has a managed (ref T @this) -> void calling convention, unless + * fForceBoxedEntryPoint is set, then managed (object @this) -> void stub is returned. * The returned MethodDesc* is appropriately instantiated over any necessary generic args. */ MethodDesc* QCALLTYPE RuntimeTypeHandle::GetDefaultCtor( - QCall::TypeHandle pTypeHandle) + QCall::TypeHandle pTypeHandle, + BOOL fForceBoxedEntryPoint) { QCALL_CONTRACT; @@ -2129,7 +2131,7 @@ MethodDesc* QCALLTYPE RuntimeTypeHandle::GetDefaultCtor( pMT->EnsureInstanceActive(); if (pMT->HasDefaultConstructor()) { - pMethodDesc = pMT->GetDefaultConstructor(); + pMethodDesc = pMT->GetDefaultConstructor(fForceBoxedEntryPoint); } END_QCALL; diff --git a/src/coreclr/src/vm/runtimehandles.h b/src/coreclr/src/vm/runtimehandles.h index 224a28b69599d..609615b7edd64 100644 --- a/src/coreclr/src/vm/runtimehandles.h +++ b/src/coreclr/src/vm/runtimehandles.h @@ -127,7 +127,7 @@ class RuntimeTypeHandle { void QCALLTYPE GetAllocatorFtn(QCall::TypeHandle pTypeHandle, PCODE* ppNewobjHelper, MethodTable** ppMT, BOOL fGetUninitializedObject); static - MethodDesc* QCALLTYPE GetDefaultCtor(QCall::TypeHandle pTypeHandle); + MethodDesc* QCALLTYPE GetDefaultCtor(QCall::TypeHandle pTypeHandle, BOOL fForceBoxedEntryPoint); static void QCALLTYPE AllocateComObject(QCall::ObjectHandleOnStack refRuntimeType, QCall::ObjectHandleOnStack retInstance); From 49a8de11a0ffd1caf9924e0c722e80b3f9acb3db Mon Sep 17 00:00:00 2001 From: Levi Broderick Date: Thu, 19 Nov 2020 18:01:54 -0800 Subject: [PATCH 23/64] Fix linker warning --- .../System.Private.CoreLib/src/System/ActivatorCache.CoreCLR.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/coreclr/src/System.Private.CoreLib/src/System/ActivatorCache.CoreCLR.cs b/src/coreclr/src/System.Private.CoreLib/src/System/ActivatorCache.CoreCLR.cs index 25b7010c1b5e0..7b0a6a0fda8ea 100644 --- a/src/coreclr/src/System.Private.CoreLib/src/System/ActivatorCache.CoreCLR.cs +++ b/src/coreclr/src/System.Private.CoreLib/src/System/ActivatorCache.CoreCLR.cs @@ -81,6 +81,8 @@ internal ActivatorCache([DynamicallyAccessedMembers(DynamicallyAccessedMemberTyp // handles we create live for the lifetime of the app, but that's ok since it // matches coreclr's internal implementation anyway (see GetComClassHelper). + [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2072:UnrecognizedReflectionPattern", + Justification = "Linker already saw this type through Activator/Type.CreateInstance.")] static object AllocateComObject(IntPtr runtimeTypeHandle) { RuntimeType rt = (RuntimeType)GCHandle.FromIntPtr(runtimeTypeHandle).Target!; From 98c7941d0b1078b8bdb5506a4c318eae89335772 Mon Sep 17 00:00:00 2001 From: Levi Broderick Date: Thu, 19 Nov 2020 18:12:17 -0800 Subject: [PATCH 24/64] Update error conditions --- src/coreclr/src/vm/reflectioninvocation.cpp | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/src/coreclr/src/vm/reflectioninvocation.cpp b/src/coreclr/src/vm/reflectioninvocation.cpp index b2dbfb30841f6..36991860a02c0 100644 --- a/src/coreclr/src/vm/reflectioninvocation.cpp +++ b/src/coreclr/src/vm/reflectioninvocation.cpp @@ -2006,7 +2006,7 @@ FCIMPLEND * a boxed zero-inited instance of the value type. The "fGetUninitializedObject" * parameter dictates whether the caller is RuntimeHelpers.GetUninitializedObject or * Activator.CreateInstance, which have different behavior w.r.t. what exceptions are - * thrown on failure and how nullables are handled. + * thrown on failure and how nullables and COM types are handled. */ void QCALLTYPE RuntimeTypeHandle::GetAllocatorFtn( QCall::TypeHandle pTypeHandle, @@ -2058,12 +2058,16 @@ void QCALLTYPE RuntimeTypeHandle::GetAllocatorFtn( // Don't allow abstract classes or interface types if (pMT->IsAbstract()) { - COMPlusThrow(kMemberAccessException, W("Acc_CreateAbst")); + RuntimeExceptionKind exKind = fGetUninitializedObject ? kMemberAccessException : kMissingMethodException; + if (pMT->IsInterface()) + COMPlusThrow(exKind, W("Acc_CreateInterface")); + else + COMPlusThrow(exKind, W("Acc_CreateAbst")); } // Don't allow open generics or generics instantiated over __Canon if (pMT->ContainsGenericVariables()) { - COMPlusThrow(kMemberAccessException, W("Acc_CreateGeneric")); + COMPlusThrow(fGetUninitializedObject ? kMemberAccessException : kArgumentException, W("Acc_CreateGeneric")); } if (pMT->IsSharedByGenericInstantiations()) { COMPlusThrow(kNotSupportedException, W("NotSupported_Type")); @@ -2079,8 +2083,14 @@ void QCALLTYPE RuntimeTypeHandle::GetAllocatorFtn( #ifdef FEATURE_COMINTEROP // COM allocation can involve the __ComObject base type (with attached CLSID) or an RCW type. - // We'll optimistically return a reference to the allocator, which will fail if there's not - // a legal CLSID associated with the requested type. + // For Activator.CreateInstance, we'll optimistically return a reference to the allocator, + // which will fail if there's not a legal CLSID associated with the requested type. + // For __ComObject, Activator.CreateInstance will special-case this and replace it with a stub. + // RuntimeHelpers.GetUninitializedObject always fails when it sees COM objects. + if (fGetUninitializedObject && pMT->IsComObjectType()) + { + COMPlusThrow(kNotSupportedException, W("NotSupported_ManagedActivation")); + } #endif // FEATURE_COMINTEROP // If the caller is GetUninitializedInstance, they'll want a boxed T instead of a boxed Nullable. From 0a443c2d43403c9a0135a992ee3f7411565ff5ea Mon Sep 17 00:00:00 2001 From: Levi Broderick Date: Thu, 19 Nov 2020 19:05:06 -0800 Subject: [PATCH 25/64] Friendlier exception messages when activation fails --- .../src/System/ActivatorCache.CoreCLR.cs | 39 ++++++++++++- src/coreclr/src/vm/reflectioninvocation.cpp | 10 ++-- .../src/Resources/Strings.resx | 57 ++++++++++--------- 3 files changed, 73 insertions(+), 33 deletions(-) diff --git a/src/coreclr/src/System.Private.CoreLib/src/System/ActivatorCache.CoreCLR.cs b/src/coreclr/src/System.Private.CoreLib/src/System/ActivatorCache.CoreCLR.cs index 7b0a6a0fda8ea..ce712394bfb19 100644 --- a/src/coreclr/src/System.Private.CoreLib/src/System/ActivatorCache.CoreCLR.cs +++ b/src/coreclr/src/System.Private.CoreLib/src/System/ActivatorCache.CoreCLR.cs @@ -38,7 +38,19 @@ internal ActivatorCache([DynamicallyAccessedMembers(DynamicallyAccessedMemberTyp _originalRuntimeType = new WeakReference(rt); #endif - _pfnAllocator = (delegate*)RuntimeTypeHandle.GetAllocatorFtn(rt, out MethodTable* pMT, forGetUninitializedObject: false); + MethodTable* pMT; + + try + { + _pfnAllocator = (delegate*)RuntimeTypeHandle.GetAllocatorFtn(rt, out pMT, forGetUninitializedObject: false); + } + catch (Exception ex) + { + Exception? friendlyException = CreateAllocatorLookupFailedException(rt, ex); + if (friendlyException != null) { throw friendlyException; } + throw; // throw original exception if we couldn't create a friendly one + } + _allocatorFirstArg = (IntPtr)pMT; RuntimeMethodHandleInternal ctorHandle = RuntimeMethodHandleInternal.EmptyHandle; // default nullptr @@ -151,6 +163,31 @@ static void CtorNoopStub(object? uninitializedObject) { } [MethodImpl(MethodImplOptions.AggressiveInlining)] internal void CallConstructor(object? uninitializedObject) => _pfnCtor(uninitializedObject); + + [StackTraceHidden] + private static Exception? CreateAllocatorLookupFailedException(RuntimeType rt, Exception ex) + { + // If the inner exception is populated, probably a failed cctor, and we should + // propagate the exception as-is. If the exception is a type that we understand, + // we should make the error message friendlier so as to include the name of the type + // that couldn't be instantiated. + + if (ex.InnerException is null) + { + string friendlyMessage = SR.Format(SR.ActivatorCache_CannotGetAllocator, rt, ex.Message); + + switch (ex) + { + case ArgumentException: return new ArgumentException(friendlyMessage); + case NotSupportedException: return new NotSupportedException(friendlyMessage); + case MethodAccessException: return new MethodAccessException(friendlyMessage); + case MissingMethodException: return new MissingMethodException(friendlyMessage); + case MemberAccessException: return new MemberAccessException(friendlyMessage); + } + } + + return null; // caller should rethrow + } } } } diff --git a/src/coreclr/src/vm/reflectioninvocation.cpp b/src/coreclr/src/vm/reflectioninvocation.cpp index 36991860a02c0..e42765e965301 100644 --- a/src/coreclr/src/vm/reflectioninvocation.cpp +++ b/src/coreclr/src/vm/reflectioninvocation.cpp @@ -2030,13 +2030,13 @@ void QCALLTYPE RuntimeTypeHandle::GetAllocatorFtn( // Don't allow void if (typeHandle.GetSignatureCorElementType() == ELEMENT_TYPE_VOID) { - COMPlusThrow(kArgumentException, W("Argument_InvalidValue")); + COMPlusThrow(kArgumentException, W("NotSupported_Type")); } // Don't allow arrays, pointers, byrefs, or function pointers if (typeHandle.IsTypeDesc() || typeHandle.IsArray()) { - COMPlusThrow(kArgumentException, W("Argument_InvalidValue")); + COMPlusThrow(kNotSupportedException, W("NotSupported_Type")); } MethodTable* pMT = typeHandle.AsMethodTable(); @@ -2044,10 +2044,10 @@ void QCALLTYPE RuntimeTypeHandle::GetAllocatorFtn( pMT->EnsureInstanceActive(); - // Don't allow creating instances of void or delegates - if (pMT == CoreLibBinder::GetElementType(ELEMENT_TYPE_VOID) || pMT->IsDelegate()) + // Don't allow creating instances of delegates + if (pMT->IsDelegate()) { - COMPlusThrow(kArgumentException, W("Argument_InvalidValue")); + COMPlusThrow(kArgumentException, W("NotSupported_Type")); } // Don't allow string or string-like (variable length) types. diff --git a/src/libraries/System.Private.CoreLib/src/Resources/Strings.resx b/src/libraries/System.Private.CoreLib/src/Resources/Strings.resx index f3a82588fc95c..ae5144f7abed5 100644 --- a/src/libraries/System.Private.CoreLib/src/Resources/Strings.resx +++ b/src/libraries/System.Private.CoreLib/src/Resources/Strings.resx @@ -1,17 +1,17 @@  - @@ -3775,4 +3775,7 @@ CodeBase is not supported on assemblies loaded from a single-file bundle. + + Cannot dynamically create an instance of type '{0}'. Reason: {1} + From 3b34ae6c22fbf7263dff141b229626d8325b2eee Mon Sep 17 00:00:00 2001 From: Levi Broderick Date: Thu, 19 Nov 2020 19:11:29 -0800 Subject: [PATCH 26/64] Cleanup usings --- .../src/System/Activator.RuntimeType.cs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/libraries/System.Private.CoreLib/src/System/Activator.RuntimeType.cs b/src/libraries/System.Private.CoreLib/src/System/Activator.RuntimeType.cs index 1008e9e186720..7607ec2ea838e 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Activator.RuntimeType.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Activator.RuntimeType.cs @@ -2,13 +2,11 @@ // The .NET Foundation licenses this file to you under the MIT license. using System.Diagnostics.CodeAnalysis; -using System.Reflection; using System.Globalization; -using System.Runtime.CompilerServices; +using System.Reflection; using System.Runtime.Loader; using System.Runtime.Remoting; using System.Threading; -using System.Diagnostics; namespace System { From 6e1b7cdedbdd0f2a9f32a0e6376d0c0d4cd216a2 Mon Sep 17 00:00:00 2001 From: Levi Broderick Date: Thu, 19 Nov 2020 21:39:53 -0800 Subject: [PATCH 27/64] Move friendly exception messages to common place --- .../src/System/ActivatorCache.CoreCLR.cs | 39 +------------------ .../src/System/RuntimeHandles.cs | 36 ++++++++++++++--- src/coreclr/src/vm/reflectioninvocation.cpp | 21 +++++++--- 3 files changed, 48 insertions(+), 48 deletions(-) diff --git a/src/coreclr/src/System.Private.CoreLib/src/System/ActivatorCache.CoreCLR.cs b/src/coreclr/src/System.Private.CoreLib/src/System/ActivatorCache.CoreCLR.cs index ce712394bfb19..7b0a6a0fda8ea 100644 --- a/src/coreclr/src/System.Private.CoreLib/src/System/ActivatorCache.CoreCLR.cs +++ b/src/coreclr/src/System.Private.CoreLib/src/System/ActivatorCache.CoreCLR.cs @@ -38,19 +38,7 @@ internal ActivatorCache([DynamicallyAccessedMembers(DynamicallyAccessedMemberTyp _originalRuntimeType = new WeakReference(rt); #endif - MethodTable* pMT; - - try - { - _pfnAllocator = (delegate*)RuntimeTypeHandle.GetAllocatorFtn(rt, out pMT, forGetUninitializedObject: false); - } - catch (Exception ex) - { - Exception? friendlyException = CreateAllocatorLookupFailedException(rt, ex); - if (friendlyException != null) { throw friendlyException; } - throw; // throw original exception if we couldn't create a friendly one - } - + _pfnAllocator = (delegate*)RuntimeTypeHandle.GetAllocatorFtn(rt, out MethodTable* pMT, forGetUninitializedObject: false); _allocatorFirstArg = (IntPtr)pMT; RuntimeMethodHandleInternal ctorHandle = RuntimeMethodHandleInternal.EmptyHandle; // default nullptr @@ -163,31 +151,6 @@ static void CtorNoopStub(object? uninitializedObject) { } [MethodImpl(MethodImplOptions.AggressiveInlining)] internal void CallConstructor(object? uninitializedObject) => _pfnCtor(uninitializedObject); - - [StackTraceHidden] - private static Exception? CreateAllocatorLookupFailedException(RuntimeType rt, Exception ex) - { - // If the inner exception is populated, probably a failed cctor, and we should - // propagate the exception as-is. If the exception is a type that we understand, - // we should make the error message friendlier so as to include the name of the type - // that couldn't be instantiated. - - if (ex.InnerException is null) - { - string friendlyMessage = SR.Format(SR.ActivatorCache_CannotGetAllocator, rt, ex.Message); - - switch (ex) - { - case ArgumentException: return new ArgumentException(friendlyMessage); - case NotSupportedException: return new NotSupportedException(friendlyMessage); - case MethodAccessException: return new MethodAccessException(friendlyMessage); - case MissingMethodException: return new MissingMethodException(friendlyMessage); - case MemberAccessException: return new MemberAccessException(friendlyMessage); - } - } - - return null; // caller should rethrow - } } } } diff --git a/src/coreclr/src/System.Private.CoreLib/src/System/RuntimeHandles.cs b/src/coreclr/src/System.Private.CoreLib/src/System/RuntimeHandles.cs index 01b51b0cb3c60..7faff86d881ec 100644 --- a/src/coreclr/src/System.Private.CoreLib/src/System/RuntimeHandles.cs +++ b/src/coreclr/src/System.Private.CoreLib/src/System/RuntimeHandles.cs @@ -221,11 +221,37 @@ internal static bool HasElementType(RuntimeType type) delegate* pNewobjHelperTemp = null; MethodTable* pMTTemp = null; - GetAllocatorFtn( - new QCallTypeHandle(ref type), - &pNewobjHelperTemp, - &pMTTemp, - forGetUninitializedObject ? Interop.BOOL.TRUE : Interop.BOOL.FALSE); + try + { + GetAllocatorFtn( + new QCallTypeHandle(ref type), + &pNewobjHelperTemp, + &pMTTemp, + forGetUninitializedObject ? Interop.BOOL.TRUE : Interop.BOOL.FALSE); + } + catch (Exception ex) + { + // If the inner exception is populated, probably a failed cctor, and we should + // propagate the exception as-is. If the exception is a type that we understand, + // we should make the error message friendlier so as to include the name of the type + // that couldn't be instantiated. + + if (ex.InnerException is null) + { + string friendlyMessage = SR.Format(SR.ActivatorCache_CannotGetAllocator, type, ex.Message); + + switch (ex) + { + case ArgumentException: throw new ArgumentException(friendlyMessage); + case NotSupportedException: throw new NotSupportedException(friendlyMessage); + case MethodAccessException: throw new MethodAccessException(friendlyMessage); + case MissingMethodException: throw new MissingMethodException(friendlyMessage); + case MemberAccessException: throw new MemberAccessException(friendlyMessage); + } + } + + throw; // can't make a friendlier message, rethrow original exception + } pMT = pMTTemp; return pNewobjHelperTemp; diff --git a/src/coreclr/src/vm/reflectioninvocation.cpp b/src/coreclr/src/vm/reflectioninvocation.cpp index e42765e965301..f3b41d1314422 100644 --- a/src/coreclr/src/vm/reflectioninvocation.cpp +++ b/src/coreclr/src/vm/reflectioninvocation.cpp @@ -2033,10 +2033,17 @@ void QCALLTYPE RuntimeTypeHandle::GetAllocatorFtn( COMPlusThrow(kArgumentException, W("NotSupported_Type")); } + // Don't allow generic variables (e.g., the 'T' from List) + // or open generic types (List<>) - Activator variation of check. + if (!fGetUninitializedObject && typeHandle.ContainsGenericVariables()) + { + COMPlusThrow(kArgumentException, W("Acc_CreateGeneric")); + } + // Don't allow arrays, pointers, byrefs, or function pointers if (typeHandle.IsTypeDesc() || typeHandle.IsArray()) { - COMPlusThrow(kNotSupportedException, W("NotSupported_Type")); + COMPlusThrow(fGetUninitializedObject ? kArgumentException : kMissingMethodException, W("NotSupported_Type")); } MethodTable* pMT = typeHandle.AsMethodTable(); @@ -2053,7 +2060,7 @@ void QCALLTYPE RuntimeTypeHandle::GetAllocatorFtn( // Don't allow string or string-like (variable length) types. if (pMT->HasComponentSize()) { - COMPlusThrow(kArgumentException, W("Argument_NoUninitializedStrings")); + COMPlusThrow(fGetUninitializedObject ? kArgumentException : kMissingMethodException, W("Argument_NoUninitializedStrings")); } // Don't allow abstract classes or interface types @@ -2065,10 +2072,14 @@ void QCALLTYPE RuntimeTypeHandle::GetAllocatorFtn( COMPlusThrow(exKind, W("Acc_CreateAbst")); } - // Don't allow open generics or generics instantiated over __Canon - if (pMT->ContainsGenericVariables()) { - COMPlusThrow(fGetUninitializedObject ? kMemberAccessException : kArgumentException, W("Acc_CreateGeneric")); + // Don't allow generic variables (e.g., the 'T' from List) + // or open generic types (List<>) - FormatterServices variation of check. + if (fGetUninitializedObject && typeHandle.ContainsGenericVariables()) + { + COMPlusThrow(kMemberAccessException, W("Acc_CreateGeneric")); } + + // Don't allow generics instantiated over __Canon if (pMT->IsSharedByGenericInstantiations()) { COMPlusThrow(kNotSupportedException, W("NotSupported_Type")); } From b588d5d62cbdd777009c0138747f1a1b00e6cb1e Mon Sep 17 00:00:00 2001 From: Levi Broderick Date: Thu, 19 Nov 2020 23:14:00 -0800 Subject: [PATCH 28/64] Fix TIE wrappers --- .../src/System/ActivatorCache.CoreCLR.cs | 6 ++- .../RuntimeHelpers.CoreCLR.cs | 2 +- .../src/System/RuntimeHandles.cs | 39 ++++++++++--------- .../src/System/RuntimeType.CoreCLR.cs | 2 +- src/coreclr/src/vm/reflectioninvocation.cpp | 7 +++- src/coreclr/src/vm/runtimehandles.h | 2 +- 6 files changed, 34 insertions(+), 24 deletions(-) diff --git a/src/coreclr/src/System.Private.CoreLib/src/System/ActivatorCache.CoreCLR.cs b/src/coreclr/src/System.Private.CoreLib/src/System/ActivatorCache.CoreCLR.cs index 7b0a6a0fda8ea..62582b28200ad 100644 --- a/src/coreclr/src/System.Private.CoreLib/src/System/ActivatorCache.CoreCLR.cs +++ b/src/coreclr/src/System.Private.CoreLib/src/System/ActivatorCache.CoreCLR.cs @@ -30,7 +30,9 @@ private sealed unsafe class ActivatorCache private readonly WeakReference _originalRuntimeType; // don't prevent the RT from being collected #endif - internal ActivatorCache([DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.NonPublicConstructors)] RuntimeType rt) + internal ActivatorCache( + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.NonPublicConstructors)] RuntimeType rt, + bool wrapExceptions) { Debug.Assert(rt != null); @@ -38,7 +40,7 @@ internal ActivatorCache([DynamicallyAccessedMembers(DynamicallyAccessedMemberTyp _originalRuntimeType = new WeakReference(rt); #endif - _pfnAllocator = (delegate*)RuntimeTypeHandle.GetAllocatorFtn(rt, out MethodTable* pMT, forGetUninitializedObject: false); + _pfnAllocator = (delegate*)RuntimeTypeHandle.GetAllocatorFtn(rt, out MethodTable* pMT, forGetUninitializedObject: false, wrapExceptions); _allocatorFirstArg = (IntPtr)pMT; RuntimeMethodHandleInternal ctorHandle = RuntimeMethodHandleInternal.EmptyHandle; // default nullptr diff --git a/src/coreclr/src/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.CoreCLR.cs b/src/coreclr/src/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.CoreCLR.cs index 0526bd8028da4..b74fd6f0c4050 100644 --- a/src/coreclr/src/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.CoreCLR.cs +++ b/src/coreclr/src/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.CoreCLR.cs @@ -144,7 +144,7 @@ private static unsafe object GetUninitializedObjectInternal( Debug.Assert(rt != null); // If type is Nullable, returns the allocator and MethodTable* for the underlying T. - delegate* pfnAllocator = RuntimeTypeHandle.GetAllocatorFtn(rt, out MethodTable* pMT, forGetUninitializedObject: true); + delegate* pfnAllocator = RuntimeTypeHandle.GetAllocatorFtn(rt, out MethodTable* pMT, forGetUninitializedObject: true, wrapExceptions: false); Debug.Assert(pfnAllocator != null); Debug.Assert(pMT != null); Debug.Assert(!pMT->IsNullable, "Should've unwrapped any Nullable input."); diff --git a/src/coreclr/src/System.Private.CoreLib/src/System/RuntimeHandles.cs b/src/coreclr/src/System.Private.CoreLib/src/System/RuntimeHandles.cs index 7faff86d881ec..7903d79a09e3c 100644 --- a/src/coreclr/src/System.Private.CoreLib/src/System/RuntimeHandles.cs +++ b/src/coreclr/src/System.Private.CoreLib/src/System/RuntimeHandles.cs @@ -214,12 +214,13 @@ internal static bool HasElementType(RuntimeType type) // constructor are an academic problem. Valuetypes with no constructors are a problem, // but IL Linker currently treats them as always implicitly boxed. [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.NonPublicConstructors)] RuntimeType type, - out MethodTable* pMT, bool forGetUninitializedObject) + out MethodTable* pMT, bool forGetUninitializedObject, bool wrapExceptions) { Debug.Assert(type != null); delegate* pNewobjHelperTemp = null; MethodTable* pMTTemp = null; + Interop.BOOL fFailedWhileRunningCctor = Interop.BOOL.FALSE; try { @@ -227,27 +228,29 @@ internal static bool HasElementType(RuntimeType type) new QCallTypeHandle(ref type), &pNewobjHelperTemp, &pMTTemp, - forGetUninitializedObject ? Interop.BOOL.TRUE : Interop.BOOL.FALSE); + forGetUninitializedObject ? Interop.BOOL.TRUE : Interop.BOOL.FALSE, + &fFailedWhileRunningCctor); } catch (Exception ex) { - // If the inner exception is populated, probably a failed cctor, and we should - // propagate the exception as-is. If the exception is a type that we understand, - // we should make the error message friendlier so as to include the name of the type - // that couldn't be instantiated. + // If the cctor failed, propagate the exception as-is, wrapping in a TIE + // if needed. Otherwise, make the error message friendlier by including + // the name of the type that couldn't be instantiated. - if (ex.InnerException is null) + if (fFailedWhileRunningCctor != Interop.BOOL.FALSE) { - string friendlyMessage = SR.Format(SR.ActivatorCache_CannotGetAllocator, type, ex.Message); - - switch (ex) - { - case ArgumentException: throw new ArgumentException(friendlyMessage); - case NotSupportedException: throw new NotSupportedException(friendlyMessage); - case MethodAccessException: throw new MethodAccessException(friendlyMessage); - case MissingMethodException: throw new MissingMethodException(friendlyMessage); - case MemberAccessException: throw new MemberAccessException(friendlyMessage); - } + if (wrapExceptions) throw new TargetInvocationException(ex); + else throw; // rethrow original, no TIE + } + + string friendlyMessage = SR.Format(SR.ActivatorCache_CannotGetAllocator, type, ex.Message); + switch (ex) + { + case ArgumentException: throw new ArgumentException(friendlyMessage); + case NotSupportedException: throw new NotSupportedException(friendlyMessage); + case MethodAccessException: throw new MethodAccessException(friendlyMessage); + case MissingMethodException: throw new MissingMethodException(friendlyMessage); + case MemberAccessException: throw new MemberAccessException(friendlyMessage); } throw; // can't make a friendlier message, rethrow original exception @@ -258,7 +261,7 @@ internal static bool HasElementType(RuntimeType type) } [DllImport(RuntimeHelpers.QCall, CharSet = CharSet.Unicode)] - private static extern void GetAllocatorFtn(QCallTypeHandle typeHandle, delegate** ppNewobjHelper, MethodTable** ppMT, Interop.BOOL fGetUninitializedObject); + private static extern void GetAllocatorFtn(QCallTypeHandle typeHandle, delegate** ppNewobjHelper, MethodTable** ppMT, Interop.BOOL fGetUninitializedObject, Interop.BOOL* pfFailedWhileRunningCctor); /// /// Returns the MethodDesc* for this type's parameterless instance ctor. diff --git a/src/coreclr/src/System.Private.CoreLib/src/System/RuntimeType.CoreCLR.cs b/src/coreclr/src/System.Private.CoreLib/src/System/RuntimeType.CoreCLR.cs index 8545a205015b1..3da5023d8215e 100644 --- a/src/coreclr/src/System.Private.CoreLib/src/System/RuntimeType.CoreCLR.cs +++ b/src/coreclr/src/System.Private.CoreLib/src/System/RuntimeType.CoreCLR.cs @@ -3966,7 +3966,7 @@ private void CreateInstanceCheckThis() if (GenericCache is not ActivatorCache cache) { - cache = new ActivatorCache(this); + cache = new ActivatorCache(this, wrapExceptions); GenericCache = cache; } diff --git a/src/coreclr/src/vm/reflectioninvocation.cpp b/src/coreclr/src/vm/reflectioninvocation.cpp index f3b41d1314422..82cf331b1dddc 100644 --- a/src/coreclr/src/vm/reflectioninvocation.cpp +++ b/src/coreclr/src/vm/reflectioninvocation.cpp @@ -2012,14 +2012,17 @@ void QCALLTYPE RuntimeTypeHandle::GetAllocatorFtn( QCall::TypeHandle pTypeHandle, PCODE* ppNewobjHelper, MethodTable** ppMT, - BOOL fGetUninitializedObject) + BOOL fGetUninitializedObject, + BOOL* pfFailedWhileRunningCctor) { CONTRACTL{ QCALL_CHECK; PRECONDITION(CheckPointer(ppNewobjHelper)); PRECONDITION(CheckPointer(ppMT)); + PRECONDITION(CheckPointer(pfFailedWhileRunningCctor)); PRECONDITION(*ppNewobjHelper == NULL); PRECONDITION(*ppMT == NULL); + PRECONDITION(!*pfFailedWhileRunningCctor); } CONTRACTL_END; @@ -2114,7 +2117,9 @@ void QCALLTYPE RuntimeTypeHandle::GetAllocatorFtn( // Run the type's cctor if needed (if not marked beforefieldinit) if (pMT->HasPreciseInitCctors()) { + *pfFailedWhileRunningCctor = TRUE; pMT->CheckRunClassInitAsIfConstructingThrowing(); + *pfFailedWhileRunningCctor = FALSE; } // And we're done! diff --git a/src/coreclr/src/vm/runtimehandles.h b/src/coreclr/src/vm/runtimehandles.h index 609615b7edd64..1bcecf8154e64 100644 --- a/src/coreclr/src/vm/runtimehandles.h +++ b/src/coreclr/src/vm/runtimehandles.h @@ -124,7 +124,7 @@ class RuntimeTypeHandle { // Static method on RuntimeTypeHandle static - void QCALLTYPE GetAllocatorFtn(QCall::TypeHandle pTypeHandle, PCODE* ppNewobjHelper, MethodTable** ppMT, BOOL fGetUninitializedObject); + void QCALLTYPE GetAllocatorFtn(QCall::TypeHandle pTypeHandle, PCODE* ppNewobjHelper, MethodTable** ppMT, BOOL fGetUninitializedObject, BOOL* pfFailedWhileRunningCctor); static MethodDesc* QCALLTYPE GetDefaultCtor(QCall::TypeHandle pTypeHandle, BOOL fForceBoxedEntryPoint); From 55aa1289b3acb969238a1304926b80d5d331e27d Mon Sep 17 00:00:00 2001 From: Levi Broderick Date: Thu, 19 Nov 2020 23:29:18 -0800 Subject: [PATCH 29/64] Update unit tests --- .../CompilerServices/RuntimeHelpers.CoreCLR.cs | 2 +- src/coreclr/src/vm/reflectioninvocation.cpp | 2 +- .../System.Runtime/tests/System/ActivatorTests.cs | 12 ++++++------ .../Runtime/CompilerServices/RuntimeHelpersTests.cs | 2 +- 4 files changed, 9 insertions(+), 9 deletions(-) diff --git a/src/coreclr/src/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.CoreCLR.cs b/src/coreclr/src/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.CoreCLR.cs index b74fd6f0c4050..fed423b44ec6d 100644 --- a/src/coreclr/src/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.CoreCLR.cs +++ b/src/coreclr/src/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.CoreCLR.cs @@ -140,8 +140,8 @@ private static unsafe object GetUninitializedObjectInternal( [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.NonPublicConstructors)] Type type) { + Debug.Assert(type != null); RuntimeType rt = (RuntimeType)type; - Debug.Assert(rt != null); // If type is Nullable, returns the allocator and MethodTable* for the underlying T. delegate* pfnAllocator = RuntimeTypeHandle.GetAllocatorFtn(rt, out MethodTable* pMT, forGetUninitializedObject: true, wrapExceptions: false); diff --git a/src/coreclr/src/vm/reflectioninvocation.cpp b/src/coreclr/src/vm/reflectioninvocation.cpp index 82cf331b1dddc..e2b27cea88cf0 100644 --- a/src/coreclr/src/vm/reflectioninvocation.cpp +++ b/src/coreclr/src/vm/reflectioninvocation.cpp @@ -2033,7 +2033,7 @@ void QCALLTYPE RuntimeTypeHandle::GetAllocatorFtn( // Don't allow void if (typeHandle.GetSignatureCorElementType() == ELEMENT_TYPE_VOID) { - COMPlusThrow(kArgumentException, W("NotSupported_Type")); + COMPlusThrow(kNotSupportedException, W("NotSupported_Type")); } // Don't allow generic variables (e.g., the 'T' from List) diff --git a/src/libraries/System.Runtime/tests/System/ActivatorTests.cs b/src/libraries/System.Runtime/tests/System/ActivatorTests.cs index 05e07447a9616..e497b64291bb2 100644 --- a/src/libraries/System.Runtime/tests/System/ActivatorTests.cs +++ b/src/libraries/System.Runtime/tests/System/ActivatorTests.cs @@ -150,17 +150,17 @@ public void CreateInstance_ContainsGenericParameters_ThrowsArgumentException(Typ public static IEnumerable CreateInstance_InvalidType_TestData() { - yield return new object[] { typeof(void) }; - yield return new object[] { typeof(void).MakeArrayType() }; - yield return new object[] { typeof(ArgIterator) }; + yield return new object[] { typeof(void), typeof(NotSupportedException) }; + yield return new object[] { typeof(void).MakeArrayType(), typeof(MissingMethodException) }; + yield return new object[] { typeof(ArgIterator), typeof(NotSupportedException) }; } [Theory] [MemberData(nameof(CreateInstance_InvalidType_TestData))] - public void CreateInstance_InvalidType_ThrowsNotSupportedException(Type type) + public void CreateInstance_InvalidType_ThrowsNotSupportedException(Type typeToActivate, Type expectedExceptionType) { - Assert.Throws(() => Activator.CreateInstance(type)); - Assert.Throws(() => Activator.CreateInstance(type, new object[0])); + Assert.Throws(expectedExceptionType, () => Activator.CreateInstance(typeToActivate)); + Assert.Throws(expectedExceptionType, () => Activator.CreateInstance(typeToActivate, new object[0])); } [Fact] diff --git a/src/libraries/System.Runtime/tests/System/Runtime/CompilerServices/RuntimeHelpersTests.cs b/src/libraries/System.Runtime/tests/System/Runtime/CompilerServices/RuntimeHelpersTests.cs index 12620e947c06b..1653850fcd8e9 100644 --- a/src/libraries/System.Runtime/tests/System/Runtime/CompilerServices/RuntimeHelpersTests.cs +++ b/src/libraries/System.Runtime/tests/System/Runtime/CompilerServices/RuntimeHelpersTests.cs @@ -211,7 +211,7 @@ public static IEnumerable GetUninitializedObject_NegativeTestCases() yield return new[] { typeof(Delegate), typeof(MemberAccessException) }; // abstract type - yield return new[] { typeof(void), typeof(ArgumentException) }; // explicit block in place + yield return new[] { typeof(void), typeof(NotSupportedException) }; // explicit block in place yield return new[] { typeof(int).MakePointerType(), typeof(ArgumentException) }; // pointer typedesc yield return new[] { typeof(int).MakeByRefType(), typeof(ArgumentException) }; // byref typedesc From 0ef275c43f2e31fc0c8b4db7f97218a14c5f005b Mon Sep 17 00:00:00 2001 From: Levi Broderick Date: Thu, 19 Nov 2020 23:47:41 -0800 Subject: [PATCH 30/64] Call CreateInstanceCheckThis for better exception handling --- .../src/System/ActivatorCache.CoreCLR.cs | 6 ++++++ src/coreclr/src/vm/reflectioninvocation.cpp | 13 +++---------- .../System.Runtime/tests/System/ActivatorTests.cs | 12 ++++++------ .../Runtime/CompilerServices/RuntimeHelpersTests.cs | 2 +- 4 files changed, 16 insertions(+), 17 deletions(-) diff --git a/src/coreclr/src/System.Private.CoreLib/src/System/ActivatorCache.CoreCLR.cs b/src/coreclr/src/System.Private.CoreLib/src/System/ActivatorCache.CoreCLR.cs index 62582b28200ad..1d789aa555581 100644 --- a/src/coreclr/src/System.Private.CoreLib/src/System/ActivatorCache.CoreCLR.cs +++ b/src/coreclr/src/System.Private.CoreLib/src/System/ActivatorCache.CoreCLR.cs @@ -40,6 +40,12 @@ internal ActivatorCache( _originalRuntimeType = new WeakReference(rt); #endif + // The check below is redundant since these same checks are performed at the + // unmanaged layer, but this call will throw slightly different exceptions + // than the unmanaged layer, and callers might be dependent on this. + + rt.CreateInstanceCheckThis(); + _pfnAllocator = (delegate*)RuntimeTypeHandle.GetAllocatorFtn(rt, out MethodTable* pMT, forGetUninitializedObject: false, wrapExceptions); _allocatorFirstArg = (IntPtr)pMT; diff --git a/src/coreclr/src/vm/reflectioninvocation.cpp b/src/coreclr/src/vm/reflectioninvocation.cpp index e2b27cea88cf0..9cbfdd190e769 100644 --- a/src/coreclr/src/vm/reflectioninvocation.cpp +++ b/src/coreclr/src/vm/reflectioninvocation.cpp @@ -2033,14 +2033,7 @@ void QCALLTYPE RuntimeTypeHandle::GetAllocatorFtn( // Don't allow void if (typeHandle.GetSignatureCorElementType() == ELEMENT_TYPE_VOID) { - COMPlusThrow(kNotSupportedException, W("NotSupported_Type")); - } - - // Don't allow generic variables (e.g., the 'T' from List) - // or open generic types (List<>) - Activator variation of check. - if (!fGetUninitializedObject && typeHandle.ContainsGenericVariables()) - { - COMPlusThrow(kArgumentException, W("Acc_CreateGeneric")); + COMPlusThrow(kArgumentException, W("NotSupported_Type")); } // Don't allow arrays, pointers, byrefs, or function pointers @@ -2076,8 +2069,8 @@ void QCALLTYPE RuntimeTypeHandle::GetAllocatorFtn( } // Don't allow generic variables (e.g., the 'T' from List) - // or open generic types (List<>) - FormatterServices variation of check. - if (fGetUninitializedObject && typeHandle.ContainsGenericVariables()) + // or open generic types (List<>). + if (typeHandle.ContainsGenericVariables()) { COMPlusThrow(kMemberAccessException, W("Acc_CreateGeneric")); } diff --git a/src/libraries/System.Runtime/tests/System/ActivatorTests.cs b/src/libraries/System.Runtime/tests/System/ActivatorTests.cs index e497b64291bb2..05e07447a9616 100644 --- a/src/libraries/System.Runtime/tests/System/ActivatorTests.cs +++ b/src/libraries/System.Runtime/tests/System/ActivatorTests.cs @@ -150,17 +150,17 @@ public void CreateInstance_ContainsGenericParameters_ThrowsArgumentException(Typ public static IEnumerable CreateInstance_InvalidType_TestData() { - yield return new object[] { typeof(void), typeof(NotSupportedException) }; - yield return new object[] { typeof(void).MakeArrayType(), typeof(MissingMethodException) }; - yield return new object[] { typeof(ArgIterator), typeof(NotSupportedException) }; + yield return new object[] { typeof(void) }; + yield return new object[] { typeof(void).MakeArrayType() }; + yield return new object[] { typeof(ArgIterator) }; } [Theory] [MemberData(nameof(CreateInstance_InvalidType_TestData))] - public void CreateInstance_InvalidType_ThrowsNotSupportedException(Type typeToActivate, Type expectedExceptionType) + public void CreateInstance_InvalidType_ThrowsNotSupportedException(Type type) { - Assert.Throws(expectedExceptionType, () => Activator.CreateInstance(typeToActivate)); - Assert.Throws(expectedExceptionType, () => Activator.CreateInstance(typeToActivate, new object[0])); + Assert.Throws(() => Activator.CreateInstance(type)); + Assert.Throws(() => Activator.CreateInstance(type, new object[0])); } [Fact] diff --git a/src/libraries/System.Runtime/tests/System/Runtime/CompilerServices/RuntimeHelpersTests.cs b/src/libraries/System.Runtime/tests/System/Runtime/CompilerServices/RuntimeHelpersTests.cs index 1653850fcd8e9..12620e947c06b 100644 --- a/src/libraries/System.Runtime/tests/System/Runtime/CompilerServices/RuntimeHelpersTests.cs +++ b/src/libraries/System.Runtime/tests/System/Runtime/CompilerServices/RuntimeHelpersTests.cs @@ -211,7 +211,7 @@ public static IEnumerable GetUninitializedObject_NegativeTestCases() yield return new[] { typeof(Delegate), typeof(MemberAccessException) }; // abstract type - yield return new[] { typeof(void), typeof(NotSupportedException) }; // explicit block in place + yield return new[] { typeof(void), typeof(ArgumentException) }; // explicit block in place yield return new[] { typeof(int).MakePointerType(), typeof(ArgumentException) }; // pointer typedesc yield return new[] { typeof(int).MakeByRefType(), typeof(ArgumentException) }; // byref typedesc From 97f5f606e887bca6e4532b292d495de62acdef97 Mon Sep 17 00:00:00 2001 From: Levi Broderick Date: Fri, 20 Nov 2020 00:27:57 -0800 Subject: [PATCH 31/64] Fix build breaks --- .../System/Collections/Generic/ArraySortHelper.CoreCLR.cs | 4 ++-- .../src/System/Reflection/CustomAttribute.cs | 2 +- .../src/System/Runtime/InteropServices/Marshal.CoreCLR.cs | 2 +- .../src/System/RuntimeType.CoreCLR.cs | 7 +++++-- src/coreclr/src/vm/reflectioninvocation.cpp | 4 ++++ .../src/System/Activator.RuntimeType.cs | 4 ++-- 6 files changed, 15 insertions(+), 8 deletions(-) diff --git a/src/coreclr/src/System.Private.CoreLib/src/System/Collections/Generic/ArraySortHelper.CoreCLR.cs b/src/coreclr/src/System.Private.CoreLib/src/System/Collections/Generic/ArraySortHelper.CoreCLR.cs index be5fc2874c56c..0422905c95679 100644 --- a/src/coreclr/src/System.Private.CoreLib/src/System/Collections/Generic/ArraySortHelper.CoreCLR.cs +++ b/src/coreclr/src/System.Private.CoreLib/src/System/Collections/Generic/ArraySortHelper.CoreCLR.cs @@ -27,7 +27,7 @@ private static IArraySortHelper CreateArraySortHelper() if (typeof(IComparable).IsAssignableFrom(typeof(T))) { - defaultArraySortHelper = (IArraySortHelper)typeof(GenericArraySortHelper).TypeHandle.Instantiate(new Type[] { typeof(T) }).CreateInstanceDefaultCtor(publicOnly: false, wrapExceptions: false)!; + defaultArraySortHelper = (IArraySortHelper)typeof(GenericArraySortHelper).TypeHandle.Instantiate(new Type[] { typeof(T) }).CreateInstanceDefaultCtor(publicOnly: false, skipCheckThis: true, fillCache: false, wrapExceptions: false)!; } else { @@ -62,7 +62,7 @@ private static IArraySortHelper CreateArraySortHelper() if (typeof(IComparable).IsAssignableFrom(typeof(TKey))) { - defaultArraySortHelper = (IArraySortHelper)typeof(GenericArraySortHelper).TypeHandle.Instantiate(new Type[] { typeof(TKey), typeof(TValue) }).CreateInstanceDefaultCtor(publicOnly: false, wrapExceptions: false)!; + defaultArraySortHelper = (IArraySortHelper)typeof(GenericArraySortHelper).TypeHandle.Instantiate(new Type[] { typeof(TKey), typeof(TValue) }).CreateInstanceDefaultCtor(publicOnly: false, skipCheckThis: true, fillCache: false, wrapExceptions: false)!; } else { diff --git a/src/coreclr/src/System.Private.CoreLib/src/System/Reflection/CustomAttribute.cs b/src/coreclr/src/System.Private.CoreLib/src/System/Reflection/CustomAttribute.cs index 33df3dfc35af8..41534ab065183 100644 --- a/src/coreclr/src/System.Private.CoreLib/src/System/Reflection/CustomAttribute.cs +++ b/src/coreclr/src/System.Private.CoreLib/src/System/Reflection/CustomAttribute.cs @@ -1228,7 +1228,7 @@ private static void AddCustomAttributes( } else { - attribute = attributeType.CreateInstanceDefaultCtor(publicOnly: false, wrapExceptions: false)!; + attribute = attributeType.CreateInstanceDefaultCtor(publicOnly: false, skipCheckThis: true, fillCache: true, wrapExceptions: false)!; // It is allowed by the ECMA spec to have an empty signature blob int blobLen = (int)((byte*)blobEnd - (byte*)blobStart); diff --git a/src/coreclr/src/System.Private.CoreLib/src/System/Runtime/InteropServices/Marshal.CoreCLR.cs b/src/coreclr/src/System.Private.CoreLib/src/System/Runtime/InteropServices/Marshal.CoreCLR.cs index cfa743db019e2..8bf969b604081 100644 --- a/src/coreclr/src/System.Private.CoreLib/src/System/Runtime/InteropServices/Marshal.CoreCLR.cs +++ b/src/coreclr/src/System.Private.CoreLib/src/System/Runtime/InteropServices/Marshal.CoreCLR.cs @@ -200,7 +200,7 @@ private static void PrelinkCore(MethodInfo m) private static object PtrToStructureHelper(IntPtr ptr, Type structureType) { var rt = (RuntimeType)structureType; - object structure = rt.CreateInstanceDefaultCtor(publicOnly: false, wrapExceptions: true)!; + object structure = rt.CreateInstanceDefaultCtor(publicOnly: false, skipCheckThis: false, fillCache: false, wrapExceptions: true)!; PtrToStructureHelper(ptr, structure, allowValueClasses: true); return structure; } diff --git a/src/coreclr/src/System.Private.CoreLib/src/System/RuntimeType.CoreCLR.cs b/src/coreclr/src/System.Private.CoreLib/src/System/RuntimeType.CoreCLR.cs index 3da5023d8215e..2dfc8295979be 100644 --- a/src/coreclr/src/System.Private.CoreLib/src/System/RuntimeType.CoreCLR.cs +++ b/src/coreclr/src/System.Private.CoreLib/src/System/RuntimeType.CoreCLR.cs @@ -3883,7 +3883,7 @@ private void CreateInstanceCheckThis() if (args.Length == 0 && (bindingAttr & BindingFlags.Public) != 0 && (bindingAttr & BindingFlags.Instance) != 0 && (IsGenericCOMObjectImpl() || IsValueType)) { - instance = CreateInstanceDefaultCtor(publicOnly, wrapExceptions); + instance = CreateInstanceDefaultCtor(publicOnly, skipCheckThis: false, fillCache: true, wrapExceptions); } else { @@ -3959,10 +3959,13 @@ private void CreateInstanceCheckThis() Justification = "Implementation detail of Activator that linker intrinsically recognizes")] [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2085:UnrecognizedReflectionPattern", Justification = "Implementation detail of Activator that linker intrinsically recognizes")] - internal object? CreateInstanceDefaultCtor(bool publicOnly, bool wrapExceptions) + internal object? CreateInstanceDefaultCtor(bool publicOnly, bool skipCheckThis, bool fillCache, bool wrapExceptions) { // Get or create the cached factory. Creating the cache will fail if one // of our invariant checks fails; e.g., no appropriate ctor found. + // + // n.b. In coreclr we ignore 'skipCheckThis' (assumed to be false) + // and 'fillCache' (assumed to be true). if (GenericCache is not ActivatorCache cache) { diff --git a/src/coreclr/src/vm/reflectioninvocation.cpp b/src/coreclr/src/vm/reflectioninvocation.cpp index 9cbfdd190e769..fe8fe2a6220d4 100644 --- a/src/coreclr/src/vm/reflectioninvocation.cpp +++ b/src/coreclr/src/vm/reflectioninvocation.cpp @@ -2192,7 +2192,11 @@ void QCALLTYPE RuntimeTypeHandle::AllocateComObject( if (!allocated) { +#ifdef FEATURE_COMINTEROP COMPlusThrow(kInvalidComObjectException, IDS_EE_NO_BACKING_CLASS_FACTORY); +#else // FEATURE_COMINTEROP + COMPlusThrow(kPlatformNotSupportedException, IDS_EE_NO_BACKING_CLASS_FACTORY); +#endif // FEATURE_COMINTEROP } END_QCALL; diff --git a/src/libraries/System.Private.CoreLib/src/System/Activator.RuntimeType.cs b/src/libraries/System.Private.CoreLib/src/System/Activator.RuntimeType.cs index 7607ec2ea838e..2f54f175199ee 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Activator.RuntimeType.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Activator.RuntimeType.cs @@ -92,7 +92,7 @@ public static partial class Activator throw new ArgumentNullException(nameof(type)); if (type.UnderlyingSystemType is RuntimeType rt) - return rt.CreateInstanceDefaultCtor(publicOnly: !nonPublic, wrapExceptions: wrapExceptions); + return rt.CreateInstanceDefaultCtor(publicOnly: !nonPublic, skipCheckThis: false, fillCache: true, wrapExceptions: wrapExceptions); throw new ArgumentException(SR.Arg_MustBeType, nameof(type)); } @@ -148,7 +148,7 @@ public static partial class Activator [System.Runtime.CompilerServices.Intrinsic] public static T CreateInstance<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)]T>() { - return (T)((RuntimeType)typeof(T)).CreateInstanceDefaultCtor(publicOnly: true, wrapExceptions: true)!; + return (T)((RuntimeType)typeof(T)).CreateInstanceDefaultCtor(publicOnly: true, skipCheckThis: true, fillCache: true, wrapExceptions: true)!; } private static T CreateDefaultInstance() where T : struct => default; From 2e440139fe6d65813e31984f775fc425f4d31767 Mon Sep 17 00:00:00 2001 From: Levi Broderick Date: Fri, 20 Nov 2020 00:31:59 -0800 Subject: [PATCH 32/64] Fix strings.resx whitespace --- .../src/Resources/Strings.resx | 54 +++++++++---------- 1 file changed, 27 insertions(+), 27 deletions(-) diff --git a/src/libraries/System.Private.CoreLib/src/Resources/Strings.resx b/src/libraries/System.Private.CoreLib/src/Resources/Strings.resx index ae5144f7abed5..0f07171f4475a 100644 --- a/src/libraries/System.Private.CoreLib/src/Resources/Strings.resx +++ b/src/libraries/System.Private.CoreLib/src/Resources/Strings.resx @@ -1,17 +1,17 @@  - From bead4c6565e75a818bf184874967bd2a3366787f Mon Sep 17 00:00:00 2001 From: Levi Broderick Date: Fri, 20 Nov 2020 14:38:04 -0800 Subject: [PATCH 33/64] PR feedback --- .../src/System/ActivatorCache.CoreCLR.cs | 12 ++++++++---- .../src/System/RuntimeHandles.cs | 2 +- .../src/Resources/Strings.resx | 2 +- 3 files changed, 10 insertions(+), 6 deletions(-) diff --git a/src/coreclr/src/System.Private.CoreLib/src/System/ActivatorCache.CoreCLR.cs b/src/coreclr/src/System.Private.CoreLib/src/System/ActivatorCache.CoreCLR.cs index 1d789aa555581..26c00613dcff2 100644 --- a/src/coreclr/src/System.Private.CoreLib/src/System/ActivatorCache.CoreCLR.cs +++ b/src/coreclr/src/System.Private.CoreLib/src/System/ActivatorCache.CoreCLR.cs @@ -27,7 +27,7 @@ private sealed unsafe class ActivatorCache private readonly delegate* _pfnCtor; #if DEBUG - private readonly WeakReference _originalRuntimeType; // don't prevent the RT from being collected + private readonly RuntimeType _originalRuntimeType; #endif internal ActivatorCache( @@ -37,7 +37,7 @@ internal ActivatorCache( Debug.Assert(rt != null); #if DEBUG - _originalRuntimeType = new WeakReference(rt); + _originalRuntimeType = rt; #endif // The check below is redundant since these same checks are performed at the @@ -148,8 +148,12 @@ static void CtorNoopStub(object? uninitializedObject) { } // as the object itself will keep the type alive. #if DEBUG - Debug.Assert(_originalRuntimeType.TryGetTarget(out RuntimeType? originalRT) && originalRT == rt, - "Caller passed the wrong RuntimeType to this routine."); + if (_originalRuntimeType != rt) + { + Debug.Fail("Caller passed the wrong RuntimeType to this routine." + + Environment.NewLineConst + "Expected: " + (_originalRuntimeType ?? (object)"") + + Environment.NewLineConst + "Actual: " + (rt ?? (object)"")); + } #endif object? retVal = _pfnAllocator(_allocatorFirstArg); diff --git a/src/coreclr/src/System.Private.CoreLib/src/System/RuntimeHandles.cs b/src/coreclr/src/System.Private.CoreLib/src/System/RuntimeHandles.cs index 7903d79a09e3c..3d00b42d004ee 100644 --- a/src/coreclr/src/System.Private.CoreLib/src/System/RuntimeHandles.cs +++ b/src/coreclr/src/System.Private.CoreLib/src/System/RuntimeHandles.cs @@ -243,7 +243,7 @@ internal static bool HasElementType(RuntimeType type) else throw; // rethrow original, no TIE } - string friendlyMessage = SR.Format(SR.ActivatorCache_CannotGetAllocator, type, ex.Message); + string friendlyMessage = SR.Format(SR.Activator_CannotCreateInstance, type, ex.Message); switch (ex) { case ArgumentException: throw new ArgumentException(friendlyMessage); diff --git a/src/libraries/System.Private.CoreLib/src/Resources/Strings.resx b/src/libraries/System.Private.CoreLib/src/Resources/Strings.resx index 0f07171f4475a..6b97656fdf7ce 100644 --- a/src/libraries/System.Private.CoreLib/src/Resources/Strings.resx +++ b/src/libraries/System.Private.CoreLib/src/Resources/Strings.resx @@ -3775,7 +3775,7 @@ CodeBase is not supported on assemblies loaded from a single-file bundle. - + Cannot dynamically create an instance of type '{0}'. Reason: {1} From 8f12dd8c3903ee0cf3d10afd530f8f29f1795da3 Mon Sep 17 00:00:00 2001 From: Levi Broderick Date: Fri, 20 Nov 2020 16:48:45 -0800 Subject: [PATCH 34/64] Fix GC hole in AllocateComObject --- src/coreclr/src/vm/reflectioninvocation.cpp | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/src/coreclr/src/vm/reflectioninvocation.cpp b/src/coreclr/src/vm/reflectioninvocation.cpp index fe8fe2a6220d4..4e2354098cee2 100644 --- a/src/coreclr/src/vm/reflectioninvocation.cpp +++ b/src/coreclr/src/vm/reflectioninvocation.cpp @@ -2169,22 +2169,27 @@ void QCALLTYPE RuntimeTypeHandle::AllocateComObject( { QCALL_CONTRACT; - REFLECTCLASSBASEREF refThis = (REFLECTCLASSBASEREF)refRuntimeType.Get(); bool allocated = false; BEGIN_QCALL; #ifdef FEATURE_COMINTEROP #ifdef FEATURE_COMINTEROP_UNMANAGED_ACTIVATION - if (IsComObjectClass(refThis->GetType())) { - SyncBlock* pSyncBlock = refThis->GetSyncBlock(); + GCX_COOP(); + REFLECTCLASSBASEREF refThis = (REFLECTCLASSBASEREF)refRuntimeType.Get(); - void* pClassFactory = (void*)pSyncBlock->GetInteropInfo()->GetComClassFactory(); - if (pClassFactory) + if (IsComObjectClass(refThis->GetType())) { - retInstance.Set(((ComClassFactory*)pClassFactory)->CreateInstance(NULL)); - allocated = true; + InteropSyncBlockInfo* pSyncBlockInfo = refThis->GetSyncBlock()->GetInteropInfo(); + refThis = NULL; // GetInteropInfo might trigger GC, assume 'refThis' ref now invalid + + void* pClassFactory = (void*)pSyncBlockInfo->GetComClassFactory(); + if (pClassFactory) + { + retInstance.Set(((ComClassFactory*)pClassFactory)->CreateInstance(NULL)); + allocated = true; + } } } #endif // FEATURE_COMINTEROP_UNMANAGED_ACTIVATION From 3a2021dbb7cfc98e04d5deec51ea164bff015c4c Mon Sep 17 00:00:00 2001 From: Levi Broderick Date: Sat, 21 Nov 2020 16:11:01 -0800 Subject: [PATCH 35/64] Refactor GetActivationInfo --- src/coreclr/src/vm/reflectioninvocation.cpp | 226 +++++++++++--------- src/coreclr/src/vm/runtimehandles.h | 19 +- 2 files changed, 135 insertions(+), 110 deletions(-) diff --git a/src/coreclr/src/vm/reflectioninvocation.cpp b/src/coreclr/src/vm/reflectioninvocation.cpp index 4e2354098cee2..73014def0c1be 100644 --- a/src/coreclr/src/vm/reflectioninvocation.cpp +++ b/src/coreclr/src/vm/reflectioninvocation.cpp @@ -2000,35 +2000,63 @@ lExit: ; FCIMPLEND /* - * Given a TypeHandle, returns the address of the NEWOBJ helper function that creates - * a zero-inited instance of this type. If NEWOBJ is not supported on this TypeHandle, - * throws an exception. If TypeHandle is a value type, the NEWOBJ helper will create - * a boxed zero-inited instance of the value type. The "fGetUninitializedObject" - * parameter dictates whether the caller is RuntimeHelpers.GetUninitializedObject or - * Activator.CreateInstance, which have different behavior w.r.t. what exceptions are - * thrown on failure and how nullables and COM types are handled. + * Given a RuntimeType, queries info on how to instantiate the object. + * pRefType - [required] the RuntimeType + * ppfnAllocator - [required, null-init] fnptr to the allocator + * mgd sig: void* -> object + * pvAllocatorFirstArg - [required, null-init] first argument to the allocator + * (normally, but not always, the MethodTable*) + * fUnwrapNullable - if true and type handle is Nullable, queries info for T + * fGetRefThisValueTypeCtor - if true and type handle is a value type, + * retrieves a ctor with mgd sig (ref T) -> void + * ppfnCtor - [optional, null-init] the instance's parameterless ctor, + * mgd sig object -> void, or null if no parameterless ctor exists + * pfCtorIsPublic - [optional, null-init] whether the parameterless ctor is public + * ppMethodTable - [required, null-init] the MethodTable* for the queried type + * ========== + * This method will not run the type's static ctor or instantiate the type. */ -void QCALLTYPE RuntimeTypeHandle::GetAllocatorFtn( - QCall::TypeHandle pTypeHandle, - PCODE* ppNewobjHelper, - MethodTable** ppMT, - BOOL fGetUninitializedObject, - BOOL* pfFailedWhileRunningCctor) +FCIMPL8(void, RuntimeTypeHandle::GetActivationInfo, + ReflectClassBaseObject* pRefType, + PCODE* ppfnAllocator, + void** pvAllocatorFirstArg, + CLR_BOOL fUnwrapNullable, + CLR_BOOL fGetRefThisValueTypeCtor, + PCODE* ppfnCtor, + CLR_BOOL* pfCtorIsPublic, + MethodTable** ppMethodTable +) { CONTRACTL{ - QCALL_CHECK; - PRECONDITION(CheckPointer(ppNewobjHelper)); - PRECONDITION(CheckPointer(ppMT)); - PRECONDITION(CheckPointer(pfFailedWhileRunningCctor)); - PRECONDITION(*ppNewobjHelper == NULL); - PRECONDITION(*ppMT == NULL); - PRECONDITION(!*pfFailedWhileRunningCctor); + FCALL_CHECK; + PRECONDITION(CheckPointer(pRefType)); + PRECONDITION(CheckPointer(ppfnAllocator)); + PRECONDITION(CheckPointer(pvAllocatorFirstArg)); + PRECONDITION(*ppfnAllocator == NULL); + PRECONDITION(*pvAllocatorFirstArg == NULL); + PRECONDITION((ppfnCtor == NULL) == (pfCtorIsPublic == NULL)); + PRECONDITION((ppfnCtor == NULL) || (*ppfnCtor == NULL)); + PRECONDITION((pfCtorIsPublic == NULL) || (!*pfCtorIsPublic)); + PRECONDITION(CheckPointer(ppMethodTable)); + PRECONDITION(*ppMethodTable == NULL); } CONTRACTL_END; - BEGIN_QCALL; + REFLECTCLASSBASEREF refType = (REFLECTCLASSBASEREF)ObjectToOBJECTREF(pRefType); + + // If caller didn't want us to locate a ctor, then they want the + // behavior of GetUninitializedObject, which prohibits COM activation + // and throws slightly different exceptions. + bool fGetUninitializedObject = (ppfnCtor != NULL); + + // If we're activating __ComObject. + bool fRequiresSpecialComActivationStub = false; + void* pClassFactory = NULL; - TypeHandle typeHandle = pTypeHandle.AsTypeHandle(); + // Various helpers we invoke can cause GC, so protect this ref + HELPER_METHOD_FRAME_BEGIN_1(refType); + + TypeHandle typeHandle = refType->GetType(); // Don't allow void if (typeHandle.GetSignatureCorElementType() == ELEMENT_TYPE_VOID) @@ -2045,8 +2073,6 @@ void QCALLTYPE RuntimeTypeHandle::GetAllocatorFtn( MethodTable* pMT = typeHandle.AsMethodTable(); PREFIX_ASSUME(pMT != NULL); - pMT->EnsureInstanceActive(); - // Don't allow creating instances of delegates if (pMT->IsDelegate()) { @@ -2085,111 +2111,103 @@ void QCALLTYPE RuntimeTypeHandle::GetAllocatorFtn( COMPlusThrow(kNotSupportedException, W("NotSupported_ByRefLike")); } - // Never allow the allocation of an unitialized ContextBoundObject derived type, these must always be created with a paired - // transparent proxy or the jit will get confused. - -#ifdef FEATURE_COMINTEROP - // COM allocation can involve the __ComObject base type (with attached CLSID) or an RCW type. - // For Activator.CreateInstance, we'll optimistically return a reference to the allocator, - // which will fail if there's not a legal CLSID associated with the requested type. - // For __ComObject, Activator.CreateInstance will special-case this and replace it with a stub. - // RuntimeHelpers.GetUninitializedObject always fails when it sees COM objects. - if (fGetUninitializedObject && pMT->IsComObjectType()) - { - COMPlusThrow(kNotSupportedException, W("NotSupported_ManagedActivation")); - } -#endif // FEATURE_COMINTEROP - - // If the caller is GetUninitializedInstance, they'll want a boxed T instead of a boxed Nullable. - // Other callers will get the MethodTable* corresponding to Nullable. - if (fGetUninitializedObject && Nullable::IsNullableType(pMT)) + // Caller passed Nullable but wanted information for T instead? + if (fUnwrapNullable && Nullable::IsNullableType(pMT)) { pMT = pMT->GetInstantiation()[0].GetMethodTable(); } - // Run the type's cctor if needed (if not marked beforefieldinit) - if (pMT->HasPreciseInitCctors()) - { - *pfFailedWhileRunningCctor = TRUE; - pMT->CheckRunClassInitAsIfConstructingThrowing(); - *pfFailedWhileRunningCctor = FALSE; - } +#ifdef FEATURE_COMINTEROP + // COM allocation can involve the __ComObject base type (with attached CLSID) or a + // VM-implemented [ComImport] class. For GetUninitializedObject, we block all COM types. + // For CreateInstance, the flowchart is: + // - For __ComObject, + // .. on Windows, bypass normal newobj logic and use ComClassFactory::CreateInstance. + // .. on non-Windows, treat as a normal class, type has no special handling in VM. + // - For [ComImport] class, treat as a normal class. VM will replace default + // ctor with COM activation logic on supported platforms, else ctor itself will PNSE. + if (pMT->IsComObjectType()) + { + if (fGetUninitializedObject) + { + COMPlusThrow(kNotSupportedException, W("NotSupported_ManagedActivation")); + } - // And we're done! - PCODE pNewobjFn = (PCODE)CEEJitInfo::getHelperFtnStatic(CEEInfo::getNewHelperStatic(pMT)); - _ASSERTE(pNewobjFn != NULL); + if (IsComObjectClass(typeHandle)) + { +#ifdef FEATURE_COMINTEROP_UNMANAGED_ACTIVATION + SyncBlock* pSyncBlock = refType->GetSyncBlock(); + pClassFactory = (void*)pSyncBlock->GetInteropInfo()->GetComClassFactory(); +#endif // FEATURE_COMINTEROP_UNMANAGED_ACTIVATION - *ppNewobjHelper = pNewobjFn; - *ppMT = pMT; + if (!pClassFactory) + { + COMPlusThrow(kInvalidComObjectException, IDS_EE_NO_BACKING_CLASS_FACTORY); + } - END_QCALL; -} + fRequiresSpecialComActivationStub = true; + } + } +#endif // FEATURE_COMINTEROP -/* - * Given a TypeHandle, returns the MethodDesc* for the default (parameterless) ctor, - * or nullptr if the parameterless ctor doesn't exist. For reference types, the parameterless - * ctor has a managed (object @this) -> void calling convention. For value types, - * the parameterless ctor has a managed (ref T @this) -> void calling convention, unless - * fForceBoxedEntryPoint is set, then managed (object @this) -> void stub is returned. - * The returned MethodDesc* is appropriately instantiated over any necessary generic args. - */ -MethodDesc* QCALLTYPE RuntimeTypeHandle::GetDefaultCtor( - QCall::TypeHandle pTypeHandle, - BOOL fForceBoxedEntryPoint) -{ - QCALL_CONTRACT; + pMT->EnsureInstanceActive(); - MethodDesc* pMethodDesc = NULL; + // All checks passed! Pass parameters back to the caller. - BEGIN_QCALL; + if (fRequiresSpecialComActivationStub) + { + // managed sig: ComClassFactory* -> object (via FCALL) + *ppfnAllocator = (PCODE)GetEEFuncEntryPoint(AllocateComObject); + *pvAllocatorFirstArg = pClassFactory; + } + else + { + // managed sig: MethodTable* -> object (via JIT helper) + *ppfnAllocator = CEEJitInfo::getHelperFtnStatic(CEEInfo::getNewHelperStatic(pMT)); + *pvAllocatorFirstArg = pMT; - TypeHandle typeHandle = pTypeHandle.AsTypeHandle(); - MethodTable* pMT = typeHandle.AsMethodTable(); - PREFIX_ASSUME(pMT != NULL); + if (ppfnCtor != NULL && pMT->HasDefaultConstructor()) + { + MethodDesc* pMD = pMT->GetDefaultConstructor(!fGetRefThisValueTypeCtor); + _ASSERTE(pMD != NULL); - pMT->EnsureInstanceActive(); - if (pMT->HasDefaultConstructor()) - { - pMethodDesc = pMT->GetDefaultConstructor(fForceBoxedEntryPoint); + pMD->EnsureActive(); + *ppfnCtor = pMD->GetMultiCallableAddrOfCode(); + *pfCtorIsPublic = pMD->IsPublic() != FALSE; + } } - END_QCALL; + *ppMethodTable = pMT; - return pMethodDesc; + HELPER_METHOD_FRAME_END(); } +FCIMPLEND /* - * Given a RuntimeType that represents __ComObject, activates an instance of the - * COM object and returns a RCW around it. Throws if activation fails or if the - * RuntimeType isn't __ComObject. + * Given a ComClassFactory*, calls the COM allocator + * and returns a RCW. */ -void QCALLTYPE RuntimeTypeHandle::AllocateComObject( - QCall::ObjectHandleOnStack refRuntimeType, - QCall::ObjectHandleOnStack retInstance) +FCIMPL1(Object*, RuntimeTypeHandle::AllocateComObject( + void* pClassFactory) { - QCALL_CONTRACT; + CONTRACTL{ + FCALL_CHECK; + PRECONDITION(CheckPointer(pClassFactory)); + } + CONTRACTL_END; + OBJECTREF rv = NULL; bool allocated = false; - BEGIN_QCALL; + HELPER_METHOD_FRAME_BEGIN_RET_1(rv); #ifdef FEATURE_COMINTEROP #ifdef FEATURE_COMINTEROP_UNMANAGED_ACTIVATION { - GCX_COOP(); - REFLECTCLASSBASEREF refThis = (REFLECTCLASSBASEREF)refRuntimeType.Get(); - - if (IsComObjectClass(refThis->GetType())) + if (pClassFactory != NULL) { - InteropSyncBlockInfo* pSyncBlockInfo = refThis->GetSyncBlock()->GetInteropInfo(); - refThis = NULL; // GetInteropInfo might trigger GC, assume 'refThis' ref now invalid - - void* pClassFactory = (void*)pSyncBlockInfo->GetComClassFactory(); - if (pClassFactory) - { - retInstance.Set(((ComClassFactory*)pClassFactory)->CreateInstance(NULL)); - allocated = true; - } + auto x = ((ComClassFactory*)pClassFactory)->CreateInstance(NULL); + allocated = true; } } #endif // FEATURE_COMINTEROP_UNMANAGED_ACTIVATION @@ -2204,8 +2222,10 @@ void QCALLTYPE RuntimeTypeHandle::AllocateComObject( #endif // FEATURE_COMINTEROP } - END_QCALL; + HELPER_METHOD_FRAME_END(); + return OBJECTREFToObject(rv); } +FCIMPLEND //************************************************************************************************* //************************************************************************************************* @@ -2328,6 +2348,8 @@ void QCALLTYPE ReflectionEnum::GetEnumValuesAndNames(QCall::TypeHandle pEnumType static_assert_no_msg(offsetof(MDDefaultValue, m_ulValue) == offsetof(MDDefaultValue, m_ullValue)); PVOID pValue = &defaultValue.m_byteValue; + auto y = ReflectionEnum::InternalGetEnumUnderlyingType; + switch (type) { case ELEMENT_TYPE_I1: value = *((INT8 *)pValue); diff --git a/src/coreclr/src/vm/runtimehandles.h b/src/coreclr/src/vm/runtimehandles.h index 1bcecf8154e64..357fd2ac50f37 100644 --- a/src/coreclr/src/vm/runtimehandles.h +++ b/src/coreclr/src/vm/runtimehandles.h @@ -123,14 +123,17 @@ class RuntimeTypeHandle { // Static method on RuntimeTypeHandle - static - void QCALLTYPE GetAllocatorFtn(QCall::TypeHandle pTypeHandle, PCODE* ppNewobjHelper, MethodTable** ppMT, BOOL fGetUninitializedObject, BOOL* pfFailedWhileRunningCctor); - - static - MethodDesc* QCALLTYPE GetDefaultCtor(QCall::TypeHandle pTypeHandle, BOOL fForceBoxedEntryPoint); - - static - void QCALLTYPE AllocateComObject(QCall::ObjectHandleOnStack refRuntimeType, QCall::ObjectHandleOnStack retInstance); + static FCDECL8(void, GetActivationInfo, + ReflectClassBaseObject* pRefType, + PCODE* ppfnAllocator, + void** pvAllocatorFirstArg, + CLR_BOOL fUnwrapNullable, + CLR_BOOL fGetRefThisValueTypeCtor, + PCODE* ppfnCtor, + CLR_BOOL* pfCtorIsPublic, + MethodTable** ppMethodTable); + + static FCDECL1(Object*, AllocateComObject, void* pClassFactory); static void QCALLTYPE MakeByRef(QCall::TypeHandle pTypeHandle, QCall::ObjectHandleOnStack retType); From 46ed0b8dd901e1ec7b4acecfb647e4be9f6e33af Mon Sep 17 00:00:00 2001 From: Levi Broderick Date: Sat, 21 Nov 2020 16:29:10 -0800 Subject: [PATCH 36/64] Refactor RunClassConstructor - Add "precise cctors only?" flag - Convert from FCALL to QCALL --- .../RuntimeHelpers.CoreCLR.cs | 18 +++++-- src/coreclr/src/vm/ecalllist.h | 2 +- src/coreclr/src/vm/reflectioninvocation.cpp | 48 +++++++++++-------- src/coreclr/src/vm/reflectioninvocation.h | 4 +- 4 files changed, 46 insertions(+), 26 deletions(-) diff --git a/src/coreclr/src/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.CoreCLR.cs b/src/coreclr/src/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.CoreCLR.cs index fed423b44ec6d..51bd31faa195b 100644 --- a/src/coreclr/src/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.CoreCLR.cs +++ b/src/coreclr/src/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.CoreCLR.cs @@ -40,14 +40,24 @@ public static partial class RuntimeHelpers // This call will generate an exception if the specified class constructor threw an // exception when it ran. - [MethodImpl(MethodImplOptions.InternalCall)] - private static extern void _RunClassConstructor(RuntimeType type); - public static void RunClassConstructor(RuntimeTypeHandle type) { - _RunClassConstructor(type.GetRuntimeType()); + RunClassConstructor(type, preciseCctorsOnly: false); + } + + // If 'preciseCctorsOnly' is true, runs only cctors *NOT* marked .beforefieldinit. + // If 'preciseCctorsOnly' is false, runs any cctor, regardless of .beforefieldinit annotation. + internal static void RunClassConstructor(RuntimeTypeHandle typeHandle, bool preciseCctorsOnly) + { + _RunClassConstructor( + new QCallTypeHandle(ref typeHandle), + (preciseCctorsOnly) ? Interop.BOOL.TRUE : Interop.BOOL.FALSE); } + [DllImport(RuntimeHelpers.QCall, CharSet = CharSet.Unicode)] + private static extern void _RunClassConstructor(QCallTypeHandle typeHandle, Interop.BOOL preciseCctorsOnly); + + // RunModuleConstructor causes the module constructor for the given type to be triggered // in the current domain. After this call returns, the module constructor is guaranteed to // have at least been started by some thread. In the absence of module constructor diff --git a/src/coreclr/src/vm/ecalllist.h b/src/coreclr/src/vm/ecalllist.h index 542198a1d8c11..c00cc74b967b8 100644 --- a/src/coreclr/src/vm/ecalllist.h +++ b/src/coreclr/src/vm/ecalllist.h @@ -870,7 +870,7 @@ FCFuncEnd() FCFuncStart(gRuntimeHelpers) FCFuncElement("GetObjectValue", ObjectNative::GetObjectValue) FCIntrinsic("InitializeArray", ArrayNative::InitializeArray, CORINFO_INTRINSIC_InitializeArray) - FCFuncElement("_RunClassConstructor", ReflectionInvocation::RunClassConstructor) + QCFuncElement("_RunClassConstructor", ReflectionInvocation::RunClassConstructor) FCFuncElement("_RunModuleConstructor", ReflectionInvocation::RunModuleConstructor) QCFuncElement("_CompileMethod", ReflectionInvocation::CompileMethod) FCFuncElement("_PrepareMethod", ReflectionInvocation::PrepareMethod) diff --git a/src/coreclr/src/vm/reflectioninvocation.cpp b/src/coreclr/src/vm/reflectioninvocation.cpp index 73014def0c1be..b7a4a8058a1ee 100644 --- a/src/coreclr/src/vm/reflectioninvocation.cpp +++ b/src/coreclr/src/vm/reflectioninvocation.cpp @@ -1585,35 +1585,43 @@ void QCALLTYPE ReflectionInvocation::CompileMethod(MethodDesc * pMD) } // This method triggers the class constructor for a give type -FCIMPL1(void, ReflectionInvocation::RunClassConstructor, ReflectClassBaseObject *pTypeUNSAFE) +void QCALLTYPE ReflectionInvocation::RunClassConstructor( + QCall::TypeHandle pTypeHandle, + BOOL fPreciseCctorsOnly) { - FCALL_CONTRACT; - - REFLECTCLASSBASEREF refType = (REFLECTCLASSBASEREF)ObjectToOBJECTREF(pTypeUNSAFE); - - if (refType == NULL) - FCThrowArgumentVoidEx(kArgumentException, NULL, W("InvalidOperation_HandleIsNotInitialized")); - - TypeHandle typeHnd = refType->GetType(); - if (typeHnd.IsTypeDesc()) - return; + QCALL_CONTRACT; - MethodTable *pMT = typeHnd.AsMethodTable(); + BEGIN_QCALL; - Assembly *pAssem = pMT->GetAssembly(); + TypeHandle typeHnd = pTypeHandle.AsTypeHandle(); + if (typeHnd.IsNull()) + COMPlusThrow(kArgumentException, W("InvalidOperation_HandleIsNotInitialized")); - if (!pMT->IsClassInited()) + if (!typeHnd.IsTypeDesc()) { - HELPER_METHOD_FRAME_BEGIN_1(refType); + MethodTable* pMT = typeHnd.AsMethodTable(); - pMT->CheckRestore(); - pMT->EnsureInstanceActive(); - pMT->CheckRunClassInitThrowing(); + Assembly* pAssem = pMT->GetAssembly(); - HELPER_METHOD_FRAME_END(); + if (!pMT->IsClassInited()) + { + pMT->CheckRestore(); + pMT->EnsureInstanceActive(); + if (fPreciseCctorsOnly) + { + // only cctors NOT marked .beforefieldinit + pMT->CheckRunClassInitAsIfConstructingThrowing(); + } + else + { + // any cctor, regardless of .beforefieldinit annotation + pMT->CheckRunClassInitThrowing(); + } + } } + + END_QCALL; } -FCIMPLEND // This method triggers the module constructor for a give module FCIMPL1(void, ReflectionInvocation::RunModuleConstructor, ReflectModuleBaseObject *pModuleUNSAFE) { diff --git a/src/coreclr/src/vm/reflectioninvocation.h b/src/coreclr/src/vm/reflectioninvocation.h index 59897aeb74665..9f85a2a5bf715 100644 --- a/src/coreclr/src/vm/reflectioninvocation.h +++ b/src/coreclr/src/vm/reflectioninvocation.h @@ -48,7 +48,9 @@ class ReflectionInvocation { static void QCALLTYPE CompileMethod(MethodDesc * pMD); - static FCDECL1(void, RunClassConstructor, ReflectClassBaseObject *pTypeUNSAFE); + static + void QCALLTYPE RunClassConstructor(QCall::TypeHandle pTypeHandle, BOOL fPreciseCctorsOnly); + static FCDECL1(void, RunModuleConstructor, ReflectModuleBaseObject *pModuleUNSAFE); static FCDECL3(void, PrepareMethod, ReflectMethodObject* pMethodUNSAFE, TypeHandle *pInstantiation, UINT32 cInstantiation); static FCDECL1(void, PrepareDelegate, Object* delegateUNSAFE); From d3bc6323523927d979b57f4abd938c7ef4930945 Mon Sep 17 00:00:00 2001 From: Levi Broderick Date: Sat, 21 Nov 2020 17:02:57 -0800 Subject: [PATCH 37/64] Update GetUninitializedObject to call the new APIs --- .../RuntimeHelpers.CoreCLR.cs | 26 +++- .../src/System/RuntimeHandles.cs | 141 ++++++++---------- src/coreclr/src/vm/ecalllist.h | 4 +- 3 files changed, 88 insertions(+), 83 deletions(-) diff --git a/src/coreclr/src/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.CoreCLR.cs b/src/coreclr/src/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.CoreCLR.cs index 51bd31faa195b..407effa236bc2 100644 --- a/src/coreclr/src/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.CoreCLR.cs +++ b/src/coreclr/src/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.CoreCLR.cs @@ -154,12 +154,25 @@ private static unsafe object GetUninitializedObjectInternal( RuntimeType rt = (RuntimeType)type; // If type is Nullable, returns the allocator and MethodTable* for the underlying T. - delegate* pfnAllocator = RuntimeTypeHandle.GetAllocatorFtn(rt, out MethodTable* pMT, forGetUninitializedObject: true, wrapExceptions: false); + RuntimeTypeHandle.GetActivationInfo(rt, forGetUninitializedInstance: true, out MethodTable* pMT, + out delegate* pfnAllocator, out void* vAllocatorFirstArg, out _, out _); + Debug.Assert(pfnAllocator != null); Debug.Assert(pMT != null); Debug.Assert(!pMT->IsNullable, "Should've unwrapped any Nullable input."); + Debug.Assert(!pMT->HasComponentSize, "Should've blocked string, array, and similar."); + + // Per ECMA-335, Sec. I.8.9.5, the instance ctor is normally responsible for + // invoking any non-.beforefieldinit static cctor. However, since we're bypassing + // instance ctors here, we need to invoke any non-.beforefieldinit static cctor + // manually, otherwise we could hand the caller a not-fully-initialized type. + + if (pMT->HasPreciseInitCctors) + { + RunClassConstructor(rt.TypeHandle, preciseCctorsOnly: true); + } - object retVal = pfnAllocator(pMT); + object retVal = pfnAllocator(vAllocatorFirstArg); GC.KeepAlive(rt); // don't allow the type to be collected before the object is instantiated return retVal; @@ -407,6 +420,7 @@ internal unsafe struct MethodTable private const uint enum_flag_ComObject = 0x40000000; private const uint enum_flag_HasComponentSize = 0x80000000; private const uint enum_flag_HasDefaultCtor = 0x00000200; + private const uint enum_flag_HasPreciseInitCctors = 0x00000400; private const uint enum_flag_HasTypeEquivalence = 0x00004000; // TODO: shouldn't this be 0x02000000? // Types that require non-trivial interface cast have this bit set in the category private const uint enum_flag_NonTrivialInterfaceCast = 0x00080000 // enum_flag_Category_Array @@ -472,6 +486,14 @@ public bool HasDefaultConstructor } } + public bool HasPreciseInitCctors + { + get + { + return !HasComponentSize && (Flags & enum_flag_HasPreciseInitCctors) != 0; + } + } + public bool IsComObject { get diff --git a/src/coreclr/src/System.Private.CoreLib/src/System/RuntimeHandles.cs b/src/coreclr/src/System.Private.CoreLib/src/System/RuntimeHandles.cs index 3d00b42d004ee..0314f6204f32c 100644 --- a/src/coreclr/src/System.Private.CoreLib/src/System/RuntimeHandles.cs +++ b/src/coreclr/src/System.Private.CoreLib/src/System/RuntimeHandles.cs @@ -203,47 +203,69 @@ internal static bool HasElementType(RuntimeType type) internal static extern object CreateInstanceForAnotherGenericParameter(RuntimeType type, RuntimeType genericParameter); /// - /// Given a RuntimeType, returns both the address of the JIT's newobj allocator helper for - /// that type and the MethodTable* corresponding to that type. Return value signature is - /// managed calli (MethodTable* pMT) -> object. + /// Given a RuntimeType, returns information about how to activate it via calli + /// semantics. This method will ensure the type object is fully initialized within + /// the VM, but it will not call any static ctors on the type. /// - internal static delegate* GetAllocatorFtn( - // This API doesn't call any constructors, but the type needs to be seen as constructed. - // A type is seen as constructed if a constructor is kept. - // This obviously won't cover a type with no constructor. Reference types with no - // constructor are an academic problem. Valuetypes with no constructors are a problem, - // but IL Linker currently treats them as always implicitly boxed. - [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.NonPublicConstructors)] RuntimeType type, - out MethodTable* pMT, bool forGetUninitializedObject, bool wrapExceptions) + internal static void GetActivationInfo( + RuntimeType rt, + bool forGetUninitializedInstance, + out MethodTable* pMT, + out delegate* pfnAllocator, + out void* vAllocatorFirstArg, + out delegate* pfnCtor, + out bool ctorIsPublic) { - Debug.Assert(type != null); + Debug.Assert(rt != null); - delegate* pNewobjHelperTemp = null; - MethodTable* pMTTemp = null; - Interop.BOOL fFailedWhileRunningCctor = Interop.BOOL.FALSE; + // Initialize all out vars + + pMT = default; + pfnAllocator = default; + vAllocatorFirstArg = default; + pfnCtor = default; + ctorIsPublic = default; + + // Get the requested activation information + // GetUninitializedInstance doesn't care about ctor information try { - GetAllocatorFtn( - new QCallTypeHandle(ref type), - &pNewobjHelperTemp, - &pMTTemp, - forGetUninitializedObject ? Interop.BOOL.TRUE : Interop.BOOL.FALSE, - &fFailedWhileRunningCctor); + delegate* pfnAllocatorTemp = default; + void* vAllocatorFirstArgTemp = default; + delegate* pfnCtorTemp = default; + bool fCtorIsPublicTemp = default; + MethodTable* pMethodTableTemp = default; + + GetActivationInfo( + rt, &pfnAllocatorTemp, &vAllocatorFirstArgTemp, + fUnwrapNullable: forGetUninitializedInstance, + fGetRefThisValueTypeCtor: false, + ppfnCtor: forGetUninitializedInstance ? null : &pfnCtorTemp, + pfCtorIsPublic: forGetUninitializedInstance ? null : &fCtorIsPublicTemp, + &pMethodTableTemp); + + // Marshal values back to caller + + Debug.Assert(pMethodTableTemp != null); + pMT = pMethodTableTemp; + + Debug.Assert(pfnAllocatorTemp != null); + pfnAllocator = pfnAllocatorTemp; + + Debug.Assert(vAllocatorFirstArgTemp != null); + vAllocatorFirstArg = vAllocatorFirstArgTemp; + + pfnCtor = pfnCtorTemp; // could be null + ctorIsPublic = fCtorIsPublicTemp; } catch (Exception ex) { - // If the cctor failed, propagate the exception as-is, wrapping in a TIE - // if needed. Otherwise, make the error message friendlier by including - // the name of the type that couldn't be instantiated. + // Exception messages coming from the runtime won't include + // the type name. Let's include it here to improve the + // debugging experience for our callers. - if (fFailedWhileRunningCctor != Interop.BOOL.FALSE) - { - if (wrapExceptions) throw new TargetInvocationException(ex); - else throw; // rethrow original, no TIE - } - - string friendlyMessage = SR.Format(SR.Activator_CannotCreateInstance, type, ex.Message); + string friendlyMessage = SR.Format(SR.Activator_CannotCreateInstance, rt, ex.Message); switch (ex) { case ArgumentException: throw new ArgumentException(friendlyMessage); @@ -255,55 +277,18 @@ internal static bool HasElementType(RuntimeType type) throw; // can't make a friendlier message, rethrow original exception } - - pMT = pMTTemp; - return pNewobjHelperTemp; - } - - [DllImport(RuntimeHelpers.QCall, CharSet = CharSet.Unicode)] - private static extern void GetAllocatorFtn(QCallTypeHandle typeHandle, delegate** ppNewobjHelper, MethodTable** ppMT, Interop.BOOL fGetUninitializedObject, Interop.BOOL* pfFailedWhileRunningCctor); - - /// - /// Returns the MethodDesc* for this type's parameterless instance ctor. - /// For reference types, signature is (object @this) -> void. - /// For value types, unboxed signature is (ref T @thisUnboxed) -> void. - /// For value types, forced boxed signature is (object @this) -> void. - /// Returns nullptr if no parameterless ctor is defined. - /// - internal static RuntimeMethodHandleInternal GetDefaultConstructor( - [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.NonPublicConstructors)] RuntimeType type, - bool forceBoxedEntryPoint) - { - Debug.Assert(type != null); - - return GetDefaultCtor(new QCallTypeHandle(ref type), (forceBoxedEntryPoint) ? Interop.BOOL.TRUE : Interop.BOOL.FALSE); - } - - [DllImport(RuntimeHelpers.QCall, CharSet = CharSet.Unicode)] - private static extern RuntimeMethodHandleInternal GetDefaultCtor(QCallTypeHandle typeHandle, Interop.BOOL forceBoxedEntryPoint); - - /// - /// Given a RuntimeType which represents __ComObject, activates the class and creates - /// a RCW around it. - /// - /// No CLSID present, or invalid CLSID. - internal static object AllocateComObject( - [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.NonPublicConstructors)] RuntimeType type) - { - Debug.Assert(type != null); - - // n.b. use ObjectHandleOnStack instead of QCallTypeHandle since runtime needs the actual RuntimeType instance, - // not just its underlying TypeHandle. - - object activatedInstance = null!; - AllocateComObject(ObjectHandleOnStack.Create(ref type), ObjectHandleOnStack.Create(ref activatedInstance)); - - Debug.Assert(activatedInstance != null); - return activatedInstance; } - [DllImport(RuntimeHelpers.QCall, CharSet = CharSet.Unicode)] - private static extern void AllocateComObject(ObjectHandleOnStack runtimeType, ObjectHandleOnStack activatedInstance); + [MethodImpl(MethodImplOptions.InternalCall)] + private static extern void GetActivationInfo( + RuntimeType pRefType, + delegate** ppfnAllocator, + void** pvAllocatorFirstArg, + bool fUnwrapNullable, + bool fGetRefThisValueTypeCtor, + delegate** ppfnCtor, + bool* pfCtorIsPublic, + MethodTable** ppMethodTable); internal RuntimeType GetRuntimeType() { diff --git a/src/coreclr/src/vm/ecalllist.h b/src/coreclr/src/vm/ecalllist.h index c00cc74b967b8..a6b0ecab2a7f8 100644 --- a/src/coreclr/src/vm/ecalllist.h +++ b/src/coreclr/src/vm/ecalllist.h @@ -238,9 +238,7 @@ FCFuncStart(gCOMTypeHandleFuncs) FCFuncElement("IsGenericTypeDefinition", RuntimeTypeHandle::IsGenericTypeDefinition) FCFuncElement("ContainsGenericVariables", RuntimeTypeHandle::ContainsGenericVariables) FCFuncElement("SatisfiesConstraints", RuntimeTypeHandle::SatisfiesConstraints) - QCFuncElement("GetAllocatorFtn", RuntimeTypeHandle::GetAllocatorFtn) - QCFuncElement("GetDefaultCtor", RuntimeTypeHandle::GetDefaultCtor) - QCFuncElement("AllocateComObject", RuntimeTypeHandle::AllocateComObject) + FCFuncElement("GetActivationInfo", RuntimeTypeHandle::GetActivationInfo) FCFuncElement("CompareCanonicalHandles", RuntimeTypeHandle::CompareCanonicalHandles) FCIntrinsic("GetValueInternal", RuntimeTypeHandle::GetValueInternal, CORINFO_INTRINSIC_RTH_GetValueInternal) FCFuncElement("IsEquivalentTo", RuntimeTypeHandle::IsEquivalentTo) From 3f362f2c7f172dce229d251efba9c4c551fb9f21 Mon Sep 17 00:00:00 2001 From: Levi Broderick Date: Sat, 21 Nov 2020 17:20:11 -0800 Subject: [PATCH 38/64] Hook up ActivatorCache to new system --- .../src/System/ActivatorCache.CoreCLR.cs | 85 ++++++------------- .../RuntimeHelpers.CoreCLR.cs | 9 -- 2 files changed, 28 insertions(+), 66 deletions(-) diff --git a/src/coreclr/src/System.Private.CoreLib/src/System/ActivatorCache.CoreCLR.cs b/src/coreclr/src/System.Private.CoreLib/src/System/ActivatorCache.CoreCLR.cs index 26c00613dcff2..d3018d51628c7 100644 --- a/src/coreclr/src/System.Private.CoreLib/src/System/ActivatorCache.CoreCLR.cs +++ b/src/coreclr/src/System.Private.CoreLib/src/System/ActivatorCache.CoreCLR.cs @@ -3,9 +3,7 @@ using System.Diagnostics; using System.Diagnostics.CodeAnalysis; -using System.Reflection; using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; namespace System { @@ -19,12 +17,12 @@ private sealed unsafe class ActivatorCache { // The managed calli to the newobj allocator, plus its first argument (MethodTable*). // In the case of the COM allocator, first arg is ComClassFactory*, not MethodTable*. - private readonly delegate* _pfnAllocator; - private readonly IntPtr _allocatorFirstArg; + private readonly delegate* _pfnAllocator; + private readonly void* _allocatorFirstArg; // The managed calli to the parameterless ctor, taking "this" (as object) as its first argument. - // For value type ctors, we'll point to a special unboxing stub. private readonly delegate* _pfnCtor; + private readonly bool _ctorIsPublic; #if DEBUG private readonly RuntimeType _originalRuntimeType; @@ -46,10 +44,11 @@ internal ActivatorCache( rt.CreateInstanceCheckThis(); - _pfnAllocator = (delegate*)RuntimeTypeHandle.GetAllocatorFtn(rt, out MethodTable* pMT, forGetUninitializedObject: false, wrapExceptions); - _allocatorFirstArg = (IntPtr)pMT; + RuntimeTypeHandle.GetActivationInfo(rt, forGetUninitializedInstance: false, + out MethodTable* pMT, out _pfnAllocator!, out _allocatorFirstArg, + out _pfnCtor!, out _ctorIsPublic); - RuntimeMethodHandleInternal ctorHandle = RuntimeMethodHandleInternal.EmptyHandle; // default nullptr + bool useNoopCtorStub = false; if (pMT->IsValueType) { @@ -57,19 +56,17 @@ internal ActivatorCache( { // Activator.CreateInstance returns null given typeof(Nullable). - static object? ReturnNull(IntPtr _) => null; + static object? ReturnNull(void* _) => null; _pfnAllocator = &ReturnNull; - } - else if (pMT->HasDefaultConstructor) - { - // Value type with an explicit default ctor; we'll ask the runtime to create - // an unboxing stub on our behalf. + _allocatorFirstArg = default; - ctorHandle = RuntimeTypeHandle.GetDefaultConstructor(rt, forceBoxedEntryPoint: true); + useNoopCtorStub = true; } - else + else if (_pfnCtor == null) { - // ValueType with no explicit parameterless ctor; assume ctor returns default(T) + // Value type with no parameterless ctor - we'll point it to our noop stub. + + useNoopCtorStub = true; } } else @@ -78,66 +75,40 @@ internal ActivatorCache( Debug.Assert(rt.IsClass); +#if FEATURE_COMINTEROP if (pMT->IsComObject) { - if (rt.IsGenericCOMObjectImpl()) + if (rt == typeof(__ComObject)) { - // This is the __ComObject base type, which means that the MethodTable* we have - // doesn't contain CLSID information. The CLSID information is instead hanging - // off of the RuntimeType's sync block. We'll set the allocator to our stub, and - // instead of a MethodTable* we'll pass in the handle to the RuntimeType. The - // handles we create live for the lifetime of the app, but that's ok since it - // matches coreclr's internal implementation anyway (see GetComClassHelper). - - [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2072:UnrecognizedReflectionPattern", - Justification = "Linker already saw this type through Activator/Type.CreateInstance.")] - static object AllocateComObject(IntPtr runtimeTypeHandle) - { - RuntimeType rt = (RuntimeType)GCHandle.FromIntPtr(runtimeTypeHandle).Target!; - Debug.Assert(rt != null); - - return RuntimeTypeHandle.AllocateComObject(rt); - } - _pfnAllocator = &AllocateComObject; - _allocatorFirstArg = GCHandle.ToIntPtr(GCHandle.Alloc(rt)); - } - - // Neither __ComObject nor any derived type gets its parameterless ctor called. - // Activation is handled entirely by the allocator. + // Base COM class - activation is handled entirely by the allocator. + // We shouldn't call the ctor fn (which points to __ComObject's ctor). - ctorHandle = default; - } - else if (!pMT->HasDefaultConstructor) - { - throw new MissingMethodException(SR.Format(SR.Arg_NoDefCTor, rt)); + useNoopCtorStub = true; + } } else +#endif + if (_pfnCtor == null) { - // Reference type with explicit parameterless ctor + // Reference type with no parameterless ctor - we cannot continue. - ctorHandle = RuntimeTypeHandle.GetDefaultConstructor(rt, forceBoxedEntryPoint: false); - Debug.Assert(!ctorHandle.IsNullHandle()); + throw new MissingMethodException(SR.Format(SR.Arg_NoDefCTor, rt)); } } - if (ctorHandle.IsNullHandle()) + if (useNoopCtorStub) { static void CtorNoopStub(object? uninitializedObject) { } _pfnCtor = &CtorNoopStub; // we use null singleton pattern if no ctor call is necessary - CtorIsPublic = true; // implicit parameterless ctor is always considered public - } - else - { - _pfnCtor = (delegate*)RuntimeMethodHandle.GetFunctionPointer(ctorHandle); - CtorIsPublic = (RuntimeMethodHandle.GetAttributes(ctorHandle) & MethodAttributes.Public) != 0; + _ctorIsPublic = true; // implicit parameterless ctor is always considered public } Debug.Assert(_pfnAllocator != null); - Debug.Assert(_allocatorFirstArg != IntPtr.Zero); + Debug.Assert(_allocatorFirstArg != null); Debug.Assert(_pfnCtor != null); // we use null singleton pattern if no ctor call is necessary } - internal bool CtorIsPublic { get; } + internal bool CtorIsPublic => _ctorIsPublic; [MethodImpl(MethodImplOptions.AggressiveInlining)] internal object? CreateUninitializedObject(RuntimeType rt) diff --git a/src/coreclr/src/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.CoreCLR.cs b/src/coreclr/src/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.CoreCLR.cs index 407effa236bc2..cd8307bbe6e6d 100644 --- a/src/coreclr/src/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.CoreCLR.cs +++ b/src/coreclr/src/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.CoreCLR.cs @@ -419,7 +419,6 @@ internal unsafe struct MethodTable private const uint enum_flag_ContainsPointers = 0x01000000; private const uint enum_flag_ComObject = 0x40000000; private const uint enum_flag_HasComponentSize = 0x80000000; - private const uint enum_flag_HasDefaultCtor = 0x00000200; private const uint enum_flag_HasPreciseInitCctors = 0x00000400; private const uint enum_flag_HasTypeEquivalence = 0x00004000; // TODO: shouldn't this be 0x02000000? // Types that require non-trivial interface cast have this bit set in the category @@ -478,14 +477,6 @@ public bool NonTrivialInterfaceCast } } - public bool HasDefaultConstructor - { - get - { - return (Flags & enum_flag_HasDefaultCtor) != 0; - } - } - public bool HasPreciseInitCctors { get From 879937b13cbce98310f0f897b4f4705cd89401a9 Mon Sep 17 00:00:00 2001 From: Levi Broderick Date: Sat, 21 Nov 2020 17:25:12 -0800 Subject: [PATCH 39/64] Fix typo causing build break --- .../src/System/ActivatorCache.CoreCLR.cs | 4 +++- src/coreclr/src/vm/reflectioninvocation.cpp | 4 ++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/src/coreclr/src/System.Private.CoreLib/src/System/ActivatorCache.CoreCLR.cs b/src/coreclr/src/System.Private.CoreLib/src/System/ActivatorCache.CoreCLR.cs index d3018d51628c7..8bc5d5463f5cc 100644 --- a/src/coreclr/src/System.Private.CoreLib/src/System/ActivatorCache.CoreCLR.cs +++ b/src/coreclr/src/System.Private.CoreLib/src/System/ActivatorCache.CoreCLR.cs @@ -96,6 +96,9 @@ internal ActivatorCache( } } + // We don't need to worry about invoking cctors here. The runtime will figure it + // out for us when the instance ctor is called. + if (useNoopCtorStub) { static void CtorNoopStub(object? uninitializedObject) { } @@ -104,7 +107,6 @@ static void CtorNoopStub(object? uninitializedObject) { } } Debug.Assert(_pfnAllocator != null); - Debug.Assert(_allocatorFirstArg != null); Debug.Assert(_pfnCtor != null); // we use null singleton pattern if no ctor call is necessary } diff --git a/src/coreclr/src/vm/reflectioninvocation.cpp b/src/coreclr/src/vm/reflectioninvocation.cpp index b7a4a8058a1ee..e40418fa1c304 100644 --- a/src/coreclr/src/vm/reflectioninvocation.cpp +++ b/src/coreclr/src/vm/reflectioninvocation.cpp @@ -2195,7 +2195,7 @@ FCIMPLEND * Given a ComClassFactory*, calls the COM allocator * and returns a RCW. */ -FCIMPL1(Object*, RuntimeTypeHandle::AllocateComObject( +FCIMPL1(Object*, RuntimeTypeHandle::AllocateComObject, void* pClassFactory) { CONTRACTL{ @@ -2214,7 +2214,7 @@ FCIMPL1(Object*, RuntimeTypeHandle::AllocateComObject( { if (pClassFactory != NULL) { - auto x = ((ComClassFactory*)pClassFactory)->CreateInstance(NULL); + rv = ((ComClassFactory*)pClassFactory)->CreateInstance(NULL); allocated = true; } } From 93bcedbd57ea0718a303d057b0eb3b03c1bad8ea Mon Sep 17 00:00:00 2001 From: Levi Broderick Date: Sat, 21 Nov 2020 18:52:10 -0800 Subject: [PATCH 40/64] Change GetActivationInfo to QCALL --- .../src/System/RuntimeHandles.cs | 26 +++++----- src/coreclr/src/vm/ecalllist.h | 2 +- src/coreclr/src/vm/reflectioninvocation.cpp | 48 +++++++++++-------- src/coreclr/src/vm/runtimehandles.h | 12 +++-- 4 files changed, 51 insertions(+), 37 deletions(-) diff --git a/src/coreclr/src/System.Private.CoreLib/src/System/RuntimeHandles.cs b/src/coreclr/src/System.Private.CoreLib/src/System/RuntimeHandles.cs index 0314f6204f32c..d623d3134e956 100644 --- a/src/coreclr/src/System.Private.CoreLib/src/System/RuntimeHandles.cs +++ b/src/coreclr/src/System.Private.CoreLib/src/System/RuntimeHandles.cs @@ -234,13 +234,14 @@ internal static void GetActivationInfo( delegate* pfnAllocatorTemp = default; void* vAllocatorFirstArgTemp = default; delegate* pfnCtorTemp = default; - bool fCtorIsPublicTemp = default; + Interop.BOOL fCtorIsPublicTemp = default; MethodTable* pMethodTableTemp = default; - GetActivationInfo( - rt, &pfnAllocatorTemp, &vAllocatorFirstArgTemp, - fUnwrapNullable: forGetUninitializedInstance, - fGetRefThisValueTypeCtor: false, + _GetActivationInfo( + new QCallTypeHandle(ref rt), ObjectHandleOnStack.Create(ref rt), + &pfnAllocatorTemp, &vAllocatorFirstArgTemp, + fUnwrapNullable: forGetUninitializedInstance ? Interop.BOOL.TRUE : Interop.BOOL.FALSE, + fForceObjectRefCtorEntryPoint: Interop.BOOL.TRUE, ppfnCtor: forGetUninitializedInstance ? null : &pfnCtorTemp, pfCtorIsPublic: forGetUninitializedInstance ? null : &fCtorIsPublicTemp, &pMethodTableTemp); @@ -257,7 +258,7 @@ internal static void GetActivationInfo( vAllocatorFirstArg = vAllocatorFirstArgTemp; pfnCtor = pfnCtorTemp; // could be null - ctorIsPublic = fCtorIsPublicTemp; + ctorIsPublic = fCtorIsPublicTemp != Interop.BOOL.FALSE; } catch (Exception ex) { @@ -279,15 +280,16 @@ internal static void GetActivationInfo( } } - [MethodImpl(MethodImplOptions.InternalCall)] - private static extern void GetActivationInfo( - RuntimeType pRefType, + [DllImport(RuntimeHelpers.QCall, CharSet = CharSet.Unicode)] + private static extern void _GetActivationInfo( + QCallTypeHandle pTypeHandle, + ObjectHandleOnStack pRuntimeType, delegate** ppfnAllocator, void** pvAllocatorFirstArg, - bool fUnwrapNullable, - bool fGetRefThisValueTypeCtor, + Interop.BOOL fUnwrapNullable, + Interop.BOOL fForceObjectRefCtorEntryPoint, delegate** ppfnCtor, - bool* pfCtorIsPublic, + Interop.BOOL* pfCtorIsPublic, MethodTable** ppMethodTable); internal RuntimeType GetRuntimeType() diff --git a/src/coreclr/src/vm/ecalllist.h b/src/coreclr/src/vm/ecalllist.h index a6b0ecab2a7f8..e081528156bfb 100644 --- a/src/coreclr/src/vm/ecalllist.h +++ b/src/coreclr/src/vm/ecalllist.h @@ -238,7 +238,7 @@ FCFuncStart(gCOMTypeHandleFuncs) FCFuncElement("IsGenericTypeDefinition", RuntimeTypeHandle::IsGenericTypeDefinition) FCFuncElement("ContainsGenericVariables", RuntimeTypeHandle::ContainsGenericVariables) FCFuncElement("SatisfiesConstraints", RuntimeTypeHandle::SatisfiesConstraints) - FCFuncElement("GetActivationInfo", RuntimeTypeHandle::GetActivationInfo) + QCFuncElement("_GetActivationInfo", RuntimeTypeHandle::GetActivationInfo) FCFuncElement("CompareCanonicalHandles", RuntimeTypeHandle::CompareCanonicalHandles) FCIntrinsic("GetValueInternal", RuntimeTypeHandle::GetValueInternal, CORINFO_INTRINSIC_RTH_GetValueInternal) FCFuncElement("IsEquivalentTo", RuntimeTypeHandle::IsEquivalentTo) diff --git a/src/coreclr/src/vm/reflectioninvocation.cpp b/src/coreclr/src/vm/reflectioninvocation.cpp index e40418fa1c304..85415414f8542 100644 --- a/src/coreclr/src/vm/reflectioninvocation.cpp +++ b/src/coreclr/src/vm/reflectioninvocation.cpp @@ -2009,14 +2009,16 @@ FCIMPLEND /* * Given a RuntimeType, queries info on how to instantiate the object. + * pTypeHandle - [required] the TypeHandle for the RuntimeType + * pRuntimeType - [required] the RuntimeType object (used for COM) * pRefType - [required] the RuntimeType * ppfnAllocator - [required, null-init] fnptr to the allocator * mgd sig: void* -> object * pvAllocatorFirstArg - [required, null-init] first argument to the allocator * (normally, but not always, the MethodTable*) * fUnwrapNullable - if true and type handle is Nullable, queries info for T - * fGetRefThisValueTypeCtor - if true and type handle is a value type, - * retrieves a ctor with mgd sig (ref T) -> void + * fForceObjectRefCtorEntryPoint - if false and type handle is a value type, + * retrieves a ctor with mgd sig (ref T) -> void * ppfnCtor - [optional, null-init] the instance's parameterless ctor, * mgd sig object -> void, or null if no parameterless ctor exists * pfCtorIsPublic - [optional, null-init] whether the parameterless ctor is public @@ -2024,20 +2026,21 @@ FCIMPLEND * ========== * This method will not run the type's static ctor or instantiate the type. */ -FCIMPL8(void, RuntimeTypeHandle::GetActivationInfo, - ReflectClassBaseObject* pRefType, +void QCALLTYPE RuntimeTypeHandle::GetActivationInfo( + QCall::TypeHandle pTypeHandle, + QCall::ObjectHandleOnStack pRuntimeType, PCODE* ppfnAllocator, void** pvAllocatorFirstArg, - CLR_BOOL fUnwrapNullable, - CLR_BOOL fGetRefThisValueTypeCtor, + BOOL fUnwrapNullable, + BOOL fForceObjectRefCtorEntryPoint, PCODE* ppfnCtor, - CLR_BOOL* pfCtorIsPublic, + BOOL* pfCtorIsPublic, MethodTable** ppMethodTable ) { CONTRACTL{ - FCALL_CHECK; - PRECONDITION(CheckPointer(pRefType)); + QCALL_CHECK; + PRECONDITION(!pTypeHandle.AsTypeHandle().IsNull()); PRECONDITION(CheckPointer(ppfnAllocator)); PRECONDITION(CheckPointer(pvAllocatorFirstArg)); PRECONDITION(*ppfnAllocator == NULL); @@ -2050,8 +2053,6 @@ FCIMPL8(void, RuntimeTypeHandle::GetActivationInfo, } CONTRACTL_END; - REFLECTCLASSBASEREF refType = (REFLECTCLASSBASEREF)ObjectToOBJECTREF(pRefType); - // If caller didn't want us to locate a ctor, then they want the // behavior of GetUninitializedObject, which prohibits COM activation // and throws slightly different exceptions. @@ -2061,10 +2062,9 @@ FCIMPL8(void, RuntimeTypeHandle::GetActivationInfo, bool fRequiresSpecialComActivationStub = false; void* pClassFactory = NULL; - // Various helpers we invoke can cause GC, so protect this ref - HELPER_METHOD_FRAME_BEGIN_1(refType); + BEGIN_QCALL; - TypeHandle typeHandle = refType->GetType(); + TypeHandle typeHandle = pTypeHandle.AsTypeHandle(); // Don't allow void if (typeHandle.GetSignatureCorElementType() == ELEMENT_TYPE_VOID) @@ -2144,7 +2144,12 @@ FCIMPL8(void, RuntimeTypeHandle::GetActivationInfo, if (IsComObjectClass(typeHandle)) { #ifdef FEATURE_COMINTEROP_UNMANAGED_ACTIVATION - SyncBlock* pSyncBlock = refType->GetSyncBlock(); + // Need to enter cooperative mode to manipulate OBJECTREFs + GCX_COOP(); + + _ASSERTE(pRuntimeType.Get()->GetTypeHandle() == typeHandle); + SyncBlock* pSyncBlock = pRuntimeType.Get()->GetSyncBlock(); + pClassFactory = (void*)pSyncBlock->GetInteropInfo()->GetComClassFactory(); #endif // FEATURE_COMINTEROP_UNMANAGED_ACTIVATION @@ -2176,20 +2181,25 @@ FCIMPL8(void, RuntimeTypeHandle::GetActivationInfo, if (ppfnCtor != NULL && pMT->HasDefaultConstructor()) { - MethodDesc* pMD = pMT->GetDefaultConstructor(!fGetRefThisValueTypeCtor); + if (!pMT->IsValueType()) + { + // Reference types always get an (object) -> void ctor. + fForceObjectRefCtorEntryPoint = TRUE; + } + + MethodDesc* pMD = pMT->GetDefaultConstructor(fForceObjectRefCtorEntryPoint); _ASSERTE(pMD != NULL); pMD->EnsureActive(); *ppfnCtor = pMD->GetMultiCallableAddrOfCode(); - *pfCtorIsPublic = pMD->IsPublic() != FALSE; + *pfCtorIsPublic = pMD->IsPublic(); } } *ppMethodTable = pMT; - HELPER_METHOD_FRAME_END(); + END_QCALL; } -FCIMPLEND /* * Given a ComClassFactory*, calls the COM allocator diff --git a/src/coreclr/src/vm/runtimehandles.h b/src/coreclr/src/vm/runtimehandles.h index 357fd2ac50f37..ed42b104c20d6 100644 --- a/src/coreclr/src/vm/runtimehandles.h +++ b/src/coreclr/src/vm/runtimehandles.h @@ -123,14 +123,16 @@ class RuntimeTypeHandle { // Static method on RuntimeTypeHandle - static FCDECL8(void, GetActivationInfo, - ReflectClassBaseObject* pRefType, + static + void QCALLTYPE GetActivationInfo( + QCall::TypeHandle pTypeHandle, + QCall::ObjectHandleOnStack pRuntimeType, PCODE* ppfnAllocator, void** pvAllocatorFirstArg, - CLR_BOOL fUnwrapNullable, - CLR_BOOL fGetRefThisValueTypeCtor, + BOOL fUnwrapNullable, + BOOL fForceObjectRefCtorEntryPoint, PCODE* ppfnCtor, - CLR_BOOL* pfCtorIsPublic, + BOOL* pfCtorIsPublic, MethodTable** ppMethodTable); static FCDECL1(Object*, AllocateComObject, void* pClassFactory); From e04401fa5c4029bffe0f1f848fbf303f5561a46b Mon Sep 17 00:00:00 2001 From: Levi Broderick Date: Sat, 21 Nov 2020 20:31:33 -0800 Subject: [PATCH 41/64] Simplify some call sites --- .../RuntimeHelpers.CoreCLR.cs | 17 +++++------ .../src/System/RuntimeHandles.cs | 13 +------- src/coreclr/src/vm/reflectioninvocation.cpp | 30 +++++++++---------- src/coreclr/src/vm/runtimehandles.h | 2 -- 4 files changed, 22 insertions(+), 40 deletions(-) diff --git a/src/coreclr/src/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.CoreCLR.cs b/src/coreclr/src/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.CoreCLR.cs index cd8307bbe6e6d..fb7a4d3f5ccad 100644 --- a/src/coreclr/src/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.CoreCLR.cs +++ b/src/coreclr/src/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.CoreCLR.cs @@ -42,20 +42,15 @@ public static partial class RuntimeHelpers public static void RunClassConstructor(RuntimeTypeHandle type) { - RunClassConstructor(type, preciseCctorsOnly: false); + _RunClassConstructor( + new QCallTypeHandle(ref type), + preciseCctorsOnly: Interop.BOOL.FALSE); } // If 'preciseCctorsOnly' is true, runs only cctors *NOT* marked .beforefieldinit. // If 'preciseCctorsOnly' is false, runs any cctor, regardless of .beforefieldinit annotation. - internal static void RunClassConstructor(RuntimeTypeHandle typeHandle, bool preciseCctorsOnly) - { - _RunClassConstructor( - new QCallTypeHandle(ref typeHandle), - (preciseCctorsOnly) ? Interop.BOOL.TRUE : Interop.BOOL.FALSE); - } - [DllImport(RuntimeHelpers.QCall, CharSet = CharSet.Unicode)] - private static extern void _RunClassConstructor(QCallTypeHandle typeHandle, Interop.BOOL preciseCctorsOnly); + internal static extern void _RunClassConstructor(QCallTypeHandle typeHandle, Interop.BOOL preciseCctorsOnly); // RunModuleConstructor causes the module constructor for the given type to be triggered @@ -169,7 +164,9 @@ private static unsafe object GetUninitializedObjectInternal( if (pMT->HasPreciseInitCctors) { - RunClassConstructor(rt.TypeHandle, preciseCctorsOnly: true); + _RunClassConstructor( + new QCallTypeHandle(ref rt), + preciseCctorsOnly: Interop.BOOL.TRUE); } object retVal = pfnAllocator(vAllocatorFirstArg); diff --git a/src/coreclr/src/System.Private.CoreLib/src/System/RuntimeHandles.cs b/src/coreclr/src/System.Private.CoreLib/src/System/RuntimeHandles.cs index d623d3134e956..4840326acc0d3 100644 --- a/src/coreclr/src/System.Private.CoreLib/src/System/RuntimeHandles.cs +++ b/src/coreclr/src/System.Private.CoreLib/src/System/RuntimeHandles.cs @@ -218,14 +218,6 @@ internal static void GetActivationInfo( { Debug.Assert(rt != null); - // Initialize all out vars - - pMT = default; - pfnAllocator = default; - vAllocatorFirstArg = default; - pfnCtor = default; - ctorIsPublic = default; - // Get the requested activation information // GetUninitializedInstance doesn't care about ctor information @@ -238,10 +230,9 @@ internal static void GetActivationInfo( MethodTable* pMethodTableTemp = default; _GetActivationInfo( - new QCallTypeHandle(ref rt), ObjectHandleOnStack.Create(ref rt), + ObjectHandleOnStack.Create(ref rt), &pfnAllocatorTemp, &vAllocatorFirstArgTemp, fUnwrapNullable: forGetUninitializedInstance ? Interop.BOOL.TRUE : Interop.BOOL.FALSE, - fForceObjectRefCtorEntryPoint: Interop.BOOL.TRUE, ppfnCtor: forGetUninitializedInstance ? null : &pfnCtorTemp, pfCtorIsPublic: forGetUninitializedInstance ? null : &fCtorIsPublicTemp, &pMethodTableTemp); @@ -282,12 +273,10 @@ internal static void GetActivationInfo( [DllImport(RuntimeHelpers.QCall, CharSet = CharSet.Unicode)] private static extern void _GetActivationInfo( - QCallTypeHandle pTypeHandle, ObjectHandleOnStack pRuntimeType, delegate** ppfnAllocator, void** pvAllocatorFirstArg, Interop.BOOL fUnwrapNullable, - Interop.BOOL fForceObjectRefCtorEntryPoint, delegate** ppfnCtor, Interop.BOOL* pfCtorIsPublic, MethodTable** ppMethodTable); diff --git a/src/coreclr/src/vm/reflectioninvocation.cpp b/src/coreclr/src/vm/reflectioninvocation.cpp index 85415414f8542..51b2a7d8a20aa 100644 --- a/src/coreclr/src/vm/reflectioninvocation.cpp +++ b/src/coreclr/src/vm/reflectioninvocation.cpp @@ -2009,16 +2009,13 @@ FCIMPLEND /* * Given a RuntimeType, queries info on how to instantiate the object. - * pTypeHandle - [required] the TypeHandle for the RuntimeType - * pRuntimeType - [required] the RuntimeType object (used for COM) + * pRuntimeType - [required] the RuntimeType object * pRefType - [required] the RuntimeType * ppfnAllocator - [required, null-init] fnptr to the allocator * mgd sig: void* -> object * pvAllocatorFirstArg - [required, null-init] first argument to the allocator * (normally, but not always, the MethodTable*) * fUnwrapNullable - if true and type handle is Nullable, queries info for T - * fForceObjectRefCtorEntryPoint - if false and type handle is a value type, - * retrieves a ctor with mgd sig (ref T) -> void * ppfnCtor - [optional, null-init] the instance's parameterless ctor, * mgd sig object -> void, or null if no parameterless ctor exists * pfCtorIsPublic - [optional, null-init] whether the parameterless ctor is public @@ -2027,12 +2024,10 @@ FCIMPLEND * This method will not run the type's static ctor or instantiate the type. */ void QCALLTYPE RuntimeTypeHandle::GetActivationInfo( - QCall::TypeHandle pTypeHandle, QCall::ObjectHandleOnStack pRuntimeType, PCODE* ppfnAllocator, void** pvAllocatorFirstArg, BOOL fUnwrapNullable, - BOOL fForceObjectRefCtorEntryPoint, PCODE* ppfnCtor, BOOL* pfCtorIsPublic, MethodTable** ppMethodTable @@ -2040,7 +2035,6 @@ void QCALLTYPE RuntimeTypeHandle::GetActivationInfo( { CONTRACTL{ QCALL_CHECK; - PRECONDITION(!pTypeHandle.AsTypeHandle().IsNull()); PRECONDITION(CheckPointer(ppfnAllocator)); PRECONDITION(CheckPointer(pvAllocatorFirstArg)); PRECONDITION(*ppfnAllocator == NULL); @@ -2062,9 +2056,14 @@ void QCALLTYPE RuntimeTypeHandle::GetActivationInfo( bool fRequiresSpecialComActivationStub = false; void* pClassFactory = NULL; + TypeHandle typeHandle = NULL; + BEGIN_QCALL; - TypeHandle typeHandle = pTypeHandle.AsTypeHandle(); + { + GCX_COOP(); + typeHandle = ((REFLECTCLASSBASEREF)pRuntimeType.Get())->GetType(); + } // Don't allow void if (typeHandle.GetSignatureCorElementType() == ELEMENT_TYPE_VOID) @@ -2181,17 +2180,16 @@ void QCALLTYPE RuntimeTypeHandle::GetActivationInfo( if (ppfnCtor != NULL && pMT->HasDefaultConstructor()) { - if (!pMT->IsValueType()) - { - // Reference types always get an (object) -> void ctor. - fForceObjectRefCtorEntryPoint = TRUE; - } - - MethodDesc* pMD = pMT->GetDefaultConstructor(fForceObjectRefCtorEntryPoint); + // managed sig: object -> void + // for ctors on value types, lookup boxed entry point stub + MethodDesc* pMD = pMT->GetDefaultConstructor(pMT->IsValueType()); _ASSERTE(pMD != NULL); pMD->EnsureActive(); - *ppfnCtor = pMD->GetMultiCallableAddrOfCode(); + PCODE pCode = pMD->GetMultiCallableAddrOfCode(); + _ASSERTE(pCode != NULL); + + *ppfnCtor = pCode; *pfCtorIsPublic = pMD->IsPublic(); } } diff --git a/src/coreclr/src/vm/runtimehandles.h b/src/coreclr/src/vm/runtimehandles.h index ed42b104c20d6..da220eab3b701 100644 --- a/src/coreclr/src/vm/runtimehandles.h +++ b/src/coreclr/src/vm/runtimehandles.h @@ -125,12 +125,10 @@ class RuntimeTypeHandle { static void QCALLTYPE GetActivationInfo( - QCall::TypeHandle pTypeHandle, QCall::ObjectHandleOnStack pRuntimeType, PCODE* ppfnAllocator, void** pvAllocatorFirstArg, BOOL fUnwrapNullable, - BOOL fForceObjectRefCtorEntryPoint, PCODE* ppfnCtor, BOOL* pfCtorIsPublic, MethodTable** ppMethodTable); From 186fd8307a0cd8a8b7d2b52d486efbfebd9d1fb5 Mon Sep 17 00:00:00 2001 From: Levi Broderick Date: Sat, 21 Nov 2020 20:41:41 -0800 Subject: [PATCH 42/64] Fix failing tests --- src/coreclr/src/vm/reflectioninvocation.cpp | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/coreclr/src/vm/reflectioninvocation.cpp b/src/coreclr/src/vm/reflectioninvocation.cpp index 51b2a7d8a20aa..a03cb70857ee4 100644 --- a/src/coreclr/src/vm/reflectioninvocation.cpp +++ b/src/coreclr/src/vm/reflectioninvocation.cpp @@ -2050,7 +2050,7 @@ void QCALLTYPE RuntimeTypeHandle::GetActivationInfo( // If caller didn't want us to locate a ctor, then they want the // behavior of GetUninitializedObject, which prohibits COM activation // and throws slightly different exceptions. - bool fGetUninitializedObject = (ppfnCtor != NULL); + bool fGetUninitializedObject = (ppfnCtor == NULL); // If we're activating __ComObject. bool fRequiresSpecialComActivationStub = false; @@ -2145,10 +2145,7 @@ void QCALLTYPE RuntimeTypeHandle::GetActivationInfo( #ifdef FEATURE_COMINTEROP_UNMANAGED_ACTIVATION // Need to enter cooperative mode to manipulate OBJECTREFs GCX_COOP(); - - _ASSERTE(pRuntimeType.Get()->GetTypeHandle() == typeHandle); SyncBlock* pSyncBlock = pRuntimeType.Get()->GetSyncBlock(); - pClassFactory = (void*)pSyncBlock->GetInteropInfo()->GetComClassFactory(); #endif // FEATURE_COMINTEROP_UNMANAGED_ACTIVATION From 4d3d9dbc2b1e00348b1c2ce7205e22ddeb99b601 Mon Sep 17 00:00:00 2001 From: Levi Broderick Date: Sat, 21 Nov 2020 21:37:13 -0800 Subject: [PATCH 43/64] Fix bad assert --- .../System.Private.CoreLib/src/System/ActivatorCache.CoreCLR.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/coreclr/src/System.Private.CoreLib/src/System/ActivatorCache.CoreCLR.cs b/src/coreclr/src/System.Private.CoreLib/src/System/ActivatorCache.CoreCLR.cs index 8bc5d5463f5cc..c7a0bd103f6db 100644 --- a/src/coreclr/src/System.Private.CoreLib/src/System/ActivatorCache.CoreCLR.cs +++ b/src/coreclr/src/System.Private.CoreLib/src/System/ActivatorCache.CoreCLR.cs @@ -78,7 +78,7 @@ internal ActivatorCache( #if FEATURE_COMINTEROP if (pMT->IsComObject) { - if (rt == typeof(__ComObject)) + if (rt.TypeHandle.Value == typeof(__ComObject).TypeHandle.Value) { // Base COM class - activation is handled entirely by the allocator. // We shouldn't call the ctor fn (which points to __ComObject's ctor). From 036cbf033720b16926a22154b29d3e63cc7c2202 Mon Sep 17 00:00:00 2001 From: Levi Broderick Date: Sat, 21 Nov 2020 22:03:53 -0800 Subject: [PATCH 44/64] Add managed _AllocateComObject member to make FCall checks happier --- .../System.Private.CoreLib/src/System/RuntimeHandles.cs | 9 +++++++++ src/coreclr/src/vm/ecalllist.h | 3 +++ 2 files changed, 12 insertions(+) diff --git a/src/coreclr/src/System.Private.CoreLib/src/System/RuntimeHandles.cs b/src/coreclr/src/System.Private.CoreLib/src/System/RuntimeHandles.cs index 4840326acc0d3..9c705ae6fd01a 100644 --- a/src/coreclr/src/System.Private.CoreLib/src/System/RuntimeHandles.cs +++ b/src/coreclr/src/System.Private.CoreLib/src/System/RuntimeHandles.cs @@ -207,6 +207,9 @@ internal static bool HasElementType(RuntimeType type) /// semantics. This method will ensure the type object is fully initialized within /// the VM, but it will not call any static ctors on the type. /// +#if FEATURE_COMINTEROP + [DynamicDependency("_AllocateComObject(System.Void*)")] +#endif internal static void GetActivationInfo( RuntimeType rt, bool forGetUninitializedInstance, @@ -281,6 +284,12 @@ private static extern void _GetActivationInfo( Interop.BOOL* pfCtorIsPublic, MethodTable** ppMethodTable); +#if FEATURE_COMINTEROP + // May be invoked via calli by ActivatorCache + [MethodImpl(MethodImplOptions.InternalCall)] + private static extern object _AllocateComObject(void* pClassFactory); +#endif + internal RuntimeType GetRuntimeType() { return m_type; diff --git a/src/coreclr/src/vm/ecalllist.h b/src/coreclr/src/vm/ecalllist.h index e081528156bfb..7a6c5e8f17dc9 100644 --- a/src/coreclr/src/vm/ecalllist.h +++ b/src/coreclr/src/vm/ecalllist.h @@ -239,6 +239,9 @@ FCFuncStart(gCOMTypeHandleFuncs) FCFuncElement("ContainsGenericVariables", RuntimeTypeHandle::ContainsGenericVariables) FCFuncElement("SatisfiesConstraints", RuntimeTypeHandle::SatisfiesConstraints) QCFuncElement("_GetActivationInfo", RuntimeTypeHandle::GetActivationInfo) +#ifdef FEATURE_COMINTEROP + FCFuncElement("_AllocateComObject", RuntimeTypeHandle::AllocateComObject) +#endif FEATURE_COMINTEROP FCFuncElement("CompareCanonicalHandles", RuntimeTypeHandle::CompareCanonicalHandles) FCIntrinsic("GetValueInternal", RuntimeTypeHandle::GetValueInternal, CORINFO_INTRINSIC_RTH_GetValueInternal) FCFuncElement("IsEquivalentTo", RuntimeTypeHandle::IsEquivalentTo) From 882b63ec8c219c2e4778ad651031860d1f283c88 Mon Sep 17 00:00:00 2001 From: Levi Broderick Date: Sat, 21 Nov 2020 22:55:51 -0800 Subject: [PATCH 45/64] Avoid using GetEEFuncEntryPoint - Works around failing assert in frames.cpp:1790 --- .../src/System/RuntimeHandles.cs | 10 ++++++++-- src/coreclr/src/vm/reflectioninvocation.cpp | 3 ++- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/src/coreclr/src/System.Private.CoreLib/src/System/RuntimeHandles.cs b/src/coreclr/src/System.Private.CoreLib/src/System/RuntimeHandles.cs index 9c705ae6fd01a..eeef215dad449 100644 --- a/src/coreclr/src/System.Private.CoreLib/src/System/RuntimeHandles.cs +++ b/src/coreclr/src/System.Private.CoreLib/src/System/RuntimeHandles.cs @@ -208,7 +208,6 @@ internal static bool HasElementType(RuntimeType type) /// the VM, but it will not call any static ctors on the type. /// #if FEATURE_COMINTEROP - [DynamicDependency("_AllocateComObject(System.Void*)")] #endif internal static void GetActivationInfo( RuntimeType rt, @@ -245,6 +244,14 @@ internal static void GetActivationInfo( Debug.Assert(pMethodTableTemp != null); pMT = pMethodTableTemp; +#if FEATURE_COMINTEROP + if ((nint)pfnAllocatorTemp == -1) + { + // Sentinel value to mean use special COM allocator + pfnAllocatorTemp = &_AllocateComObject; + } +#endif + Debug.Assert(pfnAllocatorTemp != null); pfnAllocator = pfnAllocatorTemp; @@ -285,7 +292,6 @@ private static extern void _GetActivationInfo( MethodTable** ppMethodTable); #if FEATURE_COMINTEROP - // May be invoked via calli by ActivatorCache [MethodImpl(MethodImplOptions.InternalCall)] private static extern object _AllocateComObject(void* pClassFactory); #endif diff --git a/src/coreclr/src/vm/reflectioninvocation.cpp b/src/coreclr/src/vm/reflectioninvocation.cpp index a03cb70857ee4..1ac820d9f7653 100644 --- a/src/coreclr/src/vm/reflectioninvocation.cpp +++ b/src/coreclr/src/vm/reflectioninvocation.cpp @@ -2166,7 +2166,8 @@ void QCALLTYPE RuntimeTypeHandle::GetActivationInfo( if (fRequiresSpecialComActivationStub) { // managed sig: ComClassFactory* -> object (via FCALL) - *ppfnAllocator = (PCODE)GetEEFuncEntryPoint(AllocateComObject); + // caller understands the -1 sentinel value and substitutes real address + *ppfnAllocator = (PCODE)(-1); *pvAllocatorFirstArg = pClassFactory; } else From 0b0c8db2a32503c09456357672915cd65ce951a5 Mon Sep 17 00:00:00 2001 From: Levi Broderick Date: Sun, 22 Nov 2020 00:31:46 -0800 Subject: [PATCH 46/64] Fix build error in ecalllist.h --- src/coreclr/src/vm/ecalllist.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/coreclr/src/vm/ecalllist.h b/src/coreclr/src/vm/ecalllist.h index 7a6c5e8f17dc9..dc9d148491c46 100644 --- a/src/coreclr/src/vm/ecalllist.h +++ b/src/coreclr/src/vm/ecalllist.h @@ -241,7 +241,7 @@ FCFuncStart(gCOMTypeHandleFuncs) QCFuncElement("_GetActivationInfo", RuntimeTypeHandle::GetActivationInfo) #ifdef FEATURE_COMINTEROP FCFuncElement("_AllocateComObject", RuntimeTypeHandle::AllocateComObject) -#endif FEATURE_COMINTEROP +#endif // FEATURE_COMINTEROP FCFuncElement("CompareCanonicalHandles", RuntimeTypeHandle::CompareCanonicalHandles) FCIntrinsic("GetValueInternal", RuntimeTypeHandle::GetValueInternal, CORINFO_INTRINSIC_RTH_GetValueInternal) FCFuncElement("IsEquivalentTo", RuntimeTypeHandle::IsEquivalentTo) From c50d287807885c1e2623bef6a35c20705ece5f70 Mon Sep 17 00:00:00 2001 From: Levi Broderick Date: Sun, 22 Nov 2020 13:23:46 -0800 Subject: [PATCH 47/64] Update COM invocation unit tests --- .../Interop/COM/Reflection/Reflection.cs | 20 ++++--------------- 1 file changed, 4 insertions(+), 16 deletions(-) diff --git a/src/tests/Interop/COM/Reflection/Reflection.cs b/src/tests/Interop/COM/Reflection/Reflection.cs index a8769d1d4800f..cccb9625937ca 100644 --- a/src/tests/Interop/COM/Reflection/Reflection.cs +++ b/src/tests/Interop/COM/Reflection/Reflection.cs @@ -89,25 +89,13 @@ static bool ActivateCOMType() return true; } - catch (TargetInvocationException e) + catch (PlatformNotSupportedException) when (!RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) { - if (!RuntimeInformation.IsOSPlatform(OSPlatform.Windows) && e.InnerException is PlatformNotSupportedException) - { - return true; - } - - Console.WriteLine($"Caught unexpected {nameof(PlatformNotSupportedException)}: {e}"); - return false; + return true; } - catch(COMException e) + catch (COMException) when (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) { - if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) - { - return true; - } - - Console.WriteLine($"Caught unexpected {nameof(COMException)}: {e}"); - return false; + return true; } catch (Exception e) { From cebbf48f16d441ec170536c05e078cacfff8cf44 Mon Sep 17 00:00:00 2001 From: Levi Broderick Date: Sun, 22 Nov 2020 13:26:39 -0800 Subject: [PATCH 48/64] Allow propagation of PNSE in RuntimeHandles --- .../src/System.Private.CoreLib/src/System/RuntimeHandles.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/coreclr/src/System.Private.CoreLib/src/System/RuntimeHandles.cs b/src/coreclr/src/System.Private.CoreLib/src/System/RuntimeHandles.cs index eeef215dad449..806579b7683c3 100644 --- a/src/coreclr/src/System.Private.CoreLib/src/System/RuntimeHandles.cs +++ b/src/coreclr/src/System.Private.CoreLib/src/System/RuntimeHandles.cs @@ -271,6 +271,7 @@ internal static void GetActivationInfo( switch (ex) { case ArgumentException: throw new ArgumentException(friendlyMessage); + case PlatformNotSupportedException: throw new PlatformNotSupportedException(friendlyMessage); case NotSupportedException: throw new NotSupportedException(friendlyMessage); case MethodAccessException: throw new MethodAccessException(friendlyMessage); case MissingMethodException: throw new MissingMethodException(friendlyMessage); From 12b4578faca4cf32ede219a0afd1597d4b21d83a Mon Sep 17 00:00:00 2001 From: Levi Broderick Date: Mon, 23 Nov 2020 17:55:28 -0800 Subject: [PATCH 49/64] Remove _AllocateComObject sentinel --- .../src/System/RuntimeHandles.cs | 11 +++-------- src/coreclr/src/vm/corelib.h | 1 + src/coreclr/src/vm/metasig.h | 1 + src/coreclr/src/vm/reflectioninvocation.cpp | 5 +---- 4 files changed, 6 insertions(+), 12 deletions(-) diff --git a/src/coreclr/src/System.Private.CoreLib/src/System/RuntimeHandles.cs b/src/coreclr/src/System.Private.CoreLib/src/System/RuntimeHandles.cs index e0d2a6c2c653e..d6ebfb429e958 100644 --- a/src/coreclr/src/System.Private.CoreLib/src/System/RuntimeHandles.cs +++ b/src/coreclr/src/System.Private.CoreLib/src/System/RuntimeHandles.cs @@ -258,6 +258,7 @@ private static extern void CreateInstanceForAnotherGenericParameter( /// the VM, but it will not call any static ctors on the type. /// #if FEATURE_COMINTEROP + [DynamicDependency("_AllocateComObject(System.Void*)")] #endif internal static void GetActivationInfo( RuntimeType rt, @@ -294,14 +295,6 @@ internal static void GetActivationInfo( Debug.Assert(pMethodTableTemp != null); pMT = pMethodTableTemp; -#if FEATURE_COMINTEROP - if ((nint)pfnAllocatorTemp == -1) - { - // Sentinel value to mean use special COM allocator - pfnAllocatorTemp = &_AllocateComObject; - } -#endif - Debug.Assert(pfnAllocatorTemp != null); pfnAllocator = pfnAllocatorTemp; @@ -343,6 +336,8 @@ private static extern void _GetActivationInfo( MethodTable** ppMethodTable); #if FEATURE_COMINTEROP + // Referenced by unmanaged layer (see _GetActivationInfo). + // First parameter is ComClassFactory*. [MethodImpl(MethodImplOptions.InternalCall)] private static extern object _AllocateComObject(void* pClassFactory); #endif diff --git a/src/coreclr/src/vm/corelib.h b/src/coreclr/src/vm/corelib.h index dbf514748420a..1b659a95b28a1 100644 --- a/src/coreclr/src/vm/corelib.h +++ b/src/coreclr/src/vm/corelib.h @@ -371,6 +371,7 @@ DEFINE_CLASS(RT_TYPE_HANDLE, System, RuntimeTypeHandle) DEFINE_METHOD(RT_TYPE_HANDLE, GET_TYPE_HELPER, GetTypeHelper, SM_Type_ArrType_IntPtr_int_RetType) DEFINE_METHOD(RT_TYPE_HANDLE, PVOID_CTOR, .ctor, IM_RuntimeType_RetVoid) DEFINE_METHOD(RT_TYPE_HANDLE, GETVALUEINTERNAL, GetValueInternal, SM_RuntimeTypeHandle_RetIntPtr) +DEFINE_METHOD(RT_TYPE_HANDLE, ALLOCATECOMOBJECT, _AllocateComObject, SM_VoidPtr_RetObj) DEFINE_FIELD(RT_TYPE_HANDLE, M_TYPE, m_type) DEFINE_CLASS_U(Reflection, RtFieldInfo, NoClass) diff --git a/src/coreclr/src/vm/metasig.h b/src/coreclr/src/vm/metasig.h index 0714e0c50e209..c9856714883b8 100644 --- a/src/coreclr/src/vm/metasig.h +++ b/src/coreclr/src/vm/metasig.h @@ -464,6 +464,7 @@ DEFINE_METASIG(IM(RefObject_RetBool, r(j), F)) DEFINE_METASIG_T(IM(Class_RetObj, C(CLASS), j)) DEFINE_METASIG(IM(Int_VoidPtr_RetVoid, i P(v), v)) DEFINE_METASIG(IM(VoidPtr_RetVoid, P(v), v)) +DEFINE_METASIG(SM(VoidPtr_RetObj, P(v), j)) DEFINE_METASIG_T(IM(Str_RetModule, s, C(MODULE))) DEFINE_METASIG_T(SM(Assembly_Str_RetAssembly, C(ASSEMBLY) s, C(ASSEMBLY))) diff --git a/src/coreclr/src/vm/reflectioninvocation.cpp b/src/coreclr/src/vm/reflectioninvocation.cpp index 3feae1de374c7..bc0c8be5c8c58 100644 --- a/src/coreclr/src/vm/reflectioninvocation.cpp +++ b/src/coreclr/src/vm/reflectioninvocation.cpp @@ -1599,8 +1599,6 @@ void QCALLTYPE ReflectionInvocation::RunClassConstructor( { MethodTable* pMT = typeHnd.AsMethodTable(); - Assembly* pAssem = pMT->GetAssembly(); - if (!pMT->IsClassInited()) { pMT->CheckRestore(); @@ -2164,8 +2162,7 @@ void QCALLTYPE RuntimeTypeHandle::GetActivationInfo( if (fRequiresSpecialComActivationStub) { // managed sig: ComClassFactory* -> object (via FCALL) - // caller understands the -1 sentinel value and substitutes real address - *ppfnAllocator = (PCODE)(-1); + *ppfnAllocator = CoreLibBinder::GetMethod(METHOD__RT_TYPE_HANDLE__ALLOCATECOMOBJECT)->GetMultiCallableAddrOfCode(); *pvAllocatorFirstArg = pClassFactory; } else From a7fa617cdbfc1ffb52121e7e378e904c33879fcb Mon Sep 17 00:00:00 2001 From: Levi Broderick Date: Mon, 23 Nov 2020 18:01:01 -0800 Subject: [PATCH 50/64] PR feedback --- src/coreclr/src/vm/reflectioninvocation.cpp | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/coreclr/src/vm/reflectioninvocation.cpp b/src/coreclr/src/vm/reflectioninvocation.cpp index bc0c8be5c8c58..3c732e1a2718e 100644 --- a/src/coreclr/src/vm/reflectioninvocation.cpp +++ b/src/coreclr/src/vm/reflectioninvocation.cpp @@ -2178,7 +2178,6 @@ void QCALLTYPE RuntimeTypeHandle::GetActivationInfo( MethodDesc* pMD = pMT->GetDefaultConstructor(pMT->IsValueType()); _ASSERTE(pMD != NULL); - pMD->EnsureActive(); PCODE pCode = pMD->GetMultiCallableAddrOfCode(); _ASSERTE(pCode != NULL); @@ -2357,8 +2356,6 @@ void QCALLTYPE ReflectionEnum::GetEnumValuesAndNames(QCall::TypeHandle pEnumType static_assert_no_msg(offsetof(MDDefaultValue, m_ulValue) == offsetof(MDDefaultValue, m_ullValue)); PVOID pValue = &defaultValue.m_byteValue; - auto y = ReflectionEnum::InternalGetEnumUnderlyingType; - switch (type) { case ELEMENT_TYPE_I1: value = *((INT8 *)pValue); From 3e9438c3878ad497869dcc33fbe9e60d851f8fd2 Mon Sep 17 00:00:00 2001 From: Levi Broderick Date: Tue, 24 Nov 2020 18:22:52 -0800 Subject: [PATCH 51/64] PR feedback - simplify GetActivationInfo out params --- .../src/System/ActivatorCache.CoreCLR.cs | 64 +++++++------------ .../RuntimeHelpers.CoreCLR.cs | 24 ++++--- .../src/System/RuntimeHandles.cs | 25 ++++---- src/coreclr/src/vm/ecalllist.h | 1 + src/coreclr/src/vm/reflectioninvocation.cpp | 20 +++--- src/coreclr/src/vm/runtimehandles.cpp | 12 ++++ src/coreclr/src/vm/runtimehandles.h | 8 ++- 7 files changed, 74 insertions(+), 80 deletions(-) diff --git a/src/coreclr/src/System.Private.CoreLib/src/System/ActivatorCache.CoreCLR.cs b/src/coreclr/src/System.Private.CoreLib/src/System/ActivatorCache.CoreCLR.cs index c7a0bd103f6db..6cf2776ce9b6c 100644 --- a/src/coreclr/src/System.Private.CoreLib/src/System/ActivatorCache.CoreCLR.cs +++ b/src/coreclr/src/System.Private.CoreLib/src/System/ActivatorCache.CoreCLR.cs @@ -45,67 +45,49 @@ internal ActivatorCache( rt.CreateInstanceCheckThis(); RuntimeTypeHandle.GetActivationInfo(rt, forGetUninitializedInstance: false, - out MethodTable* pMT, out _pfnAllocator!, out _allocatorFirstArg, + out _pfnAllocator!, out _allocatorFirstArg, out _pfnCtor!, out _ctorIsPublic); - bool useNoopCtorStub = false; + Debug.Assert(_pfnAllocator != null); + Debug.Assert(_allocatorFirstArg != null); - if (pMT->IsValueType) + if (_pfnCtor == null) { - if (pMT->IsNullable) - { - // Activator.CreateInstance returns null given typeof(Nullable). - - static object? ReturnNull(void* _) => null; - _pfnAllocator = &ReturnNull; - _allocatorFirstArg = default; - - useNoopCtorStub = true; - } - else if (_pfnCtor == null) + if (!RuntimeTypeHandle.IsValueType(rt)) { - // Value type with no parameterless ctor - we'll point it to our noop stub. - - useNoopCtorStub = true; - } - } - else - { - // Reference type - we can't proceed unless there's a default ctor we can call. - - Debug.Assert(rt.IsClass); + // Reference type with no parameterless ctor. + // Is it __ComObject? If not, we can't continue. #if FEATURE_COMINTEROP - if (pMT->IsComObject) - { - if (rt.TypeHandle.Value == typeof(__ComObject).TypeHandle.Value) + if (!rt.IsGenericCOMObjectImpl()) +#endif { - // Base COM class - activation is handled entirely by the allocator. - // We shouldn't call the ctor fn (which points to __ComObject's ctor). - - useNoopCtorStub = true; + throw new MissingMethodException(SR.Format(SR.Arg_NoDefCTor, rt)); } } - else -#endif - if (_pfnCtor == null) + else if (RuntimeTypeHandle.IsConstructedNullableType(rt)) { - // Reference type with no parameterless ctor - we cannot continue. + // Activator.CreateInstance returns null given typeof(Nullable). - throw new MissingMethodException(SR.Format(SR.Arg_NoDefCTor, rt)); + static object? ReturnNull(void* _) => null; + _pfnAllocator = &ReturnNull; + _allocatorFirstArg = default; } - } - // We don't need to worry about invoking cctors here. The runtime will figure it - // out for us when the instance ctor is called. + // At this point, we have Nullable, a ctorless value type T, + // or a ctorless __ComObject. In any case, we should replace the + // ctor call with our no-op stub. - if (useNoopCtorStub) - { static void CtorNoopStub(object? uninitializedObject) { } _pfnCtor = &CtorNoopStub; // we use null singleton pattern if no ctor call is necessary _ctorIsPublic = true; // implicit parameterless ctor is always considered public } + // We don't need to worry about invoking cctors here. The runtime will figure it + // out for us when the instance ctor is called. For value types, because we're + // creating a boxed default(T), the static cctor is called when *any* instance + // method is invoked. + Debug.Assert(_pfnAllocator != null); Debug.Assert(_pfnCtor != null); // we use null singleton pattern if no ctor call is necessary } diff --git a/src/coreclr/src/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.CoreCLR.cs b/src/coreclr/src/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.CoreCLR.cs index a14ddf58c0e42..b04cd746bd79d 100644 --- a/src/coreclr/src/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.CoreCLR.cs +++ b/src/coreclr/src/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.CoreCLR.cs @@ -3,7 +3,6 @@ using System.Diagnostics; using System.Diagnostics.CodeAnalysis; -using System.Reflection; using System.Runtime.InteropServices; using Internal.Runtime.CompilerServices; @@ -152,24 +151,23 @@ private static unsafe object GetUninitializedObjectInternal( RuntimeType rt = (RuntimeType)type; // If type is Nullable, returns the allocator and MethodTable* for the underlying T. - RuntimeTypeHandle.GetActivationInfo(rt, forGetUninitializedInstance: true, out MethodTable* pMT, + RuntimeTypeHandle.GetActivationInfo(rt, forGetUninitializedInstance: true, out delegate* pfnAllocator, out void* vAllocatorFirstArg, out _, out _); Debug.Assert(pfnAllocator != null); - Debug.Assert(pMT != null); - Debug.Assert(!pMT->IsNullable, "Should've unwrapped any Nullable input."); - Debug.Assert(!pMT->HasComponentSize, "Should've blocked string, array, and similar."); - // Per ECMA-335, Sec. I.8.9.5, the instance ctor is normally responsible for - // invoking any non-.beforefieldinit static cctor. However, since we're bypassing - // instance ctors here, we need to invoke any non-.beforefieldinit static cctor - // manually, otherwise we could hand the caller a not-fully-initialized type. + // Per ECMA-335, Sec. I.8.9.5, the instance ctor is normally responsible for invoking + // any non-.beforefieldinit static cctor. However, since we're going to skip calling + // the instance ctor, we need to invoke any precise static cctors manually, otherwise + // the caller could receive a not-fully-usable type. Exception: we won't call the + // precise static cctor for value types, since we're just creating a boxed default(T), + // and the precise static cctor will run when the caller invokes any instance method + // on the struct. - if (pMT->HasPreciseInitCctors) + if (!RuntimeTypeHandle.IsValueType(rt)) { - _RunClassConstructor( - new QCallTypeHandle(ref rt), - preciseCctorsOnly: Interop.BOOL.TRUE); + // Call below will early-exit if there's no precise static cctor + _RunClassConstructor(new QCallTypeHandle(ref rt), preciseCctorsOnly: Interop.BOOL.TRUE); } object retVal = pfnAllocator(vAllocatorFirstArg); diff --git a/src/coreclr/src/System.Private.CoreLib/src/System/RuntimeHandles.cs b/src/coreclr/src/System.Private.CoreLib/src/System/RuntimeHandles.cs index d6ebfb429e958..8c0f3dc6124e4 100644 --- a/src/coreclr/src/System.Private.CoreLib/src/System/RuntimeHandles.cs +++ b/src/coreclr/src/System.Private.CoreLib/src/System/RuntimeHandles.cs @@ -263,7 +263,6 @@ private static extern void CreateInstanceForAnotherGenericParameter( internal static void GetActivationInfo( RuntimeType rt, bool forGetUninitializedInstance, - out MethodTable* pMT, out delegate* pfnAllocator, out void* vAllocatorFirstArg, out delegate* pfnCtor, @@ -280,21 +279,15 @@ internal static void GetActivationInfo( void* vAllocatorFirstArgTemp = default; delegate* pfnCtorTemp = default; Interop.BOOL fCtorIsPublicTemp = default; - MethodTable* pMethodTableTemp = default; _GetActivationInfo( ObjectHandleOnStack.Create(ref rt), &pfnAllocatorTemp, &vAllocatorFirstArgTemp, - fUnwrapNullable: forGetUninitializedInstance ? Interop.BOOL.TRUE : Interop.BOOL.FALSE, ppfnCtor: forGetUninitializedInstance ? null : &pfnCtorTemp, - pfCtorIsPublic: forGetUninitializedInstance ? null : &fCtorIsPublicTemp, - &pMethodTableTemp); + pfCtorIsPublic: forGetUninitializedInstance ? null : &fCtorIsPublicTemp); // Marshal values back to caller - Debug.Assert(pMethodTableTemp != null); - pMT = pMethodTableTemp; - Debug.Assert(pfnAllocatorTemp != null); pfnAllocator = pfnAllocatorTemp; @@ -330,10 +323,8 @@ private static extern void _GetActivationInfo( ObjectHandleOnStack pRuntimeType, delegate** ppfnAllocator, void** pvAllocatorFirstArg, - Interop.BOOL fUnwrapNullable, delegate** ppfnCtor, - Interop.BOOL* pfCtorIsPublic, - MethodTable** ppMethodTable); + Interop.BOOL* pfCtorIsPublic); #if FEATURE_COMINTEROP // Referenced by unmanaged layer (see _GetActivationInfo). @@ -667,6 +658,18 @@ internal RuntimeType MakePointer() [DllImport(RuntimeHelpers.QCall, CharSet = CharSet.Unicode)] internal static extern Interop.BOOL IsCollectible(QCallTypeHandle handle); + // Returns true iff type is a closed nullable type, like int? or double?. + // Returns false if given the open type Nullable<>. + internal static bool IsConstructedNullableType(RuntimeType type) + { + Debug.Assert(type != null); + return IsConstructedNullableType(new QCallTypeHandle(ref type)) != Interop.BOOL.FALSE; + } + + [SuppressGCTransition] + [DllImport(RuntimeHelpers.QCall, CharSet = CharSet.Unicode)] + private static extern Interop.BOOL IsConstructedNullableType(QCallTypeHandle handle); + [MethodImpl(MethodImplOptions.InternalCall)] internal static extern bool HasInstantiation(RuntimeType type); diff --git a/src/coreclr/src/vm/ecalllist.h b/src/coreclr/src/vm/ecalllist.h index aa1a5f4d8872b..d52aabb44fa94 100644 --- a/src/coreclr/src/vm/ecalllist.h +++ b/src/coreclr/src/vm/ecalllist.h @@ -232,6 +232,7 @@ FCFuncStart(gCOMTypeHandleFuncs) QCFuncElement("GetInstantiation", RuntimeTypeHandle::GetInstantiation) QCFuncElement("Instantiate", RuntimeTypeHandle::Instantiate) QCFuncElement("GetGenericTypeDefinition", RuntimeTypeHandle::GetGenericTypeDefinition) + QCFuncElement("IsConstructedNullableType", RuntimeTypeHandle::IsConstructedNullableType) FCFuncElement("HasInstantiation", RuntimeTypeHandle::HasInstantiation) FCFuncElement("GetGenericVariableIndex", RuntimeTypeHandle::GetGenericVariableIndex) FCFuncElement("IsGenericVariable", RuntimeTypeHandle::IsGenericVariable) diff --git a/src/coreclr/src/vm/reflectioninvocation.cpp b/src/coreclr/src/vm/reflectioninvocation.cpp index 3c732e1a2718e..ddf888d339359 100644 --- a/src/coreclr/src/vm/reflectioninvocation.cpp +++ b/src/coreclr/src/vm/reflectioninvocation.cpp @@ -2006,27 +2006,23 @@ FCIMPLEND /* * Given a RuntimeType, queries info on how to instantiate the object. * pRuntimeType - [required] the RuntimeType object - * pRefType - [required] the RuntimeType * ppfnAllocator - [required, null-init] fnptr to the allocator * mgd sig: void* -> object * pvAllocatorFirstArg - [required, null-init] first argument to the allocator * (normally, but not always, the MethodTable*) - * fUnwrapNullable - if true and type handle is Nullable, queries info for T * ppfnCtor - [optional, null-init] the instance's parameterless ctor, * mgd sig object -> void, or null if no parameterless ctor exists * pfCtorIsPublic - [optional, null-init] whether the parameterless ctor is public - * ppMethodTable - [required, null-init] the MethodTable* for the queried type * ========== - * This method will not run the type's static ctor or instantiate the type. + * This method will not run the type's static cctor. + * This method will not allocate an instance of the target type. */ void QCALLTYPE RuntimeTypeHandle::GetActivationInfo( QCall::ObjectHandleOnStack pRuntimeType, PCODE* ppfnAllocator, void** pvAllocatorFirstArg, - BOOL fUnwrapNullable, PCODE* ppfnCtor, - BOOL* pfCtorIsPublic, - MethodTable** ppMethodTable + BOOL* pfCtorIsPublic ) { CONTRACTL{ @@ -2035,18 +2031,20 @@ void QCALLTYPE RuntimeTypeHandle::GetActivationInfo( PRECONDITION(CheckPointer(pvAllocatorFirstArg)); PRECONDITION(*ppfnAllocator == NULL); PRECONDITION(*pvAllocatorFirstArg == NULL); + // Both ctor "out" params must be specified, or both must be null. + // If they're specified, the target must be zero-initialized. PRECONDITION((ppfnCtor == NULL) == (pfCtorIsPublic == NULL)); PRECONDITION((ppfnCtor == NULL) || (*ppfnCtor == NULL)); PRECONDITION((pfCtorIsPublic == NULL) || (!*pfCtorIsPublic)); - PRECONDITION(CheckPointer(ppMethodTable)); - PRECONDITION(*ppMethodTable == NULL); } CONTRACTL_END; // If caller didn't want us to locate a ctor, then they want the // behavior of GetUninitializedObject, which prohibits COM activation - // and throws slightly different exceptions. + // and throws slightly different exceptions. It also unwraps Nullable + // into simply 'T'. bool fGetUninitializedObject = (ppfnCtor == NULL); + bool fUnwrapNullable = fGetUninitializedObject; // If we're activating __ComObject. bool fRequiresSpecialComActivationStub = false; @@ -2186,8 +2184,6 @@ void QCALLTYPE RuntimeTypeHandle::GetActivationInfo( } } - *ppMethodTable = pMT; - END_QCALL; } diff --git a/src/coreclr/src/vm/runtimehandles.cpp b/src/coreclr/src/vm/runtimehandles.cpp index 517800a440296..63e27d0590497 100644 --- a/src/coreclr/src/vm/runtimehandles.cpp +++ b/src/coreclr/src/vm/runtimehandles.cpp @@ -1597,6 +1597,18 @@ FCIMPL2(FC_BOOL_RET, RuntimeTypeHandle::CompareCanonicalHandles, ReflectClassBas } FCIMPLEND +BOOL QCALLTYPE RuntimeTypeHandle::IsConstructedNullableType(QCall::TypeHandle pTypeHandle) +{ + QCALL_CONTRACT_NO_GC_TRANSITION; + + TypeHandle typeHandle = pTypeHandle.AsTypeHandle(); + _ASSERTE(!typeHandle.IsNull()); + + return typeHandle.HasInstantiation() + && !typeHandle.IsGenericTypeDefinition() + && typeHandle.AsMethodTable()->IsNullable(); +} + FCIMPL1(FC_BOOL_RET, RuntimeTypeHandle::HasInstantiation, PTR_ReflectClassBaseObject pTypeUNSAFE) { FCALL_CONTRACT; diff --git a/src/coreclr/src/vm/runtimehandles.h b/src/coreclr/src/vm/runtimehandles.h index 85c04d5b17840..8f505e6fcc31f 100644 --- a/src/coreclr/src/vm/runtimehandles.h +++ b/src/coreclr/src/vm/runtimehandles.h @@ -128,10 +128,8 @@ class RuntimeTypeHandle { QCall::ObjectHandleOnStack pRuntimeType, PCODE* ppfnAllocator, void** pvAllocatorFirstArg, - BOOL fUnwrapNullable, PCODE* ppfnCtor, - BOOL* pfCtorIsPublic, - MethodTable** ppMethodTable); + BOOL* pfCtorIsPublic); static FCDECL1(Object*, AllocateComObject, void* pClassFactory); @@ -198,6 +196,10 @@ class RuntimeTypeHandle { static FCDECL2(FC_BOOL_RET, IsInstanceOfType, ReflectClassBaseObject *pType, Object *object); static FCDECL6(FC_BOOL_RET, SatisfiesConstraints, PTR_ReflectClassBaseObject pGenericParameter, TypeHandle *typeContextArgs, INT32 typeContextCount, TypeHandle *methodContextArgs, INT32 methodContextCount, PTR_ReflectClassBaseObject pGenericArgument); + + static + BOOL QCALLTYPE IsConstructedNullableType(QCall::TypeHandle pTypeHandle); + static FCDECL1(FC_BOOL_RET, HasInstantiation, PTR_ReflectClassBaseObject pType); From 4b38b6c2b6ec0acdcdbe2b6755c5bcedbcf5c191 Mon Sep 17 00:00:00 2001 From: Levi Broderick Date: Tue, 24 Nov 2020 20:00:01 -0800 Subject: [PATCH 52/64] Add missing ifdef around _AllocateComInterop --- src/coreclr/src/vm/corelib.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/coreclr/src/vm/corelib.h b/src/coreclr/src/vm/corelib.h index 1b659a95b28a1..cc0e30392f802 100644 --- a/src/coreclr/src/vm/corelib.h +++ b/src/coreclr/src/vm/corelib.h @@ -371,7 +371,9 @@ DEFINE_CLASS(RT_TYPE_HANDLE, System, RuntimeTypeHandle) DEFINE_METHOD(RT_TYPE_HANDLE, GET_TYPE_HELPER, GetTypeHelper, SM_Type_ArrType_IntPtr_int_RetType) DEFINE_METHOD(RT_TYPE_HANDLE, PVOID_CTOR, .ctor, IM_RuntimeType_RetVoid) DEFINE_METHOD(RT_TYPE_HANDLE, GETVALUEINTERNAL, GetValueInternal, SM_RuntimeTypeHandle_RetIntPtr) +#ifdef FEATURE_COMINTEROP DEFINE_METHOD(RT_TYPE_HANDLE, ALLOCATECOMOBJECT, _AllocateComObject, SM_VoidPtr_RetObj) +#endif DEFINE_FIELD(RT_TYPE_HANDLE, M_TYPE, m_type) DEFINE_CLASS_U(Reflection, RtFieldInfo, NoClass) From 9ed2b9b00be3ae3aa0a733de6915870aa67d2f8c Mon Sep 17 00:00:00 2001 From: Levi Broderick Date: Tue, 24 Nov 2020 20:24:12 -0800 Subject: [PATCH 53/64] Final inspection + code cleanup --- .../src/System/ActivatorCache.CoreCLR.cs | 7 +++- .../RuntimeHelpers.CoreCLR.cs | 42 +------------------ .../src/System/RuntimeType.CoreCLR.cs | 2 +- src/coreclr/src/vm/methodtable.cpp | 2 +- src/coreclr/src/vm/reflectioninvocation.cpp | 8 +++- src/coreclr/src/vm/runtimehandles.cpp | 2 + 6 files changed, 17 insertions(+), 46 deletions(-) diff --git a/src/coreclr/src/System.Private.CoreLib/src/System/ActivatorCache.CoreCLR.cs b/src/coreclr/src/System.Private.CoreLib/src/System/ActivatorCache.CoreCLR.cs index 6cf2776ce9b6c..5f48ac8f3c907 100644 --- a/src/coreclr/src/System.Private.CoreLib/src/System/ActivatorCache.CoreCLR.cs +++ b/src/coreclr/src/System.Private.CoreLib/src/System/ActivatorCache.CoreCLR.cs @@ -29,8 +29,7 @@ private sealed unsafe class ActivatorCache #endif internal ActivatorCache( - [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.NonPublicConstructors)] RuntimeType rt, - bool wrapExceptions) + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.NonPublicConstructors)] RuntimeType rt) { Debug.Assert(rt != null); @@ -94,6 +93,8 @@ static void CtorNoopStub(object? uninitializedObject) { } internal bool CtorIsPublic => _ctorIsPublic; + [DebuggerStepThrough] + [DebuggerHidden] [MethodImpl(MethodImplOptions.AggressiveInlining)] internal object? CreateUninitializedObject(RuntimeType rt) { @@ -116,6 +117,8 @@ static void CtorNoopStub(object? uninitializedObject) { } return retVal; } + [DebuggerStepThrough] + [DebuggerHidden] [MethodImpl(MethodImplOptions.AggressiveInlining)] internal void CallConstructor(object? uninitializedObject) => _pfnCtor(uninitializedObject); } diff --git a/src/coreclr/src/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.CoreCLR.cs b/src/coreclr/src/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.CoreCLR.cs index b04cd746bd79d..14b48f3fb5add 100644 --- a/src/coreclr/src/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.CoreCLR.cs +++ b/src/coreclr/src/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.CoreCLR.cs @@ -150,7 +150,7 @@ private static unsafe object GetUninitializedObjectInternal( Debug.Assert(type != null); RuntimeType rt = (RuntimeType)type; - // If type is Nullable, returns the allocator and MethodTable* for the underlying T. + // If type is Nullable, returns the allocator for the underlying T. RuntimeTypeHandle.GetActivationInfo(rt, forGetUninitializedInstance: true, out delegate* pfnAllocator, out void* vAllocatorFirstArg, out _, out _); @@ -411,15 +411,9 @@ internal unsafe struct MethodTable public MethodTable** InterfaceMap; // WFLAGS_HIGH_ENUM & WFLAGS_LOW_ENUM - private const uint enum_flag_Category_Mask = 0x000F0000; - private const uint enum_flag_Category_Nullable = 0x00050000; - private const uint enum_flag_Category_ValueType = 0x00040000; - private const uint enum_flag_Category_ValueType_Mask = 0x000C0000; private const uint enum_flag_ContainsPointers = 0x01000000; - private const uint enum_flag_ComObject = 0x40000000; private const uint enum_flag_HasComponentSize = 0x80000000; - private const uint enum_flag_HasPreciseInitCctors = 0x00000400; - private const uint enum_flag_HasTypeEquivalence = 0x00004000; // TODO: shouldn't this be 0x02000000? + private const uint enum_flag_HasTypeEquivalence = 0x00004000; // Types that require non-trivial interface cast have this bit set in the category private const uint enum_flag_NonTrivialInterfaceCast = 0x00080000 // enum_flag_Category_Array | 0x40000000 // enum_flag_ComObject @@ -476,38 +470,6 @@ public bool NonTrivialInterfaceCast } } - public bool HasPreciseInitCctors - { - get - { - return !HasComponentSize && (Flags & enum_flag_HasPreciseInitCctors) != 0; - } - } - - public bool IsComObject - { - get - { - return (Flags & enum_flag_ComObject) != 0; - } - } - - public bool IsNullable - { - get - { - return (Flags & enum_flag_Category_Mask) == enum_flag_Category_Nullable; - } - } - - public bool IsValueType - { - get - { - return (Flags & enum_flag_Category_ValueType_Mask) == enum_flag_Category_ValueType; - } - } - public bool HasTypeEquivalence { get diff --git a/src/coreclr/src/System.Private.CoreLib/src/System/RuntimeType.CoreCLR.cs b/src/coreclr/src/System.Private.CoreLib/src/System/RuntimeType.CoreCLR.cs index fb77dafb6208e..4378729df30f0 100644 --- a/src/coreclr/src/System.Private.CoreLib/src/System/RuntimeType.CoreCLR.cs +++ b/src/coreclr/src/System.Private.CoreLib/src/System/RuntimeType.CoreCLR.cs @@ -3981,7 +3981,7 @@ private void CreateInstanceCheckThis() if (GenericCache is not ActivatorCache cache) { - cache = new ActivatorCache(this, wrapExceptions); + cache = new ActivatorCache(this); GenericCache = cache; } diff --git a/src/coreclr/src/vm/methodtable.cpp b/src/coreclr/src/vm/methodtable.cpp index 5c600590f8f75..d0d0e2f9d5cf9 100644 --- a/src/coreclr/src/vm/methodtable.cpp +++ b/src/coreclr/src/vm/methodtable.cpp @@ -9150,7 +9150,7 @@ BOOL MethodTable::HasExplicitOrImplicitPublicDefaultConstructor() } //========================================================================================== -MethodDesc *MethodTable::GetDefaultConstructor(BOOL forceBoxedEntryPoint) +MethodDesc *MethodTable::GetDefaultConstructor(BOOL forceBoxedEntryPoint /* = FALSE */) { WRAPPER_NO_CONTRACT; _ASSERTE(HasDefaultConstructor()); diff --git a/src/coreclr/src/vm/reflectioninvocation.cpp b/src/coreclr/src/vm/reflectioninvocation.cpp index ddf888d339359..aabcda58d5397 100644 --- a/src/coreclr/src/vm/reflectioninvocation.cpp +++ b/src/coreclr/src/vm/reflectioninvocation.cpp @@ -2031,8 +2031,8 @@ void QCALLTYPE RuntimeTypeHandle::GetActivationInfo( PRECONDITION(CheckPointer(pvAllocatorFirstArg)); PRECONDITION(*ppfnAllocator == NULL); PRECONDITION(*pvAllocatorFirstArg == NULL); - // Both ctor "out" params must be specified, or both must be null. - // If they're specified, the target must be zero-initialized. + // Both ctor "out" params must be specified, or both must be nullptr. + // If they're specified, the destination must be zero-initialized. PRECONDITION((ppfnCtor == NULL) == (pfCtorIsPublic == NULL)); PRECONDITION((ppfnCtor == NULL) || (*ppfnCtor == NULL)); PRECONDITION((pfCtorIsPublic == NULL) || (!*pfCtorIsPublic)); @@ -2056,6 +2056,10 @@ void QCALLTYPE RuntimeTypeHandle::GetActivationInfo( { GCX_COOP(); + + // We need to take the RuntimeType itself rather than the RuntimeTypeHandle, + // as the COM CLSID is stored in the RuntimeType object's sync block, and we + // might need to pull it out later in this method. typeHandle = ((REFLECTCLASSBASEREF)pRuntimeType.Get())->GetType(); } diff --git a/src/coreclr/src/vm/runtimehandles.cpp b/src/coreclr/src/vm/runtimehandles.cpp index 63e27d0590497..9778b01799545 100644 --- a/src/coreclr/src/vm/runtimehandles.cpp +++ b/src/coreclr/src/vm/runtimehandles.cpp @@ -635,6 +635,7 @@ FCIMPL2(MethodDesc *, RuntimeTypeHandle::GetMethodAt, ReflectClassBaseObject *pT return pRetMethod; } + FCIMPLEND FCIMPL3(FC_BOOL_RET, RuntimeTypeHandle::GetFields, ReflectClassBaseObject *pTypeUNSAFE, INT32 **result, INT32 *pCount) { @@ -1604,6 +1605,7 @@ BOOL QCALLTYPE RuntimeTypeHandle::IsConstructedNullableType(QCall::TypeHandle pT TypeHandle typeHandle = pTypeHandle.AsTypeHandle(); _ASSERTE(!typeHandle.IsNull()); + // eqivalent to IsGenericType && !IsGenericTypeDefinition && GetGenericTypeDefinition() == typeof(Nullable<>) return typeHandle.HasInstantiation() && !typeHandle.IsGenericTypeDefinition() && typeHandle.AsMethodTable()->IsNullable(); From 0ebebd62569a8b44b214fd4d8faff36c8e1b5e44 Mon Sep 17 00:00:00 2001 From: Levi Broderick Date: Tue, 24 Nov 2020 21:26:36 -0800 Subject: [PATCH 54/64] Fix compilation error on non-Windows --- src/coreclr/src/vm/reflectioninvocation.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/coreclr/src/vm/reflectioninvocation.cpp b/src/coreclr/src/vm/reflectioninvocation.cpp index aabcda58d5397..873d68d6cca4e 100644 --- a/src/coreclr/src/vm/reflectioninvocation.cpp +++ b/src/coreclr/src/vm/reflectioninvocation.cpp @@ -2163,9 +2163,11 @@ void QCALLTYPE RuntimeTypeHandle::GetActivationInfo( if (fRequiresSpecialComActivationStub) { +#ifdef FEATURE_COMINTEROP // managed sig: ComClassFactory* -> object (via FCALL) *ppfnAllocator = CoreLibBinder::GetMethod(METHOD__RT_TYPE_HANDLE__ALLOCATECOMOBJECT)->GetMultiCallableAddrOfCode(); *pvAllocatorFirstArg = pClassFactory; +#endif // FEATURE_COMINTEROP } else { From 374cf3489e0f72f2d59e33ee9cd736be0516fb59 Mon Sep 17 00:00:00 2001 From: Levi Broderick Date: Wed, 25 Nov 2020 15:48:11 -0800 Subject: [PATCH 55/64] Code cleanup & PR feedback --- .../src/System/ActivatorCache.CoreCLR.cs | 77 +++--- .../RuntimeHelpers.CoreCLR.cs | 52 +--- .../src/System/RuntimeHandles.cs | 73 ++--- src/coreclr/src/vm/ecalllist.h | 4 +- src/coreclr/src/vm/reflectioninvocation.cpp | 255 +++++++++++------- src/coreclr/src/vm/reflectioninvocation.h | 9 +- src/coreclr/src/vm/runtimehandles.cpp | 13 - src/coreclr/src/vm/runtimehandles.h | 7 +- .../src/Resources/Strings.resx | 3 + 9 files changed, 243 insertions(+), 250 deletions(-) diff --git a/src/coreclr/src/System.Private.CoreLib/src/System/ActivatorCache.CoreCLR.cs b/src/coreclr/src/System.Private.CoreLib/src/System/ActivatorCache.CoreCLR.cs index 5f48ac8f3c907..33b00e70f4f3d 100644 --- a/src/coreclr/src/System.Private.CoreLib/src/System/ActivatorCache.CoreCLR.cs +++ b/src/coreclr/src/System.Private.CoreLib/src/System/ActivatorCache.CoreCLR.cs @@ -43,58 +43,65 @@ internal ActivatorCache( rt.CreateInstanceCheckThis(); - RuntimeTypeHandle.GetActivationInfo(rt, forGetUninitializedInstance: false, - out _pfnAllocator!, out _allocatorFirstArg, - out _pfnCtor!, out _ctorIsPublic); - - Debug.Assert(_pfnAllocator != null); - Debug.Assert(_allocatorFirstArg != null); - - if (_pfnCtor == null) + try { - if (!RuntimeTypeHandle.IsValueType(rt)) - { - // Reference type with no parameterless ctor. - // Is it __ComObject? If not, we can't continue. + RuntimeTypeHandle.GetActivationInfo(rt, + out _pfnAllocator!, out _allocatorFirstArg, + out _pfnCtor!, out _ctorIsPublic); + } + catch (Exception ex) + { + // Exception messages coming from the runtime won't include + // the type name. Let's include it here to improve the + // debugging experience for our callers. -#if FEATURE_COMINTEROP - if (!rt.IsGenericCOMObjectImpl()) -#endif - { - throw new MissingMethodException(SR.Format(SR.Arg_NoDefCTor, rt)); - } - } - else if (RuntimeTypeHandle.IsConstructedNullableType(rt)) + string friendlyMessage = SR.Format(SR.Activator_CannotCreateInstance, rt, ex.Message); + switch (ex) { - // Activator.CreateInstance returns null given typeof(Nullable). - - static object? ReturnNull(void* _) => null; - _pfnAllocator = &ReturnNull; - _allocatorFirstArg = default; + case ArgumentException: throw new ArgumentException(friendlyMessage); + case PlatformNotSupportedException: throw new PlatformNotSupportedException(friendlyMessage); + case NotSupportedException: throw new NotSupportedException(friendlyMessage); + case MethodAccessException: throw new MethodAccessException(friendlyMessage); + case MissingMethodException: throw new MissingMethodException(friendlyMessage); + case MemberAccessException: throw new MemberAccessException(friendlyMessage); } - // At this point, we have Nullable, a ctorless value type T, - // or a ctorless __ComObject. In any case, we should replace the - // ctor call with our no-op stub. + throw; // can't make a friendlier message, rethrow original exception + } + + // Activator.CreateInstance returns null given typeof(Nullable). + + if (_pfnAllocator == null) + { + Debug.Assert(Nullable.GetUnderlyingType(rt) != null, + "Null allocator should only be returned for Nullable."); + + static object? ReturnNull(void* _) => null; + _pfnAllocator = &ReturnNull; + } + // If no ctor is provided, we have Nullable, a ctorless value type T, + // or a ctorless __ComObject. In any case, we should replace the + // ctor call with our no-op stub. The unmanaged GetActivationInfo layer + // would have thrown an exception if 'rt' were a normal reference type + // without a ctor. + + if (_pfnCtor is null) + { static void CtorNoopStub(object? uninitializedObject) { } _pfnCtor = &CtorNoopStub; // we use null singleton pattern if no ctor call is necessary - _ctorIsPublic = true; // implicit parameterless ctor is always considered public + + Debug.Assert(_ctorIsPublic); // implicit parameterless ctor is always considered public } // We don't need to worry about invoking cctors here. The runtime will figure it // out for us when the instance ctor is called. For value types, because we're // creating a boxed default(T), the static cctor is called when *any* instance // method is invoked. - - Debug.Assert(_pfnAllocator != null); - Debug.Assert(_pfnCtor != null); // we use null singleton pattern if no ctor call is necessary } internal bool CtorIsPublic => _ctorIsPublic; - [DebuggerStepThrough] - [DebuggerHidden] [MethodImpl(MethodImplOptions.AggressiveInlining)] internal object? CreateUninitializedObject(RuntimeType rt) { @@ -117,8 +124,6 @@ static void CtorNoopStub(object? uninitializedObject) { } return retVal; } - [DebuggerStepThrough] - [DebuggerHidden] [MethodImpl(MethodImplOptions.AggressiveInlining)] internal void CallConstructor(object? uninitializedObject) => _pfnCtor(uninitializedObject); } diff --git a/src/coreclr/src/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.CoreCLR.cs b/src/coreclr/src/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.CoreCLR.cs index 14b48f3fb5add..1f0aa576850c0 100644 --- a/src/coreclr/src/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.CoreCLR.cs +++ b/src/coreclr/src/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.CoreCLR.cs @@ -39,19 +39,14 @@ public static partial class RuntimeHelpers // This call will generate an exception if the specified class constructor threw an // exception when it ran. + [MethodImpl(MethodImplOptions.InternalCall)] + private static extern void _RunClassConstructor(RuntimeType type); + public static void RunClassConstructor(RuntimeTypeHandle type) { - _RunClassConstructor( - new QCallTypeHandle(ref type), - preciseCctorsOnly: Interop.BOOL.FALSE); + _RunClassConstructor(type.GetRuntimeType()); } - // If 'preciseCctorsOnly' is true, runs only cctors *NOT* marked .beforefieldinit. - // If 'preciseCctorsOnly' is false, runs any cctor, regardless of .beforefieldinit annotation. - [DllImport(RuntimeHelpers.QCall, CharSet = CharSet.Unicode)] - internal static extern void _RunClassConstructor(QCallTypeHandle typeHandle, Interop.BOOL preciseCctorsOnly); - - // RunModuleConstructor causes the module constructor for the given type to be triggered // in the current domain. After this call returns, the module constructor is guaranteed to // have at least been started by some thread. In the absence of module constructor @@ -143,38 +138,15 @@ public static int OffsetToStringData [MethodImpl(MethodImplOptions.InternalCall)] public static extern bool TryEnsureSufficientExecutionStack(); - private static unsafe object GetUninitializedObjectInternal( + [MethodImpl(MethodImplOptions.InternalCall)] + private static extern object GetUninitializedObjectInternal( + // This API doesn't call any constructors, but the type needs to be seen as constructed. + // A type is seen as constructed if a constructor is kept. + // This obviously won't cover a type with no constructor. Reference types with no + // constructor are an academic problem. Valuetypes with no constructors are a problem, + // but IL Linker currently treats them as always implicitly boxed. [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.NonPublicConstructors)] - Type type) - { - Debug.Assert(type != null); - RuntimeType rt = (RuntimeType)type; - - // If type is Nullable, returns the allocator for the underlying T. - RuntimeTypeHandle.GetActivationInfo(rt, forGetUninitializedInstance: true, - out delegate* pfnAllocator, out void* vAllocatorFirstArg, out _, out _); - - Debug.Assert(pfnAllocator != null); - - // Per ECMA-335, Sec. I.8.9.5, the instance ctor is normally responsible for invoking - // any non-.beforefieldinit static cctor. However, since we're going to skip calling - // the instance ctor, we need to invoke any precise static cctors manually, otherwise - // the caller could receive a not-fully-usable type. Exception: we won't call the - // precise static cctor for value types, since we're just creating a boxed default(T), - // and the precise static cctor will run when the caller invokes any instance method - // on the struct. - - if (!RuntimeTypeHandle.IsValueType(rt)) - { - // Call below will early-exit if there's no precise static cctor - _RunClassConstructor(new QCallTypeHandle(ref rt), preciseCctorsOnly: Interop.BOOL.TRUE); - } - - object retVal = pfnAllocator(vAllocatorFirstArg); - GC.KeepAlive(rt); // don't allow the type to be collected before the object is instantiated - - return retVal; - } + Type type); [MethodImpl(MethodImplOptions.InternalCall)] internal static extern object AllocateUninitializedClone(object obj); diff --git a/src/coreclr/src/System.Private.CoreLib/src/System/RuntimeHandles.cs b/src/coreclr/src/System.Private.CoreLib/src/System/RuntimeHandles.cs index c12c2eb0a2d08..213714a839eb6 100644 --- a/src/coreclr/src/System.Private.CoreLib/src/System/RuntimeHandles.cs +++ b/src/coreclr/src/System.Private.CoreLib/src/System/RuntimeHandles.cs @@ -257,12 +257,8 @@ private static extern void CreateInstanceForAnotherGenericParameter( /// semantics. This method will ensure the type object is fully initialized within /// the VM, but it will not call any static ctors on the type. /// -#if FEATURE_COMINTEROP - [DynamicDependency("_AllocateComObject(System.Void*)")] -#endif internal static void GetActivationInfo( RuntimeType rt, - bool forGetUninitializedInstance, out delegate* pfnAllocator, out void* vAllocatorFirstArg, out delegate* pfnCtor, @@ -270,52 +266,27 @@ internal static void GetActivationInfo( { Debug.Assert(rt != null); - // Get the requested activation information - // GetUninitializedInstance doesn't care about ctor information - - try - { - delegate* pfnAllocatorTemp = default; - void* vAllocatorFirstArgTemp = default; - delegate* pfnCtorTemp = default; - Interop.BOOL fCtorIsPublicTemp = default; - - _GetActivationInfo( - ObjectHandleOnStack.Create(ref rt), - &pfnAllocatorTemp, &vAllocatorFirstArgTemp, - ppfnCtor: forGetUninitializedInstance ? null : &pfnCtorTemp, - pfCtorIsPublic: forGetUninitializedInstance ? null : &fCtorIsPublicTemp); - - // Marshal values back to caller + delegate* pfnAllocatorTemp = default; + void* vAllocatorFirstArgTemp = default; + delegate* pfnCtorTemp = default; + Interop.BOOL fCtorIsPublicTemp = default; - Debug.Assert(pfnAllocatorTemp != null); - pfnAllocator = pfnAllocatorTemp; + _GetActivationInfo( + ObjectHandleOnStack.Create(ref rt), + &pfnAllocatorTemp, &vAllocatorFirstArgTemp, + ppfnCtor: &pfnCtorTemp, + pfCtorIsPublic: &fCtorIsPublicTemp); - Debug.Assert(vAllocatorFirstArgTemp != null); - vAllocatorFirstArg = vAllocatorFirstArgTemp; + // Marshal values back to caller - pfnCtor = pfnCtorTemp; // could be null - ctorIsPublic = fCtorIsPublicTemp != Interop.BOOL.FALSE; - } - catch (Exception ex) - { - // Exception messages coming from the runtime won't include - // the type name. Let's include it here to improve the - // debugging experience for our callers. + Debug.Assert(pfnAllocatorTemp != null); + pfnAllocator = pfnAllocatorTemp; - string friendlyMessage = SR.Format(SR.Activator_CannotCreateInstance, rt, ex.Message); - switch (ex) - { - case ArgumentException: throw new ArgumentException(friendlyMessage); - case PlatformNotSupportedException: throw new PlatformNotSupportedException(friendlyMessage); - case NotSupportedException: throw new NotSupportedException(friendlyMessage); - case MethodAccessException: throw new MethodAccessException(friendlyMessage); - case MissingMethodException: throw new MissingMethodException(friendlyMessage); - case MemberAccessException: throw new MemberAccessException(friendlyMessage); - } + Debug.Assert(vAllocatorFirstArgTemp != null); + vAllocatorFirstArg = vAllocatorFirstArgTemp; - throw; // can't make a friendlier message, rethrow original exception - } + pfnCtor = pfnCtorTemp; // could be null + ctorIsPublic = fCtorIsPublicTemp != Interop.BOOL.FALSE; } [DllImport(RuntimeHelpers.QCall, CharSet = CharSet.Unicode)] @@ -669,18 +640,6 @@ internal RuntimeType MakePointer() [DllImport(RuntimeHelpers.QCall, CharSet = CharSet.Unicode)] internal static extern Interop.BOOL IsCollectible(QCallTypeHandle handle); - // Returns true iff type is a closed nullable type, like int? or double?. - // Returns false if given the open type Nullable<>. - internal static bool IsConstructedNullableType(RuntimeType type) - { - Debug.Assert(type != null); - return IsConstructedNullableType(new QCallTypeHandle(ref type)) != Interop.BOOL.FALSE; - } - - [SuppressGCTransition] - [DllImport(RuntimeHelpers.QCall, CharSet = CharSet.Unicode)] - private static extern Interop.BOOL IsConstructedNullableType(QCallTypeHandle handle); - [MethodImpl(MethodImplOptions.InternalCall)] internal static extern bool HasInstantiation(RuntimeType type); diff --git a/src/coreclr/src/vm/ecalllist.h b/src/coreclr/src/vm/ecalllist.h index d52aabb44fa94..cf8f93c7403e1 100644 --- a/src/coreclr/src/vm/ecalllist.h +++ b/src/coreclr/src/vm/ecalllist.h @@ -232,7 +232,6 @@ FCFuncStart(gCOMTypeHandleFuncs) QCFuncElement("GetInstantiation", RuntimeTypeHandle::GetInstantiation) QCFuncElement("Instantiate", RuntimeTypeHandle::Instantiate) QCFuncElement("GetGenericTypeDefinition", RuntimeTypeHandle::GetGenericTypeDefinition) - QCFuncElement("IsConstructedNullableType", RuntimeTypeHandle::IsConstructedNullableType) FCFuncElement("HasInstantiation", RuntimeTypeHandle::HasInstantiation) FCFuncElement("GetGenericVariableIndex", RuntimeTypeHandle::GetGenericVariableIndex) FCFuncElement("IsGenericVariable", RuntimeTypeHandle::IsGenericVariable) @@ -872,7 +871,7 @@ FCFuncEnd() FCFuncStart(gRuntimeHelpers) FCFuncElement("GetObjectValue", ObjectNative::GetObjectValue) FCIntrinsic("InitializeArray", ArrayNative::InitializeArray, CORINFO_INTRINSIC_InitializeArray) - QCFuncElement("_RunClassConstructor", ReflectionInvocation::RunClassConstructor) + FCFuncElement("_RunClassConstructor", ReflectionInvocation::RunClassConstructor) FCFuncElement("_RunModuleConstructor", ReflectionInvocation::RunModuleConstructor) QCFuncElement("_CompileMethod", ReflectionInvocation::CompileMethod) FCFuncElement("_PrepareMethod", ReflectionInvocation::PrepareMethod) @@ -882,6 +881,7 @@ FCFuncStart(gRuntimeHelpers) FCFuncElement("AllocateUninitializedClone", ObjectNative::AllocateUninitializedClone) FCFuncElement("EnsureSufficientExecutionStack", ReflectionInvocation::EnsureSufficientExecutionStack) FCFuncElement("TryEnsureSufficientExecutionStack", ReflectionInvocation::TryEnsureSufficientExecutionStack) + FCFuncElement("GetUninitializedObjectInternal", ReflectionSerialization::GetUninitializedObject) QCFuncElement("AllocateTypeAssociatedMemoryInternal", RuntimeTypeHandle::AllocateTypeAssociatedMemory) FCFuncElement("AllocTailCallArgBuffer", TailCallHelp::AllocTailCallArgBuffer) FCFuncElement("GetTailCallInfo", TailCallHelp::GetTailCallInfo) diff --git a/src/coreclr/src/vm/reflectioninvocation.cpp b/src/coreclr/src/vm/reflectioninvocation.cpp index 873d68d6cca4e..62af3d74e879c 100644 --- a/src/coreclr/src/vm/reflectioninvocation.cpp +++ b/src/coreclr/src/vm/reflectioninvocation.cpp @@ -1583,41 +1583,35 @@ void QCALLTYPE ReflectionInvocation::CompileMethod(MethodDesc * pMD) } // This method triggers the class constructor for a give type -void QCALLTYPE ReflectionInvocation::RunClassConstructor( - QCall::TypeHandle pTypeHandle, - BOOL fPreciseCctorsOnly) +FCIMPL1(void, ReflectionInvocation::RunClassConstructor, ReflectClassBaseObject *pTypeUNSAFE) { - QCALL_CONTRACT; + FCALL_CONTRACT; - BEGIN_QCALL; + REFLECTCLASSBASEREF refType = (REFLECTCLASSBASEREF)ObjectToOBJECTREF(pTypeUNSAFE); - TypeHandle typeHnd = pTypeHandle.AsTypeHandle(); - if (typeHnd.IsNull()) - COMPlusThrow(kArgumentException, W("InvalidOperation_HandleIsNotInitialized")); + if (refType == NULL) + FCThrowArgumentVoidEx(kArgumentException, NULL, W("InvalidOperation_HandleIsNotInitialized")); + + TypeHandle typeHnd = refType->GetType(); + if (typeHnd.IsTypeDesc()) + return; - if (!typeHnd.IsTypeDesc()) + MethodTable *pMT = typeHnd.AsMethodTable(); + + Assembly *pAssem = pMT->GetAssembly(); + + if (!pMT->IsClassInited()) { - MethodTable* pMT = typeHnd.AsMethodTable(); + HELPER_METHOD_FRAME_BEGIN_1(refType); - if (!pMT->IsClassInited()) - { - pMT->CheckRestore(); - pMT->EnsureInstanceActive(); - if (fPreciseCctorsOnly) - { - // only cctors NOT marked .beforefieldinit - pMT->CheckRunClassInitAsIfConstructingThrowing(); - } - else - { - // any cctor, regardless of .beforefieldinit annotation - pMT->CheckRunClassInitThrowing(); - } - } - } + pMT->CheckRestore(); + pMT->EnsureInstanceActive(); + pMT->CheckRunClassInitThrowing(); - END_QCALL; + HELPER_METHOD_FRAME_END(); + } } +FCIMPLEND // This method triggers the module constructor for a give module FCIMPL1(void, ReflectionInvocation::RunModuleConstructor, ReflectModuleBaseObject *pModuleUNSAFE) { @@ -2004,64 +1998,26 @@ lExit: ; FCIMPLEND /* - * Given a RuntimeType, queries info on how to instantiate the object. - * pRuntimeType - [required] the RuntimeType object - * ppfnAllocator - [required, null-init] fnptr to the allocator - * mgd sig: void* -> object - * pvAllocatorFirstArg - [required, null-init] first argument to the allocator - * (normally, but not always, the MethodTable*) - * ppfnCtor - [optional, null-init] the instance's parameterless ctor, - * mgd sig object -> void, or null if no parameterless ctor exists - * pfCtorIsPublic - [optional, null-init] whether the parameterless ctor is public + * Given a TypeHandle, validates whether it's legal to construct a real + * instance of that type. Throws an exception if the instantiation would + * be illegal; e.g., type is void or a pointer or an open generic. This + * doesn't guarantee that a ctor will succeed, only that the VM is able + * to support an instance of this type on the heap. * ========== - * This method will not run the type's static cctor. - * This method will not allocate an instance of the target type. + * The 'fForGetUninitializedInstance' parameter controls the type of + * exception that is thrown if a check fails. */ -void QCALLTYPE RuntimeTypeHandle::GetActivationInfo( - QCall::ObjectHandleOnStack pRuntimeType, - PCODE* ppfnAllocator, - void** pvAllocatorFirstArg, - PCODE* ppfnCtor, - BOOL* pfCtorIsPublic -) +void RuntimeTypeHandle::ValidateTypeAbleToBeInstantiated( + TypeHandle typeHandle, + bool fGetUninitializedObject) { - CONTRACTL{ - QCALL_CHECK; - PRECONDITION(CheckPointer(ppfnAllocator)); - PRECONDITION(CheckPointer(pvAllocatorFirstArg)); - PRECONDITION(*ppfnAllocator == NULL); - PRECONDITION(*pvAllocatorFirstArg == NULL); - // Both ctor "out" params must be specified, or both must be nullptr. - // If they're specified, the destination must be zero-initialized. - PRECONDITION((ppfnCtor == NULL) == (pfCtorIsPublic == NULL)); - PRECONDITION((ppfnCtor == NULL) || (*ppfnCtor == NULL)); - PRECONDITION((pfCtorIsPublic == NULL) || (!*pfCtorIsPublic)); - } - CONTRACTL_END; - - // If caller didn't want us to locate a ctor, then they want the - // behavior of GetUninitializedObject, which prohibits COM activation - // and throws slightly different exceptions. It also unwraps Nullable - // into simply 'T'. - bool fGetUninitializedObject = (ppfnCtor == NULL); - bool fUnwrapNullable = fGetUninitializedObject; - - // If we're activating __ComObject. - bool fRequiresSpecialComActivationStub = false; - void* pClassFactory = NULL; - - TypeHandle typeHandle = NULL; - - BEGIN_QCALL; - + CONTRACTL { - GCX_COOP(); - - // We need to take the RuntimeType itself rather than the RuntimeTypeHandle, - // as the COM CLSID is stored in the RuntimeType object's sync block, and we - // might need to pull it out later in this method. - typeHandle = ((REFLECTCLASSBASEREF)pRuntimeType.Get())->GetType(); + THROWS; + GC_TRIGGERS; + MODE_ANY; } + CONTRACTL_END; // Don't allow void if (typeHandle.GetSignatureCorElementType() == ELEMENT_TYPE_VOID) @@ -2091,7 +2047,8 @@ void QCALLTYPE RuntimeTypeHandle::GetActivationInfo( } // Don't allow abstract classes or interface types - if (pMT->IsAbstract()) { + if (pMT->IsAbstract()) + { RuntimeExceptionKind exKind = fGetUninitializedObject ? kMemberAccessException : kMissingMethodException; if (pMT->IsInterface()) COMPlusThrow(exKind, W("Acc_CreateInterface")); @@ -2115,13 +2072,64 @@ void QCALLTYPE RuntimeTypeHandle::GetActivationInfo( if (pMT->IsByRefLike()) { COMPlusThrow(kNotSupportedException, W("NotSupported_ByRefLike")); } +} + +/* + * Given a RuntimeType, queries info on how to instantiate the object. + * pRuntimeType - [required] the RuntimeType object + * ppfnAllocator - [required, null-init] fnptr to the allocator + * mgd sig: void* -> object + * pvAllocatorFirstArg - [required, null-init] first argument to the allocator + * (normally, but not always, the MethodTable*) + * ppfnCtor - [required, null-init] the instance's parameterless ctor, + * mgd sig object -> void, or null if no ctor is needed for this type + * pfCtorIsPublic - [required, null-init] whether the parameterless ctor is public + * ========== + * This method will not run the type's static cctor. + * This method will not allocate an instance of the target type. + */ +void QCALLTYPE RuntimeTypeHandle::GetActivationInfo( + QCall::ObjectHandleOnStack pRuntimeType, + PCODE* ppfnAllocator, + void** pvAllocatorFirstArg, + PCODE* ppfnCtor, + BOOL* pfCtorIsPublic +) +{ + CONTRACTL{ + QCALL_CHECK; + PRECONDITION(CheckPointer(ppfnAllocator)); + PRECONDITION(CheckPointer(pvAllocatorFirstArg)); + PRECONDITION(CheckPointer(ppfnCtor)); + PRECONDITION(CheckPointer(pfCtorIsPublic)); + PRECONDITION(*ppfnAllocator == NULL); + PRECONDITION(*pvAllocatorFirstArg == NULL); + PRECONDITION(*ppfnCtor == NULL); + PRECONDITION(*pfCtorIsPublic == NULL); + } + CONTRACTL_END; + + TypeHandle typeHandle = NULL; + + // If we're activating __ComObject. + void* pClassFactory = NULL; + + BEGIN_QCALL; - // Caller passed Nullable but wanted information for T instead? - if (fUnwrapNullable && Nullable::IsNullableType(pMT)) { - pMT = pMT->GetInstantiation()[0].GetMethodTable(); + GCX_COOP(); + + // We need to take the RuntimeType itself rather than the RuntimeTypeHandle, + // as the COM CLSID is stored in the RuntimeType object's sync block, and we + // might need to pull it out later in this method. + typeHandle = ((REFLECTCLASSBASEREF)pRuntimeType.Get())->GetType(); } + ValidateTypeAbleToBeInstantiated(typeHandle, false /* fGetUninitializedObject */); + + MethodTable* pMT = typeHandle.AsMethodTable(); + PREFIX_ASSUME(pMT != NULL); + #ifdef FEATURE_COMINTEROP // COM allocation can involve the __ComObject base type (with attached CLSID) or a // VM-implemented [ComImport] class. For GetUninitializedObject, we block all COM types. @@ -2133,11 +2141,6 @@ void QCALLTYPE RuntimeTypeHandle::GetActivationInfo( // ctor with COM activation logic on supported platforms, else ctor itself will PNSE. if (pMT->IsComObjectType()) { - if (fGetUninitializedObject) - { - COMPlusThrow(kNotSupportedException, W("NotSupported_ManagedActivation")); - } - if (IsComObjectClass(typeHandle)) { #ifdef FEATURE_COMINTEROP_UNMANAGED_ACTIVATION @@ -2147,12 +2150,10 @@ void QCALLTYPE RuntimeTypeHandle::GetActivationInfo( pClassFactory = (void*)pSyncBlock->GetInteropInfo()->GetComClassFactory(); #endif // FEATURE_COMINTEROP_UNMANAGED_ACTIVATION - if (!pClassFactory) + if (pClassFactory == NULL) { COMPlusThrow(kInvalidComObjectException, IDS_EE_NO_BACKING_CLASS_FACTORY); } - - fRequiresSpecialComActivationStub = true; } } #endif // FEATURE_COMINTEROP @@ -2161,25 +2162,35 @@ void QCALLTYPE RuntimeTypeHandle::GetActivationInfo( // All checks passed! Pass parameters back to the caller. - if (fRequiresSpecialComActivationStub) + if (Nullable::IsNullableType(typeHandle)) { + // CreateInstance returns null given Nullable + *ppfnAllocator = NULL; + *pvAllocatorFirstArg = NULL; + *ppfnCtor = NULL; + *pfCtorIsPublic = TRUE; // no ctor call needed => assume 'public' equivalent + } #ifdef FEATURE_COMINTEROP + else if (pClassFactory != NULL) + { // managed sig: ComClassFactory* -> object (via FCALL) *ppfnAllocator = CoreLibBinder::GetMethod(METHOD__RT_TYPE_HANDLE__ALLOCATECOMOBJECT)->GetMultiCallableAddrOfCode(); *pvAllocatorFirstArg = pClassFactory; -#endif // FEATURE_COMINTEROP + *ppfnCtor = NULL; // no ctor call needed; activation handled entirely by the allocator + *pfCtorIsPublic = TRUE; // no ctor call needed => assume 'public' equivalent } +#endif // FEATURE_COMINTEROP else { // managed sig: MethodTable* -> object (via JIT helper) *ppfnAllocator = CEEJitInfo::getHelperFtnStatic(CEEInfo::getNewHelperStatic(pMT)); *pvAllocatorFirstArg = pMT; - if (ppfnCtor != NULL && pMT->HasDefaultConstructor()) + if (pMT->HasDefaultConstructor()) { // managed sig: object -> void // for ctors on value types, lookup boxed entry point stub - MethodDesc* pMD = pMT->GetDefaultConstructor(pMT->IsValueType()); + MethodDesc* pMD = pMT->GetDefaultConstructor(pMT->IsValueType() /* forceBoxedEntryPoint */); _ASSERTE(pMD != NULL); PCODE pCode = pMD->GetMultiCallableAddrOfCode(); @@ -2188,6 +2199,16 @@ void QCALLTYPE RuntimeTypeHandle::GetActivationInfo( *ppfnCtor = pCode; *pfCtorIsPublic = pMD->IsPublic(); } + else if (pMT->IsValueType()) + { + *ppfnCtor = NULL; // no ctor call needed; we're creating a boxed default(T) + *pfCtorIsPublic = TRUE; // no ctor call needed => assume 'public' equivalent + } + else + { + // reference type with no parameterless ctor - we can't instantiate this + COMPlusThrow(kMissingMethodException, W("Arg_NoDefCTorWithoutTypeName")); + } } END_QCALL; @@ -2237,6 +2258,48 @@ FCIMPL1(Object*, RuntimeTypeHandle::AllocateComObject, } FCIMPLEND +//************************************************************************************************* +//************************************************************************************************* +//************************************************************************************************* +// ReflectionSerialization +//************************************************************************************************* +//************************************************************************************************* +//************************************************************************************************* +FCIMPL1(Object*, ReflectionSerialization::GetUninitializedObject, ReflectClassBaseObject* objTypeUNSAFE) { + FCALL_CONTRACT; + + OBJECTREF retVal = NULL; + REFLECTCLASSBASEREF objType = (REFLECTCLASSBASEREF) objTypeUNSAFE; + + HELPER_METHOD_FRAME_BEGIN_RET_NOPOLL(); + + TypeHandle type = objType->GetType(); + + RuntimeTypeHandle::ValidateTypeAbleToBeInstantiated(type, true /* fForGetUninitializedInstance */); + + MethodTable* pMT = type.AsMethodTable(); + + // Never allow the allocation of an unitialized ContextBoundObject derived type, these must always be created with a paired + // transparent proxy or the jit will get confused. + +#ifdef FEATURE_COMINTEROP + // Also do not allow allocation of uninitialized RCWs (COM objects). + if (pMT->IsComObjectType()) + COMPlusThrow(kNotSupportedException, W("NotSupported_ManagedActivation")); +#endif // FEATURE_COMINTEROP + + // If it is a nullable, return the underlying type instead. + if (Nullable::IsNullableType(pMT)) + pMT = pMT->GetInstantiation()[0].GetMethodTable(); + + // Allocation will invoke any precise static cctors as needed. + retVal = pMT->Allocate(); + + HELPER_METHOD_FRAME_END(); + return OBJECTREFToObject(retVal); +} +FCIMPLEND + //************************************************************************************************* //************************************************************************************************* //************************************************************************************************* diff --git a/src/coreclr/src/vm/reflectioninvocation.h b/src/coreclr/src/vm/reflectioninvocation.h index 9f85a2a5bf715..bd358e8df7fb4 100644 --- a/src/coreclr/src/vm/reflectioninvocation.h +++ b/src/coreclr/src/vm/reflectioninvocation.h @@ -48,9 +48,7 @@ class ReflectionInvocation { static void QCALLTYPE CompileMethod(MethodDesc * pMD); - static - void QCALLTYPE RunClassConstructor(QCall::TypeHandle pTypeHandle, BOOL fPreciseCctorsOnly); - + static FCDECL1(void, RunClassConstructor, ReflectClassBaseObject *pTypeUNSAFE); static FCDECL1(void, RunModuleConstructor, ReflectModuleBaseObject *pModuleUNSAFE); static FCDECL3(void, PrepareMethod, ReflectMethodObject* pMethodUNSAFE, TypeHandle *pInstantiation, UINT32 cInstantiation); static FCDECL1(void, PrepareDelegate, Object* delegateUNSAFE); @@ -74,6 +72,11 @@ class ReflectionInvocation { static FCDECL3(Object*, AllocateValueType, ReflectClassBaseObject *targetType, Object *valueUNSAFE, CLR_BOOL fForceTypeChange); }; +class ReflectionSerialization { +public: + static FCDECL1(Object*, GetUninitializedObject, ReflectClassBaseObject* objTypeUNSAFE); +}; + class ReflectionEnum { public: static FCDECL1(Object *, InternalGetEnumUnderlyingType, ReflectClassBaseObject *target); diff --git a/src/coreclr/src/vm/runtimehandles.cpp b/src/coreclr/src/vm/runtimehandles.cpp index 9778b01799545..14787a7faefc0 100644 --- a/src/coreclr/src/vm/runtimehandles.cpp +++ b/src/coreclr/src/vm/runtimehandles.cpp @@ -1598,19 +1598,6 @@ FCIMPL2(FC_BOOL_RET, RuntimeTypeHandle::CompareCanonicalHandles, ReflectClassBas } FCIMPLEND -BOOL QCALLTYPE RuntimeTypeHandle::IsConstructedNullableType(QCall::TypeHandle pTypeHandle) -{ - QCALL_CONTRACT_NO_GC_TRANSITION; - - TypeHandle typeHandle = pTypeHandle.AsTypeHandle(); - _ASSERTE(!typeHandle.IsNull()); - - // eqivalent to IsGenericType && !IsGenericTypeDefinition && GetGenericTypeDefinition() == typeof(Nullable<>) - return typeHandle.HasInstantiation() - && !typeHandle.IsGenericTypeDefinition() - && typeHandle.AsMethodTable()->IsNullable(); -} - FCIMPL1(FC_BOOL_RET, RuntimeTypeHandle::HasInstantiation, PTR_ReflectClassBaseObject pTypeUNSAFE) { FCALL_CONTRACT; diff --git a/src/coreclr/src/vm/runtimehandles.h b/src/coreclr/src/vm/runtimehandles.h index 8f505e6fcc31f..7675d1b3bcac9 100644 --- a/src/coreclr/src/vm/runtimehandles.h +++ b/src/coreclr/src/vm/runtimehandles.h @@ -197,9 +197,6 @@ class RuntimeTypeHandle { static FCDECL6(FC_BOOL_RET, SatisfiesConstraints, PTR_ReflectClassBaseObject pGenericParameter, TypeHandle *typeContextArgs, INT32 typeContextCount, TypeHandle *methodContextArgs, INT32 methodContextCount, PTR_ReflectClassBaseObject pGenericArgument); - static - BOOL QCALLTYPE IsConstructedNullableType(QCall::TypeHandle pTypeHandle); - static FCDECL1(FC_BOOL_RET, HasInstantiation, PTR_ReflectClassBaseObject pType); @@ -262,6 +259,10 @@ class RuntimeTypeHandle { static PVOID QCALLTYPE AllocateTypeAssociatedMemory(QCall::TypeHandle type, UINT32 size); + + // Helper methods not called by managed code + + static void ValidateTypeAbleToBeInstantiated(TypeHandle typeHandle, bool fGetUninitializedObject); }; class RuntimeMethodHandle { diff --git a/src/libraries/System.Private.CoreLib/src/Resources/Strings.resx b/src/libraries/System.Private.CoreLib/src/Resources/Strings.resx index 8311773d40414..fb707d1f4e7a2 100644 --- a/src/libraries/System.Private.CoreLib/src/Resources/Strings.resx +++ b/src/libraries/System.Private.CoreLib/src/Resources/Strings.resx @@ -601,6 +601,9 @@ Must specify binding flags describing the invoke operation required (BindingFlags.InvokeMethod CreateInstance GetField SetField GetProperty SetProperty). + + No parameterless constructor defined. + No parameterless constructor defined for type '{0}'. From 11be754c0c9ef5c57c4929e448fe6f68f8c7714d Mon Sep 17 00:00:00 2001 From: Levi Broderick Date: Wed, 25 Nov 2020 16:16:12 -0800 Subject: [PATCH 56/64] More code cleanup --- .../src/System/ActivatorCache.CoreCLR.cs | 10 +++++++++- .../src/System/RuntimeType.CoreCLR.cs | 4 ---- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/src/coreclr/src/System.Private.CoreLib/src/System/ActivatorCache.CoreCLR.cs b/src/coreclr/src/System.Private.CoreLib/src/System/ActivatorCache.CoreCLR.cs index 33b00e70f4f3d..dfafd096dbc7b 100644 --- a/src/coreclr/src/System.Private.CoreLib/src/System/ActivatorCache.CoreCLR.cs +++ b/src/coreclr/src/System.Private.CoreLib/src/System/ActivatorCache.CoreCLR.cs @@ -125,7 +125,15 @@ static void CtorNoopStub(object? uninitializedObject) { } } [MethodImpl(MethodImplOptions.AggressiveInlining)] - internal void CallConstructor(object? uninitializedObject) => _pfnCtor(uninitializedObject); + internal void CallConstructor(object? uninitializedObject) + { +#if DEBUG + Debug.Assert(uninitializedObject is null || uninitializedObject.GetType() == _originalRuntimeType, + "Caller passed an unexpected 'this' parameter to the ctor - possible type safety violation."); +#endif + + _pfnCtor(uninitializedObject); + } } } } diff --git a/src/coreclr/src/System.Private.CoreLib/src/System/RuntimeType.CoreCLR.cs b/src/coreclr/src/System.Private.CoreLib/src/System/RuntimeType.CoreCLR.cs index 4378729df30f0..a3d0c9a840b9d 100644 --- a/src/coreclr/src/System.Private.CoreLib/src/System/RuntimeType.CoreCLR.cs +++ b/src/coreclr/src/System.Private.CoreLib/src/System/RuntimeType.CoreCLR.cs @@ -3967,10 +3967,6 @@ private void CreateInstanceCheckThis() /// [DebuggerStepThrough] [DebuggerHidden] - [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2082:UnrecognizedReflectionPattern", - Justification = "Implementation detail of Activator that linker intrinsically recognizes")] - [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2085:UnrecognizedReflectionPattern", - Justification = "Implementation detail of Activator that linker intrinsically recognizes")] internal object? CreateInstanceDefaultCtor(bool publicOnly, bool skipCheckThis, bool fillCache, bool wrapExceptions) { // Get or create the cached factory. Creating the cache will fail if one From 5d43a912c7ca2b04c67bb8786c20d512d86432c2 Mon Sep 17 00:00:00 2001 From: Levi Broderick Date: Wed, 25 Nov 2020 16:18:56 -0800 Subject: [PATCH 57/64] Rename ActivatorCache source file --- .../src/System.Private.CoreLib/System.Private.CoreLib.csproj | 2 +- ...{ActivatorCache.CoreCLR.cs => RuntimeType.ActivatorCache.cs} | 0 2 files changed, 1 insertion(+), 1 deletion(-) rename src/coreclr/src/System.Private.CoreLib/src/System/{ActivatorCache.CoreCLR.cs => RuntimeType.ActivatorCache.cs} (100%) diff --git a/src/coreclr/src/System.Private.CoreLib/System.Private.CoreLib.csproj b/src/coreclr/src/System.Private.CoreLib/System.Private.CoreLib.csproj index c5bf5c9f45c20..1b227e0521ddb 100644 --- a/src/coreclr/src/System.Private.CoreLib/System.Private.CoreLib.csproj +++ b/src/coreclr/src/System.Private.CoreLib/System.Private.CoreLib.csproj @@ -111,7 +111,6 @@ - @@ -221,6 +220,7 @@ + diff --git a/src/coreclr/src/System.Private.CoreLib/src/System/ActivatorCache.CoreCLR.cs b/src/coreclr/src/System.Private.CoreLib/src/System/RuntimeType.ActivatorCache.cs similarity index 100% rename from src/coreclr/src/System.Private.CoreLib/src/System/ActivatorCache.CoreCLR.cs rename to src/coreclr/src/System.Private.CoreLib/src/System/RuntimeType.ActivatorCache.cs From 063539987f78d9a80520c4fc8b08fd60f80578d7 Mon Sep 17 00:00:00 2001 From: Levi Broderick Date: Wed, 25 Nov 2020 16:20:59 -0800 Subject: [PATCH 58/64] Remove unnecessary asserts, underscores, indentation --- .../RuntimeHelpers.CoreCLR.cs | 2 +- .../src/System/RuntimeHandles.cs | 19 ++++++------------- src/coreclr/src/vm/corelib.h | 2 +- src/coreclr/src/vm/ecalllist.h | 4 ++-- src/coreclr/src/vm/reflectioninvocation.cpp | 6 ++++-- .../src/System/Activator.RuntimeType.cs | 2 +- 6 files changed, 15 insertions(+), 20 deletions(-) diff --git a/src/coreclr/src/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.CoreCLR.cs b/src/coreclr/src/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.CoreCLR.cs index 1f0aa576850c0..95de9b788bfcb 100644 --- a/src/coreclr/src/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.CoreCLR.cs +++ b/src/coreclr/src/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.CoreCLR.cs @@ -382,7 +382,7 @@ internal unsafe struct MethodTable [FieldOffset(InterfaceMapOffset)] public MethodTable** InterfaceMap; - // WFLAGS_HIGH_ENUM & WFLAGS_LOW_ENUM + // WFLAGS_HIGH_ENUM private const uint enum_flag_ContainsPointers = 0x01000000; private const uint enum_flag_HasComponentSize = 0x80000000; private const uint enum_flag_HasTypeEquivalence = 0x00004000; diff --git a/src/coreclr/src/System.Private.CoreLib/src/System/RuntimeHandles.cs b/src/coreclr/src/System.Private.CoreLib/src/System/RuntimeHandles.cs index 213714a839eb6..f8ff5504380a7 100644 --- a/src/coreclr/src/System.Private.CoreLib/src/System/RuntimeHandles.cs +++ b/src/coreclr/src/System.Private.CoreLib/src/System/RuntimeHandles.cs @@ -271,26 +271,19 @@ internal static void GetActivationInfo( delegate* pfnCtorTemp = default; Interop.BOOL fCtorIsPublicTemp = default; - _GetActivationInfo( + GetActivationInfo( ObjectHandleOnStack.Create(ref rt), &pfnAllocatorTemp, &vAllocatorFirstArgTemp, - ppfnCtor: &pfnCtorTemp, - pfCtorIsPublic: &fCtorIsPublicTemp); + &pfnCtorTemp, &fCtorIsPublicTemp); - // Marshal values back to caller - - Debug.Assert(pfnAllocatorTemp != null); pfnAllocator = pfnAllocatorTemp; - - Debug.Assert(vAllocatorFirstArgTemp != null); vAllocatorFirstArg = vAllocatorFirstArgTemp; - - pfnCtor = pfnCtorTemp; // could be null + pfnCtor = pfnCtorTemp; ctorIsPublic = fCtorIsPublicTemp != Interop.BOOL.FALSE; } [DllImport(RuntimeHelpers.QCall, CharSet = CharSet.Unicode)] - private static extern void _GetActivationInfo( + private static extern void GetActivationInfo( ObjectHandleOnStack pRuntimeType, delegate** ppfnAllocator, void** pvAllocatorFirstArg, @@ -298,10 +291,10 @@ private static extern void _GetActivationInfo( Interop.BOOL* pfCtorIsPublic); #if FEATURE_COMINTEROP - // Referenced by unmanaged layer (see _GetActivationInfo). + // Referenced by unmanaged layer (see GetActivationInfo). // First parameter is ComClassFactory*. [MethodImpl(MethodImplOptions.InternalCall)] - private static extern object _AllocateComObject(void* pClassFactory); + private static extern object AllocateComObject(void* pClassFactory); #endif internal RuntimeType GetRuntimeType() diff --git a/src/coreclr/src/vm/corelib.h b/src/coreclr/src/vm/corelib.h index cc0e30392f802..fd22fe782981f 100644 --- a/src/coreclr/src/vm/corelib.h +++ b/src/coreclr/src/vm/corelib.h @@ -372,7 +372,7 @@ DEFINE_METHOD(RT_TYPE_HANDLE, GET_TYPE_HELPER, GetTypeHelper, DEFINE_METHOD(RT_TYPE_HANDLE, PVOID_CTOR, .ctor, IM_RuntimeType_RetVoid) DEFINE_METHOD(RT_TYPE_HANDLE, GETVALUEINTERNAL, GetValueInternal, SM_RuntimeTypeHandle_RetIntPtr) #ifdef FEATURE_COMINTEROP -DEFINE_METHOD(RT_TYPE_HANDLE, ALLOCATECOMOBJECT, _AllocateComObject, SM_VoidPtr_RetObj) +DEFINE_METHOD(RT_TYPE_HANDLE, ALLOCATECOMOBJECT, AllocateComObject, SM_VoidPtr_RetObj) #endif DEFINE_FIELD(RT_TYPE_HANDLE, M_TYPE, m_type) diff --git a/src/coreclr/src/vm/ecalllist.h b/src/coreclr/src/vm/ecalllist.h index cf8f93c7403e1..00cb622a7046d 100644 --- a/src/coreclr/src/vm/ecalllist.h +++ b/src/coreclr/src/vm/ecalllist.h @@ -238,9 +238,9 @@ FCFuncStart(gCOMTypeHandleFuncs) FCFuncElement("IsGenericTypeDefinition", RuntimeTypeHandle::IsGenericTypeDefinition) FCFuncElement("ContainsGenericVariables", RuntimeTypeHandle::ContainsGenericVariables) FCFuncElement("SatisfiesConstraints", RuntimeTypeHandle::SatisfiesConstraints) - QCFuncElement("_GetActivationInfo", RuntimeTypeHandle::GetActivationInfo) + QCFuncElement("GetActivationInfo", RuntimeTypeHandle::GetActivationInfo) #ifdef FEATURE_COMINTEROP - FCFuncElement("_AllocateComObject", RuntimeTypeHandle::AllocateComObject) + FCFuncElement("AllocateComObject", RuntimeTypeHandle::AllocateComObject) #endif // FEATURE_COMINTEROP FCFuncElement("CompareCanonicalHandles", RuntimeTypeHandle::CompareCanonicalHandles) FCIntrinsic("GetValueInternal", RuntimeTypeHandle::GetValueInternal, CORINFO_INTRINSIC_RTH_GetValueInternal) diff --git a/src/coreclr/src/vm/reflectioninvocation.cpp b/src/coreclr/src/vm/reflectioninvocation.cpp index 62af3d74e879c..21e0b52973d33 100644 --- a/src/coreclr/src/vm/reflectioninvocation.cpp +++ b/src/coreclr/src/vm/reflectioninvocation.cpp @@ -2064,12 +2064,14 @@ void RuntimeTypeHandle::ValidateTypeAbleToBeInstantiated( } // Don't allow generics instantiated over __Canon - if (pMT->IsSharedByGenericInstantiations()) { + if (pMT->IsSharedByGenericInstantiations()) + { COMPlusThrow(kNotSupportedException, W("NotSupported_Type")); } // Don't allow ref structs - if (pMT->IsByRefLike()) { + if (pMT->IsByRefLike()) + { COMPlusThrow(kNotSupportedException, W("NotSupported_ByRefLike")); } } diff --git a/src/libraries/System.Private.CoreLib/src/System/Activator.RuntimeType.cs b/src/libraries/System.Private.CoreLib/src/System/Activator.RuntimeType.cs index 2f54f175199ee..09d3bdf608c21 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Activator.RuntimeType.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Activator.RuntimeType.cs @@ -2,8 +2,8 @@ // The .NET Foundation licenses this file to you under the MIT license. using System.Diagnostics.CodeAnalysis; -using System.Globalization; using System.Reflection; +using System.Globalization; using System.Runtime.Loader; using System.Runtime.Remoting; using System.Threading; From 9ac3c3fd6a1c49b7a6c01929e53af2820126fcb1 Mon Sep 17 00:00:00 2001 From: Levi Broderick Date: Wed, 25 Nov 2020 16:45:24 -0800 Subject: [PATCH 59/64] Fix [DynamicallyAccessedMembers] annotations --- .../src/System/RuntimeType.ActivatorCache.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/coreclr/src/System.Private.CoreLib/src/System/RuntimeType.ActivatorCache.cs b/src/coreclr/src/System.Private.CoreLib/src/System/RuntimeType.ActivatorCache.cs index dfafd096dbc7b..93991c099681b 100644 --- a/src/coreclr/src/System.Private.CoreLib/src/System/RuntimeType.ActivatorCache.cs +++ b/src/coreclr/src/System.Private.CoreLib/src/System/RuntimeType.ActivatorCache.cs @@ -28,8 +28,7 @@ private sealed unsafe class ActivatorCache private readonly RuntimeType _originalRuntimeType; #endif - internal ActivatorCache( - [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.NonPublicConstructors)] RuntimeType rt) + internal ActivatorCache(RuntimeType rt) { Debug.Assert(rt != null); From a06ef7326472bfcfb8f5a8f9f8ecadd97de38f6d Mon Sep 17 00:00:00 2001 From: Levi Broderick Date: Wed, 25 Nov 2020 17:22:17 -0800 Subject: [PATCH 60/64] Fix bad assert --- .../src/System/RuntimeType.ActivatorCache.cs | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/coreclr/src/System.Private.CoreLib/src/System/RuntimeType.ActivatorCache.cs b/src/coreclr/src/System.Private.CoreLib/src/System/RuntimeType.ActivatorCache.cs index 93991c099681b..331237cc2b7db 100644 --- a/src/coreclr/src/System.Private.CoreLib/src/System/RuntimeType.ActivatorCache.cs +++ b/src/coreclr/src/System.Private.CoreLib/src/System/RuntimeType.ActivatorCache.cs @@ -85,7 +85,7 @@ internal ActivatorCache(RuntimeType rt) // would have thrown an exception if 'rt' were a normal reference type // without a ctor. - if (_pfnCtor is null) + if (_pfnCtor == null) { static void CtorNoopStub(object? uninitializedObject) { } _pfnCtor = &CtorNoopStub; // we use null singleton pattern if no ctor call is necessary @@ -127,8 +127,12 @@ static void CtorNoopStub(object? uninitializedObject) { } internal void CallConstructor(object? uninitializedObject) { #if DEBUG - Debug.Assert(uninitializedObject is null || uninitializedObject.GetType() == _originalRuntimeType, - "Caller passed an unexpected 'this' parameter to the ctor - possible type safety violation."); + if (uninitializedObject != null && !uninitializedObject.GetType().IsEquivalentTo(_originalRuntimeType)) + { + Debug.Fail("Caller passed an unexpected 'this' parameter to ctor - possible type safety violation." + + Environment.NewLineConst + "Expected type: " + (_originalRuntimeType ?? (object)"") + + Environment.NewLineConst + "Actual type: " + uninitializedObject.GetType()); + } #endif _pfnCtor(uninitializedObject); From 1b1261fdacd709b41537d9b6154fd548f1d8f941 Mon Sep 17 00:00:00 2001 From: Levi Broderick Date: Wed, 25 Nov 2020 18:07:21 -0800 Subject: [PATCH 61/64] Update src/coreclr/src/vm/reflectioninvocation.cpp Co-authored-by: Jan Kotas --- src/coreclr/src/vm/reflectioninvocation.cpp | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/coreclr/src/vm/reflectioninvocation.cpp b/src/coreclr/src/vm/reflectioninvocation.cpp index 21e0b52973d33..010c94711e7b5 100644 --- a/src/coreclr/src/vm/reflectioninvocation.cpp +++ b/src/coreclr/src/vm/reflectioninvocation.cpp @@ -2141,9 +2141,8 @@ void QCALLTYPE RuntimeTypeHandle::GetActivationInfo( // .. on non-Windows, treat as a normal class, type has no special handling in VM. // - For [ComImport] class, treat as a normal class. VM will replace default // ctor with COM activation logic on supported platforms, else ctor itself will PNSE. - if (pMT->IsComObjectType()) + if (IsComObjectClass(typeHandle)) { - if (IsComObjectClass(typeHandle)) { #ifdef FEATURE_COMINTEROP_UNMANAGED_ACTIVATION // Need to enter cooperative mode to manipulate OBJECTREFs @@ -2641,4 +2640,3 @@ FCIMPL2(FC_BOOL_RET, ReflectionEnum::InternalHasFlag, Object *pRefThis, Object* FC_RETURN_BOOL(cmp); } FCIMPLEND - From 94f86142a70861c6593dfade3062f1f1b58823c2 Mon Sep 17 00:00:00 2001 From: Levi Broderick Date: Wed, 25 Nov 2020 18:06:14 -0800 Subject: [PATCH 62/64] PR feedback --- src/coreclr/src/vm/reflectioninvocation.cpp | 47 +++++++++------------ 1 file changed, 21 insertions(+), 26 deletions(-) diff --git a/src/coreclr/src/vm/reflectioninvocation.cpp b/src/coreclr/src/vm/reflectioninvocation.cpp index 010c94711e7b5..a806f17473b59 100644 --- a/src/coreclr/src/vm/reflectioninvocation.cpp +++ b/src/coreclr/src/vm/reflectioninvocation.cpp @@ -2113,9 +2113,6 @@ void QCALLTYPE RuntimeTypeHandle::GetActivationInfo( TypeHandle typeHandle = NULL; - // If we're activating __ComObject. - void* pClassFactory = NULL; - BEGIN_QCALL; { @@ -2134,36 +2131,41 @@ void QCALLTYPE RuntimeTypeHandle::GetActivationInfo( #ifdef FEATURE_COMINTEROP // COM allocation can involve the __ComObject base type (with attached CLSID) or a - // VM-implemented [ComImport] class. For GetUninitializedObject, we block all COM types. - // For CreateInstance, the flowchart is: + // VM-implemented [ComImport] class. For CreateInstance, the flowchart is: // - For __ComObject, // .. on Windows, bypass normal newobj logic and use ComClassFactory::CreateInstance. // .. on non-Windows, treat as a normal class, type has no special handling in VM. // - For [ComImport] class, treat as a normal class. VM will replace default // ctor with COM activation logic on supported platforms, else ctor itself will PNSE. + // IsComObjectClass is the correct way to check for __ComObject specifically if (IsComObjectClass(typeHandle)) { - { + void* pClassFactory = NULL; + #ifdef FEATURE_COMINTEROP_UNMANAGED_ACTIVATION + { // Need to enter cooperative mode to manipulate OBJECTREFs GCX_COOP(); SyncBlock* pSyncBlock = pRuntimeType.Get()->GetSyncBlock(); pClassFactory = (void*)pSyncBlock->GetInteropInfo()->GetComClassFactory(); + } #endif // FEATURE_COMINTEROP_UNMANAGED_ACTIVATION - if (pClassFactory == NULL) - { - COMPlusThrow(kInvalidComObjectException, IDS_EE_NO_BACKING_CLASS_FACTORY); - } + if (pClassFactory == NULL) + { + // no factory *or* unmanaged activation is not enabled in this runtime + COMPlusThrow(kInvalidComObjectException, IDS_EE_NO_BACKING_CLASS_FACTORY); } + + // managed sig: ComClassFactory* -> object (via FCALL) + *ppfnAllocator = CoreLibBinder::GetMethod(METHOD__RT_TYPE_HANDLE__ALLOCATECOMOBJECT)->GetMultiCallableAddrOfCode(); + *pvAllocatorFirstArg = pClassFactory; + *ppfnCtor = NULL; // no ctor call needed; activation handled entirely by the allocator + *pfCtorIsPublic = TRUE; // no ctor call needed => assume 'public' equivalent } + else #endif // FEATURE_COMINTEROP - - pMT->EnsureInstanceActive(); - - // All checks passed! Pass parameters back to the caller. - - if (Nullable::IsNullableType(typeHandle)) + if (pMT->IsNullable()) { // CreateInstance returns null given Nullable *ppfnAllocator = NULL; @@ -2171,16 +2173,6 @@ void QCALLTYPE RuntimeTypeHandle::GetActivationInfo( *ppfnCtor = NULL; *pfCtorIsPublic = TRUE; // no ctor call needed => assume 'public' equivalent } -#ifdef FEATURE_COMINTEROP - else if (pClassFactory != NULL) - { - // managed sig: ComClassFactory* -> object (via FCALL) - *ppfnAllocator = CoreLibBinder::GetMethod(METHOD__RT_TYPE_HANDLE__ALLOCATECOMOBJECT)->GetMultiCallableAddrOfCode(); - *pvAllocatorFirstArg = pClassFactory; - *ppfnCtor = NULL; // no ctor call needed; activation handled entirely by the allocator - *pfCtorIsPublic = TRUE; // no ctor call needed => assume 'public' equivalent - } -#endif // FEATURE_COMINTEROP else { // managed sig: MethodTable* -> object (via JIT helper) @@ -2212,6 +2204,8 @@ void QCALLTYPE RuntimeTypeHandle::GetActivationInfo( } } + pMT->EnsureInstanceActive(); + END_QCALL; } @@ -2640,3 +2634,4 @@ FCIMPL2(FC_BOOL_RET, ReflectionEnum::InternalHasFlag, Object *pRefThis, Object* FC_RETURN_BOOL(cmp); } FCIMPLEND + From c73e9fabba4f50295350b5fc8fa1f90c798ab2f2 Mon Sep 17 00:00:00 2001 From: Jan Kotas Date: Wed, 25 Nov 2020 18:44:34 -0800 Subject: [PATCH 63/64] Update src/coreclr/src/vm/reflectioninvocation.cpp --- src/coreclr/src/vm/reflectioninvocation.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/coreclr/src/vm/reflectioninvocation.cpp b/src/coreclr/src/vm/reflectioninvocation.cpp index a806f17473b59..cef2ecb6d96c3 100644 --- a/src/coreclr/src/vm/reflectioninvocation.cpp +++ b/src/coreclr/src/vm/reflectioninvocation.cpp @@ -2107,7 +2107,7 @@ void QCALLTYPE RuntimeTypeHandle::GetActivationInfo( PRECONDITION(*ppfnAllocator == NULL); PRECONDITION(*pvAllocatorFirstArg == NULL); PRECONDITION(*ppfnCtor == NULL); - PRECONDITION(*pfCtorIsPublic == NULL); + PRECONDITION(*pfCtorIsPublic == FALSE); } CONTRACTL_END; @@ -2634,4 +2634,3 @@ FCIMPL2(FC_BOOL_RET, ReflectionEnum::InternalHasFlag, Object *pRefThis, Object* FC_RETURN_BOOL(cmp); } FCIMPLEND - From 359a58881bc2228c3bad2acb6bad05b204345876 Mon Sep 17 00:00:00 2001 From: Levi Broderick Date: Wed, 25 Nov 2020 20:39:13 -0800 Subject: [PATCH 64/64] Remove incorrect assert in ActivatorCache --- .../src/System/RuntimeType.ActivatorCache.cs | 14 +------------- 1 file changed, 1 insertion(+), 13 deletions(-) diff --git a/src/coreclr/src/System.Private.CoreLib/src/System/RuntimeType.ActivatorCache.cs b/src/coreclr/src/System.Private.CoreLib/src/System/RuntimeType.ActivatorCache.cs index 331237cc2b7db..abf4f4afa4410 100644 --- a/src/coreclr/src/System.Private.CoreLib/src/System/RuntimeType.ActivatorCache.cs +++ b/src/coreclr/src/System.Private.CoreLib/src/System/RuntimeType.ActivatorCache.cs @@ -124,19 +124,7 @@ static void CtorNoopStub(object? uninitializedObject) { } } [MethodImpl(MethodImplOptions.AggressiveInlining)] - internal void CallConstructor(object? uninitializedObject) - { -#if DEBUG - if (uninitializedObject != null && !uninitializedObject.GetType().IsEquivalentTo(_originalRuntimeType)) - { - Debug.Fail("Caller passed an unexpected 'this' parameter to ctor - possible type safety violation." - + Environment.NewLineConst + "Expected type: " + (_originalRuntimeType ?? (object)"") - + Environment.NewLineConst + "Actual type: " + uninitializedObject.GetType()); - } -#endif - - _pfnCtor(uninitializedObject); - } + internal void CallConstructor(object? uninitializedObject) => _pfnCtor(uninitializedObject); } } }