diff --git a/src/coreclr/System.Private.CoreLib/System.Private.CoreLib.csproj b/src/coreclr/System.Private.CoreLib/System.Private.CoreLib.csproj index 08f6699e0a02db..a6cb1fd6d1039b 100644 --- a/src/coreclr/System.Private.CoreLib/System.Private.CoreLib.csproj +++ b/src/coreclr/System.Private.CoreLib/System.Private.CoreLib.csproj @@ -209,12 +209,12 @@ - - - - - - + + + + + + @@ -248,7 +248,8 @@ Common\System\Collections\Generic\ArrayBuilder.cs - + + @@ -328,11 +329,7 @@ - + diff --git a/src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.CoreCLR.cs b/src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.CoreCLR.cs index 534a251630cdac..4e75d7db895ce4 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.CoreCLR.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.CoreCLR.cs @@ -191,14 +191,9 @@ public static object GetUninitializedObject( throw new SerializationException(SR.Format(SR.Serialization_InvalidType, type)); } - object? obj = null; - GetUninitializedObject(new QCallTypeHandle(ref rt), ObjectHandleOnStack.Create(ref obj)); - return obj!; + return rt.GetUninitializedObject(); } - [LibraryImport(QCall, EntryPoint = "ReflectionSerialization_GetUninitializedObject")] - private static partial void GetUninitializedObject(QCallTypeHandle type, ObjectHandleOnStack retObject); - [MethodImpl(MethodImplOptions.InternalCall)] internal static extern object AllocateUninitializedClone(object obj); diff --git a/src/coreclr/System.Private.CoreLib/src/System/RuntimeType.ActivatorCache.cs b/src/coreclr/System.Private.CoreLib/src/System/RuntimeType.ActivatorCache.cs index d82daa26f0ccf4..4d73cfad391430 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/RuntimeType.ActivatorCache.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/RuntimeType.ActivatorCache.cs @@ -24,6 +24,8 @@ private sealed unsafe class ActivatorCache private readonly delegate* _pfnCtor; private readonly bool _ctorIsPublic; + private CreateUninitializedCache? _createUninitializedCache; + #if DEBUG private readonly RuntimeType _originalRuntimeType; #endif @@ -110,14 +112,8 @@ static void CtorNoopStub(object? uninitializedObject) { } // as the object itself will keep the type alive. #if DEBUG - if (_originalRuntimeType != rt) - { - Debug.Fail("Caller passed the wrong RuntimeType to this routine." - + Environment.NewLineConst + "Expected: " + (_originalRuntimeType ?? (object)"") - + Environment.NewLineConst + "Actual: " + (rt ?? (object)"")); - } + CheckOriginalRuntimeType(rt); #endif - object? retVal = _pfnAllocator(_allocatorFirstArg); GC.KeepAlive(rt); return retVal; @@ -125,6 +121,27 @@ static void CtorNoopStub(object? uninitializedObject) { } [MethodImpl(MethodImplOptions.AggressiveInlining)] internal void CallConstructor(object? uninitializedObject) => _pfnCtor(uninitializedObject); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal CreateUninitializedCache GetCreateUninitializedCache(RuntimeType rt) + { +#if DEBUG + CheckOriginalRuntimeType(rt); +#endif + return _createUninitializedCache ??= new CreateUninitializedCache(rt); + } + +#if DEBUG + private void CheckOriginalRuntimeType(RuntimeType rt) + { + if (_originalRuntimeType != rt) + { + Debug.Fail("Caller passed the wrong RuntimeType to this routine." + + Environment.NewLineConst + "Expected: " + (_originalRuntimeType ?? (object)"") + + Environment.NewLineConst + "Actual: " + (rt ?? (object)"")); + } + } +#endif } } } diff --git a/src/coreclr/System.Private.CoreLib/src/System/RuntimeType.CoreCLR.cs b/src/coreclr/System.Private.CoreLib/src/System/RuntimeType.CoreCLR.cs index d0c77aeeb9dbb7..1329f1f7140b43 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/RuntimeType.CoreCLR.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/RuntimeType.CoreCLR.cs @@ -3879,6 +3879,31 @@ private void CreateInstanceCheckThis() } } + /// + /// Helper to get instances of uninitialized objects. + /// + [DebuggerStepThrough] + [DebuggerHidden] + internal object GetUninitializedObject() + { + object? genericCache = GenericCache; + + if (genericCache is not CreateUninitializedCache cache) + { + if (genericCache is ActivatorCache activatorCache) + { + cache = activatorCache.GetCreateUninitializedCache(this); + } + else + { + cache = new CreateUninitializedCache(this); + GenericCache = cache; + } + } + + return cache.CreateUninitializedObject(this); + } + /// /// Helper to invoke the default (parameterless) constructor. /// diff --git a/src/coreclr/System.Private.CoreLib/src/System/RuntimeType.CreateUninitializedCache.CoreCLR.cs b/src/coreclr/System.Private.CoreLib/src/System/RuntimeType.CreateUninitializedCache.CoreCLR.cs new file mode 100644 index 00000000000000..710b1b507e4ef2 --- /dev/null +++ b/src/coreclr/System.Private.CoreLib/src/System/RuntimeType.CreateUninitializedCache.CoreCLR.cs @@ -0,0 +1,80 @@ +// 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.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +namespace System +{ + internal sealed partial class RuntimeType + { + /// + /// A cache which allows optimizing . + /// + private sealed unsafe partial class CreateUninitializedCache + { + // The managed calli to the newobj allocator, plus its first argument (MethodTable*). + private readonly delegate* _pfnAllocator; + private readonly void* _allocatorFirstArg; + +#if DEBUG + private readonly RuntimeType _originalRuntimeType; +#endif + + internal CreateUninitializedCache(RuntimeType rt) + { + Debug.Assert(rt != null); + +#if DEBUG + _originalRuntimeType = rt; +#endif + + GetCreateUninitializedInfo(rt, out _pfnAllocator, out _allocatorFirstArg); + } + + internal object CreateUninitializedObject(RuntimeType rt) + { +#if DEBUG + 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); + GC.KeepAlive(rt); + return retVal; + } + + /// + /// Given a RuntimeType, returns information about how to create uninitialized instances + /// of it via calli semantics. + /// + private static void GetCreateUninitializedInfo( + RuntimeType rt, + out delegate* pfnAllocator, + out void* vAllocatorFirstArg) + { + Debug.Assert(rt != null); + + delegate* pfnAllocatorTemp = default; + void* vAllocatorFirstArgTemp = default; + + GetCreateUninitializedInfo( + new QCallTypeHandle(ref rt), + &pfnAllocatorTemp, &vAllocatorFirstArgTemp); + + pfnAllocator = pfnAllocatorTemp; + vAllocatorFirstArg = vAllocatorFirstArgTemp; + } + + [LibraryImport(RuntimeHelpers.QCall, EntryPoint = "ReflectionSerialization_GetCreateUninitializedObjectInfo")] + private static partial void GetCreateUninitializedInfo( + QCallTypeHandle type, + delegate** ppfnAllocator, + void** pvAllocatorFirstArg); + } + } +} diff --git a/src/coreclr/vm/qcallentrypoints.cpp b/src/coreclr/vm/qcallentrypoints.cpp index 3e149e1a763a28..58d9b2006e9059 100644 --- a/src/coreclr/vm/qcallentrypoints.cpp +++ b/src/coreclr/vm/qcallentrypoints.cpp @@ -314,7 +314,7 @@ static const Entry s_QCall[] = DllImportEntry(ReflectionInvocation_RunModuleConstructor) DllImportEntry(ReflectionInvocation_CompileMethod) DllImportEntry(ReflectionInvocation_PrepareMethod) - DllImportEntry(ReflectionSerialization_GetUninitializedObject) + DllImportEntry(ReflectionSerialization_GetCreateUninitializedObjectInfo) #if defined(FEATURE_COMWRAPPERS) DllImportEntry(ComWrappers_GetIUnknownImpl) DllImportEntry(ComWrappers_TryGetComInstance) diff --git a/src/coreclr/vm/reflectioninvocation.cpp b/src/coreclr/vm/reflectioninvocation.cpp index 72cf39758b1fdb..e12ade403c6b5c 100644 --- a/src/coreclr/vm/reflectioninvocation.cpp +++ b/src/coreclr/vm/reflectioninvocation.cpp @@ -1862,9 +1862,19 @@ FCIMPLEND //************************************************************************************************* //************************************************************************************************* //************************************************************************************************* -extern "C" void QCALLTYPE ReflectionSerialization_GetUninitializedObject(QCall::TypeHandle pType, QCall::ObjectHandleOnStack retObject) +extern "C" void QCALLTYPE ReflectionSerialization_GetCreateUninitializedObjectInfo( + QCall::TypeHandle pType, + PCODE* ppfnAllocator, + void** pvAllocatorFirstArg) { - QCALL_CONTRACT; + CONTRACTL{ + QCALL_CHECK; + PRECONDITION(CheckPointer(ppfnAllocator)); + PRECONDITION(CheckPointer(pvAllocatorFirstArg)); + PRECONDITION(*ppfnAllocator == NULL); + PRECONDITION(*pvAllocatorFirstArg == NULL); + } + CONTRACTL_END; BEGIN_QCALL; @@ -1880,14 +1890,19 @@ extern "C" void QCALLTYPE ReflectionSerialization_GetUninitializedObject(QCall:: COMPlusThrow(kNotSupportedException, W("NotSupported_ManagedActivation")); #endif // FEATURE_COMINTEROP - // If it is a nullable, return the underlying type instead. + // If it is a nullable, return the allocator for the underlying type instead. if (pMT->IsNullable()) pMT = pMT->GetInstantiation()[0].GetMethodTable(); + bool fHasSideEffectsUnused; + *ppfnAllocator = CEEJitInfo::getHelperFtnStatic(CEEInfo::getNewHelperStatic(pMT, &fHasSideEffectsUnused)); + *pvAllocatorFirstArg = pMT; + + pMT->EnsureInstanceActive(); + + if (pMT->HasPreciseInitCctors()) { - GCX_COOP(); - // Allocation will invoke any precise static cctors as needed. - retObject.Set(pMT->Allocate()); + pMT->CheckRunClassInitAsIfConstructingThrowing(); } END_QCALL; diff --git a/src/coreclr/vm/reflectioninvocation.h b/src/coreclr/vm/reflectioninvocation.h index 8ede04ba8dfc03..967e9744790e93 100644 --- a/src/coreclr/vm/reflectioninvocation.h +++ b/src/coreclr/vm/reflectioninvocation.h @@ -72,7 +72,7 @@ extern "C" void QCALLTYPE ReflectionInvocation_RunModuleConstructor(QCall::Modul extern "C" void QCALLTYPE ReflectionInvocation_PrepareMethod(MethodDesc* pMD, TypeHandle *pInstantiation, UINT32 cInstantiation); -extern "C" void QCALLTYPE ReflectionSerialization_GetUninitializedObject(QCall::TypeHandle pType, QCall::ObjectHandleOnStack retObject); +extern "C" void QCALLTYPE ReflectionSerialization_GetCreateUninitializedObjectInfo(QCall::TypeHandle pType, PCODE* ppfnAllocator, void** pvAllocatorFirstArg); class ReflectionEnum { public: