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 1b227e0521ddbe..197b3a84a85191 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 @@
+
@@ -220,7 +221,6 @@
-
diff --git a/src/coreclr/src/System.Private.CoreLib/src/System/ActivationFactory.cs b/src/coreclr/src/System.Private.CoreLib/src/System/ActivationFactory.cs
new file mode 100644
index 00000000000000..619e0ab406ca84
--- /dev/null
+++ b/src/coreclr/src/System.Private.CoreLib/src/System/ActivationFactory.cs
@@ -0,0 +1,219 @@
+// 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;
+
+namespace System
+{
+ ///
+ /// A factory which allows optimizing ,
+ /// , and related APIs.
+ /// Requires a parameterless ctor (public or non-public).
+ ///
+ internal unsafe sealed class ActivationFactory
+ {
+ // 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 void* _allocatorFirstArg;
+
+ // The managed calli to the parameterless ctor, taking "this" (as object) as its first argument.
+ // mgd sig: object -> void
+ private readonly delegate* _pfnCtor;
+ private readonly bool _ctorIsPublic;
+
+ private readonly RuntimeType _originalRuntimeType;
+
+ internal ActivationFactory(RuntimeType rt)
+ {
+ Debug.Assert(rt != null);
+
+ _originalRuntimeType = rt;
+
+ // 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();
+
+ try
+ {
+ RuntimeTypeHandle.GetActivationInfo(rt,
+ out _pfnAllocator!, out _allocatorFirstArg,
+ out _pfnCtor!, out _ctorIsPublic);
+ }
+ catch (Exception ex)
+ {
+ TryThrowFriendlyException(_originalRuntimeType, ex);
+ 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 == null)
+ {
+ static void CtorNoopStub(object? uninitializedObject) { }
+ _pfnCtor = &CtorNoopStub; // we use null singleton pattern if no ctor call is necessary
+
+ 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.
+ }
+
+ internal bool CtorIsPublic => _ctorIsPublic;
+
+ ///
+ /// Calls the default ctor over an existing zero-inited instance of the target type.
+ ///
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ internal void CallConstructor(object? uninitializedObject) => _pfnCtor(uninitializedObject);
+
+ internal Delegate CreateDelegate(RuntimeType delegateType)
+ {
+ Debug.Assert(delegateType is not null);
+
+ // We only allow Func and Func (the latter only for reference types).
+ // This could probably be a Debug.Assert instead of a runtime check, but it's not *that*
+ // expensive in the grand scheme of things, so may as well do it.
+
+ if (delegateType != typeof(Func))
+ {
+ if (_originalRuntimeType.IsValueType || delegateType != typeof(Func<>).MakeGenericType(_originalRuntimeType))
+ {
+ Debug.Fail($"Caller provided an unexpected RuntimeType: {delegateType}");
+ Environment.FailFast("Potential type safety violation in ActivationFactory.");
+ }
+ }
+
+ return Delegate.CreateDelegateUnsafe(delegateType, this, (IntPtr)(delegate*)&CreateInstance);
+ }
+
+ ///
+ /// Constructs a new instance of the target type, including calling the default ctor if needed.
+ ///
+ private static object? CreateInstance(ActivationFactory @this)
+ {
+ object? newObj = @this.GetUninitializedObject();
+ @this.CallConstructor(newObj);
+ return newObj;
+ }
+
+ ///
+ /// Validates that this instance is a factory for the type desired by the caller.
+ ///
+ [Conditional("DEBUG")]
+ internal void DebugValidateExpectedType(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)""));
+ }
+ }
+
+ ///
+ /// Allocates a new zero-inited instance of the target type, but does not run any ctors.
+ ///
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ internal object? GetUninitializedObject()
+ {
+ object? retVal = _pfnAllocator(_allocatorFirstArg);
+ GC.KeepAlive(this); // roots RuntimeType until allocation is completed
+ return retVal;
+ }
+
+ [StackTraceHidden]
+ internal static void TryThrowFriendlyException(RuntimeType rt, 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.
+
+ 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);
+ }
+ }
+ }
+
+ ///
+ /// An geared toward structs.
+ /// Requires no parameterless ctor or a public parameterless ctor.
+ ///
+ internal unsafe sealed class ActivationFactory : IActivationFactory where T : struct
+ {
+ private readonly delegate*[ _pfnCtor; // may be populated by CreateDelegate method
+
+ public ActivationFactory()
+ {
+ delegate*][ pfnCtor;
+ bool ctorIsPublic;
+
+ try
+ {
+ RuntimeTypeHandle.GetActivationInfoForStruct((RuntimeType)typeof(T), out pfnCtor, out ctorIsPublic);
+ }
+ catch (Exception ex)
+ {
+ ActivationFactory.TryThrowFriendlyException((RuntimeType)typeof(T), ex);
+ throw; // can't make a friendlier message, rethrow original exception
+ }
+
+ if (pfnCtor != null && !ctorIsPublic)
+ {
+ throw new MissingMethodException(SR.Format(SR.Arg_NoDefCTor, (RuntimeType)typeof(T)));
+ }
+
+ _pfnCtor = (delegate*][)pfnCtor;
+ }
+
+ private T CreateNoCtor()
+ {
+ Debug.Assert(_pfnCtor == null);
+ return default;
+ }
+
+ private T CreateWithCtor()
+ {
+ Debug.Assert(_pfnCtor != null);
+ T newObj = default;
+ _pfnCtor(ref newObj);
+ return newObj;
+ }
+
+ Delegate IActivationFactory.GetCreateInstanceDelegate()
+ => (_pfnCtor == null) ? (Func])CreateNoCtor : (Func)CreateWithCtor;
+ }
+
+ internal interface IActivationFactory
+ {
+ Delegate GetCreateInstanceDelegate();
+ }
+}
diff --git a/src/coreclr/src/System.Private.CoreLib/src/System/Delegate.CoreCLR.cs b/src/coreclr/src/System.Private.CoreLib/src/System/Delegate.CoreCLR.cs
index ab338cd0bc9cdf..9f98477fca4d78 100644
--- a/src/coreclr/src/System.Private.CoreLib/src/System/Delegate.CoreCLR.cs
+++ b/src/coreclr/src/System.Private.CoreLib/src/System/Delegate.CoreCLR.cs
@@ -434,10 +434,29 @@ internal static Delegate CreateDelegateNoSecurityCheck(Type type, object? target
[MethodImpl(MethodImplOptions.InternalCall)]
internal static extern bool InternalEqualTypes(object a, object b);
- // Used by the ctor. Do not call directly.
+ // Used by the ctor.
// The name of this function will appear in managed stacktraces as delegate constructor.
[MethodImpl(MethodImplOptions.InternalCall)]
- private extern void DelegateConstruct(object target, IntPtr slot);
+ private extern void DelegateConstruct(object? target, IntPtr slot);
+
+ ///
+ /// Creates a delegate with the specified first argument and function pointer.
+ /// No type safety checks take place. Caller must validate that all arguments are valid.
+ /// If no 'this' parameter is specified, caller must manually keep fnPtr alive until instantiation is complete.
+ ///
+ /// The type of the delegate to create. Must be a constructed delegate type.
+ /// The 'this' parameter to wrap. May be null.
+ /// The target function pointer. Must be compatible with the delegate type.
+ internal static Delegate CreateDelegateUnsafe(RuntimeType delegateType, object? target, IntPtr fnPtr)
+ {
+ Debug.Assert(delegateType is not null);
+ Debug.Assert(delegateType.IsDelegate());
+ Debug.Assert(fnPtr != IntPtr.Zero);
+
+ Delegate del = (Delegate)RuntimeHelpers.GetUninitializedObjectSkipChecks(delegateType);
+ del.DelegateConstruct(target, fnPtr);
+ return del;
+ }
[MethodImpl(MethodImplOptions.InternalCall)]
internal extern IntPtr GetMulticastInvoke();
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 6f4d51917f4a51..a7e982e0575e4a 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
@@ -164,12 +164,25 @@ public static object GetUninitializedObject(
}
object? obj = null;
- GetUninitializedObject(new QCallTypeHandle(ref rt), ObjectHandleOnStack.Create(ref obj));
+ GetUninitializedObject(new QCallTypeHandle(ref rt), fSkipChecks: Interop.BOOL.FALSE, ObjectHandleOnStack.Create(ref obj));
+ return obj!;
+ }
+
+ ///
+ /// Creates a new zero-inited object on the heap, bypassing all verification checks.
+ /// Caller is responsible for ensuring not attempting to instantiate an abstract type, ref struct, etc.
+ ///
+ internal static object GetUninitializedObjectSkipChecks(RuntimeType type)
+ {
+ Debug.Assert(type is not null);
+
+ object? obj = null;
+ GetUninitializedObject(new QCallTypeHandle(ref type), fSkipChecks: Interop.BOOL.TRUE, ObjectHandleOnStack.Create(ref obj));
return obj!;
}
[DllImport(RuntimeHelpers.QCall)]
- private static extern void GetUninitializedObject(QCallTypeHandle type, ObjectHandleOnStack retObject);
+ private static extern void GetUninitializedObject(QCallTypeHandle type, Interop.BOOL fSkipChecks, ObjectHandleOnStack retObject);
[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 fbca319a20c229..7b7ee272ebdb01 100644
--- a/src/coreclr/src/System.Private.CoreLib/src/System/RuntimeHandles.cs
+++ b/src/coreclr/src/System.Private.CoreLib/src/System/RuntimeHandles.cs
@@ -273,6 +273,7 @@ internal static void GetActivationInfo(
GetActivationInfo(
ObjectHandleOnStack.Create(ref rt),
+ fAvoidBoxing: Interop.BOOL.FALSE,
&pfnAllocatorTemp, &vAllocatorFirstArgTemp,
&pfnCtorTemp, &fCtorIsPublicTemp);
@@ -282,9 +283,38 @@ internal static void GetActivationInfo(
ctorIsPublic = fCtorIsPublicTemp != Interop.BOOL.FALSE;
}
+ ///
+ /// Given a RuntimeType for a struct, 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 void GetActivationInfoForStruct(
+ RuntimeType rt,
+ out delegate*[ pfnCtor,
+ out bool ctorIsPublic)
+ {
+ Debug.Assert(rt != null);
+ Debug.Assert(rt.IsValueType);
+
+ delegate*] pfnAllocatorTemp = default;
+ void* vAllocatorFirstArgTemp = default;
+ delegate*[ pfnCtorTemp = default;
+ Interop.BOOL fCtorIsPublicTemp = default;
+
+ GetActivationInfo(
+ ObjectHandleOnStack.Create(ref rt),
+ fAvoidBoxing: Interop.BOOL.TRUE,
+ &pfnAllocatorTemp, &vAllocatorFirstArgTemp,
+ (delegate*]*)&pfnCtorTemp, &fCtorIsPublicTemp);
+
+ pfnCtor = pfnCtorTemp;
+ ctorIsPublic = fCtorIsPublicTemp != Interop.BOOL.FALSE;
+ }
+
[DllImport(RuntimeHelpers.QCall, CharSet = CharSet.Unicode)]
private static extern void GetActivationInfo(
ObjectHandleOnStack pRuntimeType,
+ Interop.BOOL fAvoidBoxing,
delegate** ppfnAllocator,
void** pvAllocatorFirstArg,
delegate** ppfnCtor,
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
deleted file mode 100644
index abf4f4afa4410f..00000000000000
--- a/src/coreclr/src/System.Private.CoreLib/src/System/RuntimeType.ActivatorCache.cs
+++ /dev/null
@@ -1,130 +0,0 @@
-// Licensed to the .NET Foundation under one or more agreements.
-// The .NET Foundation licenses this file to you under the MIT license.
-
-using System.Diagnostics;
-using System.Diagnostics.CodeAnalysis;
-using System.Runtime.CompilerServices;
-
-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 void* _allocatorFirstArg;
-
- // The managed calli to the parameterless ctor, taking "this" (as object) as its first argument.
- private readonly delegate* _pfnCtor;
- private readonly bool _ctorIsPublic;
-
-#if DEBUG
- private readonly RuntimeType _originalRuntimeType;
-#endif
-
- internal ActivatorCache(RuntimeType rt)
- {
- Debug.Assert(rt != null);
-
-#if DEBUG
- _originalRuntimeType = 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();
-
- try
- {
- 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.
-
- 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);
- }
-
- 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 == null)
- {
- static void CtorNoopStub(object? uninitializedObject) { }
- _pfnCtor = &CtorNoopStub; // we use null singleton pattern if no ctor call is necessary
-
- 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.
- }
-
- internal bool CtorIsPublic => _ctorIsPublic;
-
- [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
- 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;
- }
-
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- internal void CallConstructor(object? uninitializedObject) => _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 6e2c218e31bfa0..3b6275ffa5dd95 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
@@ -3858,7 +3858,7 @@ public override Type MakeArrayType(int rank)
#region Legacy Internal
- private void CreateInstanceCheckThis()
+ internal void CreateInstanceCheckThis()
{
if (ContainsGenericParameters)
throw new ArgumentException(SR.Format(SR.Acc_CreateGenericEx, this));
@@ -3975,13 +3975,13 @@ private void CreateInstanceCheckThis()
// n.b. In coreclr we ignore 'skipCheckThis' (assumed to be false)
// and 'fillCache' (assumed to be true).
- if (GenericCache is not ActivatorCache cache)
+ if (GenericCache is not ActivationFactory factory)
{
- cache = new ActivatorCache(this);
- GenericCache = cache;
+ factory = new ActivationFactory(this);
+ GenericCache = factory;
}
- if (!cache.CtorIsPublic && publicOnly)
+ if (!factory.CtorIsPublic && publicOnly)
{
throw new MissingMethodException(SR.Format(SR.Arg_NoDefCTor, this));
}
@@ -3990,10 +3990,11 @@ private void CreateInstanceCheckThis()
// bubble up to the caller; the ctor invocation is within the try block so
// that it can be wrapped in TIE if needed.
- object? obj = cache.CreateUninitializedObject(this);
+ factory.DebugValidateExpectedType(this);
+ object? obj = factory.GetUninitializedObject();
try
{
- cache.CallConstructor(obj);
+ factory.CallConstructor(obj);
}
catch (Exception e) when (wrapExceptions)
{
diff --git a/src/coreclr/src/vm/reflectioninvocation.cpp b/src/coreclr/src/vm/reflectioninvocation.cpp
index d1f48ed93e6ada..d93335c1350ea3 100644
--- a/src/coreclr/src/vm/reflectioninvocation.cpp
+++ b/src/coreclr/src/vm/reflectioninvocation.cpp
@@ -2038,12 +2038,15 @@ void RuntimeTypeHandle::ValidateTypeAbleToBeInstantiated(
/*
* Given a RuntimeType, queries info on how to instantiate the object.
* pRuntimeType - [required] the RuntimeType object
+ * fAvoidBoxing - [required] true if ppfnCtor should have the mgd sig
+ * (ref T) -> void for value types, otherwise object -> void.
* 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
+ * mgd sig object -> void, or null if no ctor is needed for this type.
+ * for value types, can be mgd sig (ref T) -> void.
* pfCtorIsPublic - [required, null-init] whether the parameterless ctor is public
* ==========
* This method will not run the type's static cctor.
@@ -2051,6 +2054,7 @@ void RuntimeTypeHandle::ValidateTypeAbleToBeInstantiated(
*/
void QCALLTYPE RuntimeTypeHandle::GetActivationInfo(
QCall::ObjectHandleOnStack pRuntimeType,
+ BOOL fAvoidBoxing,
PCODE* ppfnAllocator,
void** pvAllocatorFirstArg,
PCODE* ppfnCtor,
@@ -2140,9 +2144,9 @@ void QCALLTYPE RuntimeTypeHandle::GetActivationInfo(
if (pMT->HasDefaultConstructor())
{
- // managed sig: object -> void
+ // managed sig: object -> void (or ref T -> void)
// for ctors on value types, lookup boxed entry point stub
- MethodDesc* pMD = pMT->GetDefaultConstructor(pMT->IsValueType() /* forceBoxedEntryPoint */);
+ MethodDesc* pMD = pMT->GetDefaultConstructor(pMT->IsValueType() && !fAvoidBoxing /* forceBoxedEntryPoint */);
_ASSERTE(pMD != NULL);
PCODE pCode = pMD->GetMultiCallableAddrOfCode();
@@ -2219,7 +2223,7 @@ FCIMPLEND
//*************************************************************************************************
//*************************************************************************************************
//*************************************************************************************************
-void QCALLTYPE ReflectionSerialization::GetUninitializedObject(QCall::TypeHandle pType, QCall::ObjectHandleOnStack retObject)
+void QCALLTYPE ReflectionSerialization::GetUninitializedObject(QCall::TypeHandle pType, BOOL fSkipChecks, QCall::ObjectHandleOnStack retObject)
{
QCALL_CONTRACT;
@@ -2227,19 +2231,25 @@ void QCALLTYPE ReflectionSerialization::GetUninitializedObject(QCall::TypeHandle
TypeHandle type = pType.AsTypeHandle();
- RuntimeTypeHandle::ValidateTypeAbleToBeInstantiated(type, true /* fForGetUninitializedInstance */);
+ if (!fSkipChecks)
+ {
+ RuntimeTypeHandle::ValidateTypeAbleToBeInstantiated(type, true /* fForGetUninitializedInstance */);
+ }
MethodTable* pMT = type.AsMethodTable();
+ if (!fSkipChecks)
+ {
#ifdef FEATURE_COMINTEROP
- // Also do not allow allocation of uninitialized RCWs (COM objects).
- if (pMT->IsComObjectType())
- COMPlusThrow(kNotSupportedException, W("NotSupported_ManagedActivation"));
+ // 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 (pMT->IsNullable())
- pMT = pMT->GetInstantiation()[0].GetMethodTable();
+ // If it is a nullable, return the underlying type instead.
+ if (pMT->IsNullable())
+ pMT = pMT->GetInstantiation()[0].GetMethodTable();
+ }
{
GCX_COOP();
diff --git a/src/coreclr/src/vm/reflectioninvocation.h b/src/coreclr/src/vm/reflectioninvocation.h
index c00c17c883ac84..0c09b1edea7b5c 100644
--- a/src/coreclr/src/vm/reflectioninvocation.h
+++ b/src/coreclr/src/vm/reflectioninvocation.h
@@ -81,7 +81,7 @@ class ReflectionInvocation {
class ReflectionSerialization {
public:
static
- void QCALLTYPE GetUninitializedObject(QCall::TypeHandle pType, QCall::ObjectHandleOnStack retObject);
+ void QCALLTYPE GetUninitializedObject(QCall::TypeHandle pType, BOOL fSkipChecks, QCall::ObjectHandleOnStack retObject);
};
class ReflectionEnum {
diff --git a/src/coreclr/src/vm/runtimehandles.h b/src/coreclr/src/vm/runtimehandles.h
index 7675d1b3bcac9f..3ed8da220a6505 100644
--- a/src/coreclr/src/vm/runtimehandles.h
+++ b/src/coreclr/src/vm/runtimehandles.h
@@ -126,6 +126,7 @@ class RuntimeTypeHandle {
static
void QCALLTYPE GetActivationInfo(
QCall::ObjectHandleOnStack pRuntimeType,
+ BOOL fAvoidBoxing,
PCODE* ppfnAllocator,
void** pvAllocatorFirstArg,
PCODE* ppfnCtor,
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 96ac22ef364295..4603f4367f5464 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.Reflection;
using System.Globalization;
+using System.Reflection;
using System.Runtime.Loader;
using System.Runtime.Remoting;
using System.Threading;
@@ -152,5 +152,50 @@ public static partial class Activator
}
private static T CreateDefaultInstance() where T : struct => default;
+
+ public static Func CreateFactory([DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.NonPublicConstructors)] Type type, bool nonPublic)
+ {
+ if (type is null)
+ throw new ArgumentNullException(nameof(type));
+
+ if (type.UnderlyingSystemType is not RuntimeType rt)
+ throw new ArgumentException(SR.Arg_MustBeType, nameof(type));
+
+ // First create the factory instance.
+
+ ActivationFactory factory = new ActivationFactory(rt);
+ if (!nonPublic && !factory.CtorIsPublic)
+ {
+ throw new MissingMethodException(SR.Format(SR.Arg_NoDefCTor, rt));
+ }
+
+ // Then create a delegate to factory.CreateInstance, closed over the "this" parameter.
+
+ return (Func)factory.CreateDelegate((RuntimeType)typeof(Func));
+ }
+
+ [DynamicDependency("#ctor", typeof(ActivationFactory<>))]
+ public static Func CreateFactory<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)]T>()
+ {
+ if (typeof(T).IsValueType)
+ {
+ IActivationFactory factory = (IActivationFactory)RuntimeTypeHandle.CreateInstanceForAnotherGenericParameter((RuntimeType)typeof(ActivationFactory<>), (RuntimeType)typeof(T));
+ return (Func)factory.GetCreateInstanceDelegate();
+ }
+ else
+ {
+ // First create the factory instance.
+
+ ActivationFactory factory = new ActivationFactory((RuntimeType)typeof(T));
+ if (!factory.CtorIsPublic)
+ {
+ throw new MissingMethodException(SR.Format(SR.Arg_NoDefCTor, typeof(T)));
+ }
+
+ // Then create a delegate to factory.CreateInstance, closed over the "this" parameter.
+
+ return (Func)factory.CreateDelegate((RuntimeType)typeof(Func));
+ }
+ }
}
}
diff --git a/src/libraries/System.Private.CoreLib/src/System/Activator.cs b/src/libraries/System.Private.CoreLib/src/System/Activator.cs
index 8bdd99692ca3f4..8a46fe7fd109ce 100644
--- a/src/libraries/System.Private.CoreLib/src/System/Activator.cs
+++ b/src/libraries/System.Private.CoreLib/src/System/Activator.cs
@@ -40,6 +40,13 @@ public static partial class Activator
public static object? CreateInstance([DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)] Type type) =>
CreateInstance(type, nonPublic: false);
+ [DebuggerHidden]
+ [DebuggerStepThrough]
+ [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2067:UnrecognizedReflectionPattern",
+ Justification = "CreateFactory(Type, bool) is annotated as requiring private ctors, but this entry point only cares about public ctors.")]
+ public static Func CreateFactory([DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)] Type type) =>
+ CreateFactory(type, nonPublic: false);
+
[RequiresUnreferencedCode("Type and its constructor could be removed")]
public static ObjectHandle? CreateInstanceFrom(string assemblyFile, string typeName) =>
CreateInstanceFrom(assemblyFile, typeName, false, ConstructorDefault, null, null, null, null);
diff --git a/src/libraries/System.Runtime/ref/System.Runtime.cs b/src/libraries/System.Runtime/ref/System.Runtime.cs
index e7b8eac6abc532..5518561d45518b 100644
--- a/src/libraries/System.Runtime/ref/System.Runtime.cs
+++ b/src/libraries/System.Runtime/ref/System.Runtime.cs
@@ -66,6 +66,9 @@ 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() { throw null; }
+ public static System.Func CreateFactory([System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembersAttribute(System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)] System.Type type) { throw null; }
+ public static System.Func CreateFactory([System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembersAttribute(System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.NonPublicConstructors | System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors)] System.Type type, bool nonPublic) { 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; }
diff --git a/src/libraries/System.Runtime/tests/System/ActivatorTests.Generic.cs b/src/libraries/System.Runtime/tests/System/ActivatorTests.Generic.cs
index 2ba5c521ae87e4..8ffcddde15d305 100644
--- a/src/libraries/System.Runtime/tests/System/ActivatorTests.Generic.cs
+++ b/src/libraries/System.Runtime/tests/System/ActivatorTests.Generic.cs
@@ -8,6 +8,22 @@ namespace System.Tests
{
public partial class ActivatorTests
{
+ [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.SupportsComInterop))]
+ public void CreateInstanceT_ComObject_Success()
+ {
+ WbemContext instance = Activator.CreateInstance();
+ Assert.NotNull(instance);
+ }
+
+ [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.SupportsComInterop))]
+ public void CreateFactoryT_ComObject_Success()
+ {
+ Func factory = Activator.CreateFactory();
+ Assert.NotNull(factory);
+ WbemContext instance = factory();
+ Assert.NotNull(instance);
+ }
+
[Fact]
public void CreateInstanceT_Array_ThrowsMissingMethodException() =>
Assert.Throws(() => Activator.CreateInstance());
@@ -60,6 +76,82 @@ public void CreateInstanceT_StructWithoutDefaultConstructor_ThrowsMissingMethodE
public void CreateInstanceT_StructWithDefaultConstructorThatThrows_ThrowsTargetInvocationException() =>
Assert.Throws(() => Activator.CreateInstance());
+ [Fact]
+ public void CreateFactoryT_ReferenceTypeWithPublicCtor_Success()
+ {
+ Func factory = Activator.CreateFactory();
+ PublicType instance = factory();
+ Assert.NotNull(instance);
+ Assert.True(instance.CtorWasCalled);
+ }
+
+ [Fact]
+ public void CreateFactoryT_ReferenceTypeWithPrivateCtor_ThrowsMissingMethodException()
+ => Assert.Throws(() => Activator.CreateFactory());
+
+ [Fact]
+ public void CreateFactoryT_ReferenceTypeWithPrivateCtorThatThrows_DoesNotWrapInTargetInvocationException()
+ {
+ Func factory = Activator.CreateFactory();
+ Assert.Throws(() => factory()); // no TIE
+ }
+
+ [Fact]
+ public void CreateFactoryT_OfNullableT_ReturnsNull()
+ {
+ Func factory = Activator.CreateFactory();
+ Assert.NotNull(factory);
+ int? instance = factory();
+ Assert.False(instance.HasValue);
+ }
+
+ [Fact]
+ public void CreateFactoryT_OfValueTypeWithoutDefaultCtor_ReturnsDefaultT()
+ {
+ Func factory = Activator.CreateFactory();
+ Assert.NotNull(factory);
+ ValueTypeWithParameterfulConstructor instance = factory();
+ Assert.False(instance.ParameterfulCtorWasCalled);
+ }
+
+ [Fact]
+ public void CreateFactoryT_OfValueTypeWithPublicDefaultCtor_CallsDefaultCtor()
+ {
+ Func factory = Activator.CreateFactory();
+ Assert.NotNull(factory);
+ StructWithPublicDefaultConstructor instance = factory();
+ Assert.True(instance.ConstructorInvoked);
+ Assert.True(IsAddressOnLocalStack(instance.AddressPassedToConstructor)); // using Func, value type ctor is called using ref to stack local, no boxing
+ }
+
+ [Fact]
+ public void CreateFactoryT_OfValueTypeWithPrivateDefaultCtor_ThrowsMissingMethodException()
+ => Assert.Throws(() => Activator.CreateFactory());
+
+ [Fact]
+ public void CreateFactoryT_OfValueTypeWithPublicDefaultCtorThatThrows_DoesNotWrapInTargetInvocationException()
+ {
+ Func factory = Activator.CreateFactory();
+ Assert.NotNull(factory);
+ Assert.Throws(() => factory()); // shouldn't wrap in TIE
+ }
+
+ [Fact]
+ public void CreateFactoryT_Array_ThrowsMissingMethodException() =>
+ Assert.Throws(() => Activator.CreateFactory());
+
+ [Fact]
+ public void CreateFactoryT_Interface_ThrowsMissingMethodException() =>
+ Assert.Throws(() => Activator.CreateFactory());
+
+ [Fact]
+ public void CreateFactoryT_AbstractClass_ThrowsMissingMethodException() =>
+ Assert.Throws(() => Activator.CreateFactory());
+
+ [Fact]
+ public void CreateFactoryT_String_ThrowsMissingMethodException() =>
+ Assert.Throws(() => Activator.CreateFactory());
+
private interface IInterface
{
}
diff --git a/src/libraries/System.Runtime/tests/System/ActivatorTests.cs b/src/libraries/System.Runtime/tests/System/ActivatorTests.cs
index 05e07447a96164..3a2a914dd79fb9 100644
--- a/src/libraries/System.Runtime/tests/System/ActivatorTests.cs
+++ b/src/libraries/System.Runtime/tests/System/ActivatorTests.cs
@@ -8,6 +8,7 @@
using System.IO;
using System.Reflection;
using System.Reflection.Emit;
+using System.Runtime.InteropServices;
using System.Runtime.Remoting;
using Microsoft.DotNet.RemoteExecutor;
using Xunit;
@@ -16,6 +17,9 @@ namespace System.Tests
{
public partial class ActivatorTests
{
+ // random GUID, shouldn't map to any known COM registration
+ private static readonly Guid UnknownComClsid = new Guid("db8d1a68-84f2-4c37-9581-33286f3c1b49");
+
[Fact]
public static void CreateInstance()
{
@@ -91,6 +95,44 @@ public void CreateInstance_NonPublicTypeWithPrivateDefaultConstructor_Success()
Assert.Equal(-1, c2.Property);
}
+ [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.SupportsComInterop))]
+ public void CreateInstance_ComObject_Success()
+ {
+ object instance = Activator.CreateInstance(typeof(WbemContext));
+ Assert.NotNull(instance);
+ Assert.True(instance.GetType().IsEquivalentTo(typeof(WbemContext)));
+ }
+
+ [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.SupportsComInterop))]
+ public void CreateInstance_ComObjectWithUnknownClsid_ThrowsCOMException()
+ {
+ // failure occurs at construction time, no TIE
+ Type unknownComType = Type.GetTypeFromCLSID(UnknownComClsid);
+ var ex = Assert.Throws(() => Activator.CreateInstance(unknownComType));
+ Assert.Equal(unchecked((int)0x80040154), ex.HResult); // REGDB_E_CLASSNOTREG, to distinguish from other unexpected exceptions
+ }
+
+ [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.SupportsComInterop))]
+ public void CreateFactory_ComObject_Success()
+ {
+ Func factory = Activator.CreateFactory(typeof(WbemContext));
+ Assert.NotNull(factory);
+ object instance = factory();
+ Assert.NotNull(instance);
+ Assert.True(instance.GetType().IsEquivalentTo(typeof(WbemContext)));
+ }
+
+ [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.SupportsComInterop))]
+ public void CreateFactory_ComObjectWithUnknownClsid_ThrowsCOMException()
+ {
+ // obtaining the factory should succeed; invoking the factory should fail
+ Type unknownComType = Type.GetTypeFromCLSID(UnknownComClsid);
+ Func factory = Activator.CreateFactory(unknownComType);
+ Assert.NotNull(factory);
+ var ex = Assert.Throws(() => factory());
+ Assert.Equal(unchecked((int)0x80040154), ex.HResult); // REGDB_E_CLASSNOTREG, to distinguish from other unexpected exceptions
+ }
+
[Fact]
public void CreateInstance_PublicOnlyTypeWithPrivateDefaultConstructor_ThrowsMissingMethodException()
{
@@ -277,6 +319,156 @@ public static void TestActivatorOnNonActivatableFinalizableTypes()
Assert.False(TypeWithPrivateDefaultCtorAndFinalizer.WasCreated);
}
+ public static IEnumerable CreateInstance_NegativeTestCases()
+ {
+ // TODO: Test actual function pointer types when typeof(delegate*<...>) support is available
+
+ yield return new[] { typeof(string), typeof(MissingMethodException) }; // variable-length type
+ yield return new[] { typeof(int[]), typeof(MissingMethodException) }; // variable-length type
+ yield return new[] { typeof(int[,]), typeof(MissingMethodException) }; // variable-length type
+ yield return new[] { Array.CreateInstance(typeof(int), new[] { 1 }, new[] { 1 }).GetType(), typeof(MissingMethodException) }; // variable-length type (non-szarray)
+ yield return new[] { typeof(Array), typeof(MissingMethodException) }; // abstract type
+ yield return new[] { typeof(Enum), typeof(MissingMethodException) }; // abstract type
+
+ yield return new[] { typeof(Stream), typeof(MissingMethodException) }; // abstract type
+ yield return new[] { typeof(Buffer), typeof(MissingMethodException) }; // static type (runtime sees it as abstract)
+ yield return new[] { typeof(IDisposable), typeof(MissingMethodException) }; // interface type
+
+ yield return new[] { typeof(List<>), typeof(ArgumentException) }; // open generic type
+ yield return new[] { typeof(List<>).GetGenericArguments()[0], PlatformDetection.IsMonoRuntime ? typeof(MemberAccessException) : typeof(ArgumentException) }; // 'T' placeholder typedesc
+
+ yield return new[] { typeof(Delegate), typeof(MissingMethodException) }; // abstract type
+
+ yield return new[] { typeof(void), typeof(NotSupportedException) }; // explicit block in place
+ yield return new[] { typeof(int).MakePointerType(), typeof(MissingMethodException) }; // pointer typedesc
+ yield return new[] { typeof(int).MakeByRefType(), typeof(MissingMethodException) }; // byref typedesc
+
+ yield return new[] { typeof(ReadOnlySpan), typeof(NotSupportedException) }; // byref type
+ yield return new[] { typeof(ArgIterator), typeof(NotSupportedException) }; // byref type
+
+ yield return new[] { typeof(TypeWithoutDefaultCtor), typeof(MissingMethodException) }; // reference type with no parameterless ctor
+ yield return new[] { typeof(TypeWithVarargsCtor), typeof(MissingMethodException) }; // parameterless ctor exists but has incorrect calling convention
+
+ Type canonType = typeof(object).Assembly.GetType("System.__Canon", throwOnError: false);
+ if (canonType != null)
+ {
+ yield return new[] { typeof(List<>).MakeGenericType(canonType), typeof(NotSupportedException) }; // shared by generic instantiations
+ }
+
+ Type comObjType = typeof(object).Assembly.GetType("System.__ComObject", throwOnError: false);
+ if (comObjType != null)
+ {
+ yield return new[] { comObjType, typeof(InvalidComObjectException) }; // COM type with no associated CLSID
+ }
+ }
+
+ [Theory]
+ [MemberData(nameof(CreateInstance_NegativeTestCases))]
+ public static void CreateInstance_InvalidType_ThrowsException(Type typeToInstantiate, Type expectedExceptionType)
+ {
+ // We don't expect a TIE wrapper since we won't get as far as calling the ctor
+ Assert.Throws(expectedExceptionType, () => Activator.CreateInstance(typeToInstantiate));
+ Assert.Throws(expectedExceptionType, () => Activator.CreateInstance(typeToInstantiate, nonPublic: true));
+ }
+
+ [Theory]
+ [MemberData(nameof(CreateInstance_NegativeTestCases))]
+ public static void CreateFactory_InvalidType_ThrowsException(Type typeToInstantiate, Type expectedExceptionType)
+ {
+ // We don't expect a TIE wrapper since we won't get as far as calling the ctor
+ Assert.Throws(expectedExceptionType, () => Activator.CreateFactory(typeToInstantiate));
+ Assert.Throws(expectedExceptionType, () => Activator.CreateFactory(typeToInstantiate, nonPublic: true));
+ }
+
+ [Fact]
+ public void CreateFactory_ReferenceTypeWithPublicCtor_Success()
+ {
+ Func factory = Activator.CreateFactory(typeof(PublicType));
+ Assert.NotNull(factory);
+ object instance = factory();
+ var castInstance = Assert.IsType(instance);
+ Assert.True(castInstance.CtorWasCalled);
+ }
+
+ [Fact]
+ public void CreateFactory_ReferenceTypeWithPrivateCtor_ThrowsMissingMethodException()
+ {
+ Assert.Throws(() => Activator.CreateFactory(typeof(PrivateTypeWithDefaultCtor)));
+ Assert.Throws(() => Activator.CreateFactory(typeof(PrivateTypeWithDefaultCtor), nonPublic: false));
+ }
+
+ [Fact]
+ public void CreateFactory_ReferenceTypeWithPrivateCtor_AllowNonPublicCtors_Success()
+ {
+ Func factory = Activator.CreateFactory(typeof(PrivateTypeWithDefaultCtor), nonPublic: true);
+ Assert.NotNull(factory);
+ object instance = factory();
+ var castInstance = Assert.IsType(instance);
+ Assert.True(castInstance.CtorWasCalled);
+ }
+
+ [Fact]
+ public void CreateFactory_ReferenceTypeWithPrivateCtorThatThrows_DoesNotWrapInTargetInvocationException()
+ {
+ Func factory = Activator.CreateFactory(typeof(TypeWithDefaultCtorThatThrows));
+ Assert.NotNull(factory);
+ Assert.Throws(() => factory()); // no TIE
+ }
+
+ [Fact]
+ public void CreateFactory_OfNullableT_ReturnsNull()
+ {
+ Func factory = Activator.CreateFactory(typeof(int?));
+ Assert.NotNull(factory);
+ Assert.Null(factory());
+ }
+
+ [Fact]
+ public void CreateFactory_OfValueTypeWithoutDefaultCtor_ReturnsBoxedDefaultT()
+ {
+ Func factory = Activator.CreateFactory(typeof(ValueTypeWithParameterfulConstructor));
+ Assert.NotNull(factory);
+ object instance = factory();
+ var castInstance = Assert.IsType(instance);
+ Assert.False(castInstance.ParameterfulCtorWasCalled);
+ }
+
+ [Fact]
+ public void CreateFactory_OfValueTypeWithPublicDefaultCtor_CallsDefaultCtor()
+ {
+ Func factory = Activator.CreateFactory(typeof(StructWithPublicDefaultConstructor));
+ Assert.NotNull(factory);
+ object instance = factory();
+ var castInstance = Assert.IsType(instance);
+ Assert.True(castInstance.ConstructorInvoked);
+ Assert.False(IsAddressOnLocalStack(castInstance.AddressPassedToConstructor)); // using Func, value type ctor is called using unboxing stub
+ }
+
+ [Fact]
+ public void CreateFactory_OfValueTypeWithPrivateDefaultCtor_ThrowsMissingMethodException()
+ {
+ Assert.Throws(() => Activator.CreateFactory(typeof(StructWithPrivateDefaultConstructor)));
+ Assert.Throws(() => Activator.CreateFactory(typeof(StructWithPrivateDefaultConstructor), nonPublic: false));
+ }
+
+ [Fact]
+ public void CreateFactory_OfValueTypeWithPrivateDefaultCtor_AllowNonPublicCtors_Success()
+ {
+ Func factory = Activator.CreateFactory(typeof(StructWithPrivateDefaultConstructor), nonPublic: true);
+ Assert.NotNull(factory);
+ object instance = factory();
+ var castInstance = Assert.IsType(instance);
+ Assert.True(castInstance.ConstructorInvoked);
+ }
+
+ [Fact]
+ public void CreateFactory_OfValueTypeWithPublicDefaultCtorThatThrows_DoesNotWrapInTargetInvocationException()
+ {
+ Func factory = Activator.CreateFactory(typeof(StructWithDefaultConstructorThatThrows));
+ Assert.NotNull(factory);
+ Assert.Throws(() => factory()); // shouldn't wrap in TIE
+ }
+
private class PrivateType
{
public PrivateType() { }
@@ -284,7 +476,8 @@ public PrivateType() { }
class PrivateTypeWithDefaultCtor
{
- private PrivateTypeWithDefaultCtor() { }
+ public readonly bool CtorWasCalled;
+ private PrivateTypeWithDefaultCtor() { CtorWasCalled = true; }
}
class PrivateTypeWithoutDefaultCtor
@@ -363,6 +556,12 @@ public struct ValueTypeWithDefaultConstructor
{
}
+ public struct ValueTypeWithParameterfulConstructor
+ {
+ public readonly bool ParameterfulCtorWasCalled;
+ public ValueTypeWithParameterfulConstructor(int i) { ParameterfulCtorWasCalled = true; }
+ }
+
public class TypeWithPrivateDefaultConstructor
{
public int Property { get; }
@@ -401,6 +600,11 @@ public class TypeWithoutDefaultCtor
private TypeWithoutDefaultCtor(int x) { }
}
+ public class TypeWithVarargsCtor
+ {
+ public TypeWithVarargsCtor(__arglist) { }
+ }
+
public class TypeWithDefaultCtorThatThrows
{
public TypeWithDefaultCtorThatThrows() { throw new Exception(); }
@@ -477,6 +681,14 @@ class Flag
public static bool Equal(int i) { return cnt == i; }
}
+ // 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
+ {
+ }
+
[ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsInvokingStaticConstructorsSupported))]
public static void TestingBindingFlags()
{
@@ -783,7 +995,8 @@ private static void CheckValidity(ObjectHandle instance, string expected)
public class PublicType
{
- public PublicType() { }
+ public readonly bool CtorWasCalled;
+ public PublicType() { CtorWasCalled = true; }
}
[ConditionalFact(typeof(RemoteExecutor), nameof(RemoteExecutor.IsSupported))]
@@ -807,5 +1020,13 @@ public void CreateInstance_TypeBuilder_ThrowsNotSupportedException()
Assert.Throws("type", () => Activator.CreateInstance(typeBuilder));
Assert.Throws(() => Activator.CreateInstance(typeBuilder, new object[0]));
}
+
+ // Returns true iff the target address is on the local thread's stack.
+ // Heuristic-based: probably true if target address is within 1MB of a local's address.
+ private unsafe static bool IsAddressOnLocalStack(IntPtr address)
+ {
+ byte dummy = 0;
+ return Math.Abs((byte*)address - &dummy) < 1024 * 1024;
+ }
}
}
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 33327d00fed473..6ed2a2037b9eaf 100644
--- a/src/libraries/System.Runtime/tests/System/Runtime/CompilerServices/RuntimeHelpersTests.cs
+++ b/src/libraries/System.Runtime/tests/System/Runtime/CompilerServices/RuntimeHelpersTests.cs
@@ -6,6 +6,7 @@
using System.IO;
using System.Reflection;
using System.Runtime.InteropServices;
+using System.Tests;
using Xunit;
namespace System.Runtime.CompilerServices.Tests
@@ -300,6 +301,14 @@ public static void GetUninitializedObject_DoesNotRunConstructor()
Assert.Equal(0, ((ObjectWithDefaultCtor)RuntimeHelpers.GetUninitializedObject(typeof(ObjectWithDefaultCtor))).Value);
}
+ [Fact]
+ public static void GetUninitializedObject_ValueTypeWithExplicitDefaultCtor_DoesNotRunConstructor()
+ {
+ object o = RuntimeHelpers.GetUninitializedObject(typeof(StructWithPublicDefaultConstructor));
+ var castObj = Assert.IsType(o);
+ Assert.False(castObj.ConstructorInvoked);
+ }
+
[Fact]
public static void GetUninitializedObject_Struct()
{
diff --git a/src/libraries/System.Runtime/tests/TestStructs/System.TestStructs.il b/src/libraries/System.Runtime/tests/TestStructs/System.TestStructs.il
index dabc5ac4a1b452..251ff74395d8df 100644
--- a/src/libraries/System.Runtime/tests/TestStructs/System.TestStructs.il
+++ b/src/libraries/System.Runtime/tests/TestStructs/System.TestStructs.il
@@ -25,10 +25,14 @@
.class public sequential sealed StructWithPublicDefaultConstructor
extends [System.Runtime]System.ValueType
{
+ .field public initonly native int AddressPassedToConstructor
.field public initonly bool ConstructorInvoked
.method public hidebysig specialname rtspecialname instance void .ctor () cil managed
{
+ ldarg.0
+ ldarg.0
+ stfld native int System.Tests.StructWithPublicDefaultConstructor::AddressPassedToConstructor
ldarg.0
ldc.i4.1
stfld bool System.Tests.StructWithPublicDefaultConstructor::ConstructorInvoked
@@ -39,10 +43,13 @@
.class public sequential sealed StructWithPrivateDefaultConstructor
extends [System.Runtime]System.ValueType
{
- .size 1
+ .field public initonly bool ConstructorInvoked
.method private hidebysig specialname rtspecialname instance void .ctor () cil managed
{
+ ldarg.0
+ ldc.i4.1
+ stfld bool System.Tests.StructWithPrivateDefaultConstructor::ConstructorInvoked
ret
}
}
diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/ReflectionEmitMemberAccessor.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/ReflectionEmitMemberAccessor.cs
index 6dc8490a398f0d..833e457db4c121 100644
--- a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/ReflectionEmitMemberAccessor.cs
+++ b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/ReflectionEmitMemberAccessor.cs
@@ -12,6 +12,10 @@ namespace System.Text.Json.Serialization
{
internal sealed class ReflectionEmitMemberAccessor : MemberAccessor
{
+#if NET6_0 // should really be NET6_0_OR_GREATER
+ [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2067:UnrecognizedReflectionPattern",
+ Justification = "Temporary until IL linker understands new patterns.")]
+#endif
public override JsonClassInfo.ConstructorDelegate? CreateConstructor(Type type)
{
Debug.Assert(type != null);
@@ -27,6 +31,9 @@ internal sealed class ReflectionEmitMemberAccessor : MemberAccessor
return null;
}
+#if NET6_0 // should really be NET6_0_OR_GREATER
+ return new JsonClassInfo.ConstructorDelegate(Activator.CreateFactory(type));
+#else
var dynamicMethod = new DynamicMethod(
ConstructorInfo.ConstructorName,
JsonClassInfo.ObjectType,
@@ -53,6 +60,7 @@ internal sealed class ReflectionEmitMemberAccessor : MemberAccessor
generator.Emit(OpCodes.Ret);
return (JsonClassInfo.ConstructorDelegate)dynamicMethod.CreateDelegate(typeof(JsonClassInfo.ConstructorDelegate));
+#endif
}
public override JsonClassInfo.ParameterizedConstructorDelegate? CreateParameterizedConstructor(ConstructorInfo constructor) =>
diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/ReflectionMemberAccessor.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/ReflectionMemberAccessor.cs
index f42d1374c75b7c..00a1ca71055cdf 100644
--- a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/ReflectionMemberAccessor.cs
+++ b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/ReflectionMemberAccessor.cs
@@ -24,7 +24,11 @@ internal sealed class ReflectionMemberAccessor : MemberAccessor
return null;
}
+#if NET6_0 // should really be NET6_0_OR_GREATER
+ return new JsonClassInfo.ConstructorDelegate(Activator.CreateFactory(type));
+#else
return () => Activator.CreateInstance(type, nonPublic: false);
+#endif
}
public override JsonClassInfo.ParameterizedConstructorDelegate? CreateParameterizedConstructor(ConstructorInfo constructor)