Skip to content
Merged
14 changes: 9 additions & 5 deletions src/coreclr/System.Private.CoreLib/src/System/RuntimeHandles.cs
Original file line number Diff line number Diff line change
Expand Up @@ -270,24 +270,27 @@ internal static void GetActivationInfo(
RuntimeType rt,
out delegate*<void*, object> pfnAllocator,
out void* vAllocatorFirstArg,
out delegate*<object, void> pfnCtor,
out delegate*<object, void> pfnRefCtor,
out delegate*<ref byte, void> pfnValueCtor,
out bool ctorIsPublic)
{
Debug.Assert(rt != null);

delegate*<void*, object> pfnAllocatorTemp = default;
void* vAllocatorFirstArgTemp = default;
delegate*<object, void> pfnCtorTemp = default;
delegate*<object, void> pfnRefCtorTemp = default;
delegate*<ref byte, void> pfnValueCtorTemp = default;
Interop.BOOL fCtorIsPublicTemp = default;

GetActivationInfo(
ObjectHandleOnStack.Create(ref rt),
&pfnAllocatorTemp, &vAllocatorFirstArgTemp,
&pfnCtorTemp, &fCtorIsPublicTemp);
&pfnRefCtorTemp, &pfnValueCtorTemp, &fCtorIsPublicTemp);

pfnAllocator = pfnAllocatorTemp;
vAllocatorFirstArg = vAllocatorFirstArgTemp;
pfnCtor = pfnCtorTemp;
pfnRefCtor = pfnRefCtorTemp;
pfnValueCtor = pfnValueCtorTemp;
ctorIsPublic = fCtorIsPublicTemp != Interop.BOOL.FALSE;
}

Expand All @@ -296,7 +299,8 @@ private static partial void GetActivationInfo(
ObjectHandleOnStack pRuntimeType,
delegate*<void*, object>* ppfnAllocator,
void** pvAllocatorFirstArg,
delegate*<object, void>* ppfnCtor,
delegate*<object, void>* ppfnRefCtor,
delegate*<ref byte, void>* ppfnValueCtor,
Interop.BOOL* pfCtorIsPublic);

#if FEATURE_COMINTEROP
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,8 @@ private sealed unsafe class ActivatorCache
private readonly void* _allocatorFirstArg;

// The managed calli to the parameterless ctor, taking "this" (as object) as its first argument.
private readonly delegate*<object?, void> _pfnCtor;
private readonly delegate*<object?, void> _pfnRefCtor;
private readonly delegate*<ref byte, void> _pfnValueCtor;
private readonly bool _ctorIsPublic;

private CreateUninitializedCache? _createUninitializedCache;
Expand All @@ -48,7 +49,7 @@ internal ActivatorCache(RuntimeType rt)
{
RuntimeTypeHandle.GetActivationInfo(rt,
out _pfnAllocator!, out _allocatorFirstArg,
out _pfnCtor!, out _ctorIsPublic);
out _pfnRefCtor!, out _pfnValueCtor!, out _ctorIsPublic);
}
catch (Exception ex)
{
Expand Down Expand Up @@ -87,14 +88,29 @@ internal ActivatorCache(RuntimeType rt)
// would have thrown an exception if 'rt' were a normal reference type
// without a ctor.

if (_pfnCtor == null)
if (_pfnRefCtor == null)
{
static void CtorNoopStub(object? uninitializedObject) { }
_pfnCtor = &CtorNoopStub; // we use null singleton pattern if no ctor call is necessary
static void RefCtorNoopStub(object? uninitializedObject) { }
_pfnRefCtor = &RefCtorNoopStub; // we use null singleton pattern if no ctor call is necessary

Debug.Assert(_ctorIsPublic); // implicit parameterless ctor is always considered public
}

if (rt.IsValueType)
{
if (_pfnValueCtor == null)
{
static void ValueRefCtorNoopStub(ref byte uninitializedObject) { }
_pfnValueCtor = &ValueRefCtorNoopStub; // we use null singleton pattern if no ctor call is necessary

Debug.Assert(_ctorIsPublic); // implicit parameterless ctor is always considered public
}
}
else
{
Debug.Assert(_pfnValueCtor == null); // Non-value types shouldn't have a value constructor.
}

// 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
Expand All @@ -120,7 +136,16 @@ static void CtorNoopStub(object? uninitializedObject) { }
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal void CallConstructor(object? uninitializedObject) => _pfnCtor(uninitializedObject);
internal void CallRefConstructor(object? uninitializedObject) => _pfnRefCtor(uninitializedObject);

[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal void CallValueConstructor(ref byte uninitializedObject)
{
#if DEBUG
Debug.Assert(_originalRuntimeType.IsValueType);
#endif
_pfnValueCtor(ref uninitializedObject);
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal CreateUninitializedCache GetCreateUninitializedCache(RuntimeType rt)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3932,7 +3932,7 @@ internal object GetUninitializedObject()
object? obj = cache.CreateUninitializedObject(this);
try
{
cache.CallConstructor(obj);
cache.CallRefConstructor(obj);
}
catch (Exception e) when (wrapExceptions)
{
Expand All @@ -3942,7 +3942,7 @@ internal object GetUninitializedObject()
return obj;
}

// Specialized version of the above for Activator.CreateInstance<T>()
// Specialized version of CreateInstanceDefaultCtor() for Activator.CreateInstance<T>()
[DebuggerStepThrough]
[DebuggerHidden]
internal object? CreateInstanceOfT()
Expand All @@ -3961,7 +3961,7 @@ internal object GetUninitializedObject()
object? obj = cache.CreateUninitializedObject(this);
try
{
cache.CallConstructor(obj);
cache.CallRefConstructor(obj);
}
catch (Exception e)
{
Expand All @@ -3971,6 +3971,34 @@ internal object GetUninitializedObject()
return obj;
}

// Specialized version of CreateInstanceDefaultCtor() for Activator.CreateInstance<T>()
[DebuggerStepThrough]
[DebuggerHidden]
internal void CallDefaultStructConstructor(ref byte data)
{
Debug.Assert(IsValueType);

if (GenericCache is not ActivatorCache cache)
{
cache = new ActivatorCache(this);
GenericCache = cache;
}

if (!cache.CtorIsPublic)
{
throw new MissingMethodException(SR.Format(SR.Arg_NoDefCTor, this));
}

try
{
cache.CallValueConstructor(ref data);
}
catch (Exception e)
{
throw new TargetInvocationException(e);
}
}

internal void InvalidateCachedNestedType() => Cache.InvalidateCachedNestedType();

internal bool IsGenericCOMObjectImpl() => RuntimeTypeHandle.IsComObject(this, true);
Expand Down
35 changes: 26 additions & 9 deletions src/coreclr/vm/reflectioninvocation.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1694,7 +1694,8 @@ extern "C" void QCALLTYPE RuntimeTypeHandle_GetActivationInfo(
QCall::ObjectHandleOnStack pRuntimeType,
PCODE* ppfnAllocator,
void** pvAllocatorFirstArg,
PCODE* ppfnCtor,
PCODE* ppfnRefCtor,
PCODE* ppfnValueCtor,
BOOL* pfCtorIsPublic
)
{
Expand All @@ -1703,11 +1704,13 @@ extern "C" void QCALLTYPE RuntimeTypeHandle_GetActivationInfo(
QCALL_CHECK;
PRECONDITION(CheckPointer(ppfnAllocator));
PRECONDITION(CheckPointer(pvAllocatorFirstArg));
PRECONDITION(CheckPointer(ppfnCtor));
PRECONDITION(CheckPointer(ppfnRefCtor));
PRECONDITION(CheckPointer(ppfnValueCtor));
PRECONDITION(CheckPointer(pfCtorIsPublic));
PRECONDITION(*ppfnAllocator == NULL);
PRECONDITION(*pvAllocatorFirstArg == NULL);
PRECONDITION(*ppfnCtor == NULL);
PRECONDITION(*ppfnRefCtor == NULL);
PRECONDITION(*ppfnValueCtor == NULL);
PRECONDITION(*pfCtorIsPublic == FALSE);
}
CONTRACTL_END;
Expand Down Expand Up @@ -1761,7 +1764,8 @@ extern "C" void QCALLTYPE RuntimeTypeHandle_GetActivationInfo(
// 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
*ppfnRefCtor = NULL; // no ctor call needed; activation handled entirely by the allocator
*ppfnValueCtor = NULL; // no value ctor for reference type
*pfCtorIsPublic = TRUE; // no ctor call needed => assume 'public' equivalent
}
else
Expand All @@ -1771,7 +1775,8 @@ extern "C" void QCALLTYPE RuntimeTypeHandle_GetActivationInfo(
// CreateInstance returns null given Nullable<T>
*ppfnAllocator = (PCODE)NULL;
*pvAllocatorFirstArg = NULL;
*ppfnCtor = (PCODE)NULL;
*ppfnRefCtor = (PCODE)NULL;
*ppfnValueCtor = (PCODE)NULL;
*pfCtorIsPublic = TRUE; // no ctor call needed => assume 'public' equivalent
}
else
Expand All @@ -1781,22 +1786,34 @@ extern "C" void QCALLTYPE RuntimeTypeHandle_GetActivationInfo(
*ppfnAllocator = CEEJitInfo::getHelperFtnStatic(CEEInfo::getNewHelperStatic(pMT, &fHasSideEffectsUnused));
*pvAllocatorFirstArg = pMT;

BOOL isValueType = pMT->IsValueType();
if (pMT->HasDefaultConstructor())
{
// managed sig: object -> void
// for ctors on value types, lookup boxed entry point stub
MethodDesc* pMD = pMT->GetDefaultConstructor(pMT->IsValueType() /* forceBoxedEntryPoint */);
MethodDesc* pMD = pMT->GetDefaultConstructor(isValueType /* forceBoxedEntryPoint */);
_ASSERTE(pMD != NULL);

PCODE pCode = pMD->GetMultiCallableAddrOfCode();
_ASSERTE(pCode != (PCODE)NULL);

*ppfnCtor = pCode;
*ppfnRefCtor = pCode;
*pfCtorIsPublic = pMD->IsPublic();

// If we have a value type, get the non-boxing entry point too.
if (isValueType)
{
pMD = pMT->GetDefaultConstructor(FALSE /* forceBoxedEntryPoint */);
_ASSERTE(pMD != NULL);
pCode = pMD->GetMultiCallableAddrOfCode();
_ASSERTE(pCode != (PCODE)NULL);
*ppfnValueCtor = pCode;
}
}
else if (pMT->IsValueType())
else if (isValueType)
{
*ppfnCtor = (PCODE)NULL; // no ctor call needed; we're creating a boxed default(T)
*ppfnRefCtor = (PCODE)NULL; // no ctor call needed; we're creating a boxed default(T)
*ppfnValueCtor = (PCODE)NULL;
*pfCtorIsPublic = TRUE; // no ctor call needed => assume 'public' equivalent
}
else
Expand Down
1 change: 1 addition & 0 deletions src/coreclr/vm/runtimehandles.h
Original file line number Diff line number Diff line change
Expand Up @@ -179,6 +179,7 @@ extern "C" void QCALLTYPE RuntimeTypeHandle_GetActivationInfo(
PCODE* ppfnAllocator,
void** pvAllocatorFirstArg,
PCODE* ppfnCtor,
PCODE* ppfnValueCtor,
BOOL* pfCtorIsPublic);
extern "C" void QCALLTYPE RuntimeTypeHandle_MakeByRef(QCall::TypeHandle pTypeHandle, QCall::ObjectHandleOnStack retType);
extern "C" void QCALLTYPE RuntimeTypeHandle_MakePointer(QCall::TypeHandle pTypeHandle, QCall::ObjectHandleOnStack retType);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -136,8 +136,27 @@ public static partial class Activator

[Intrinsic]
public static T CreateInstance<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)] T>()
where T : allows ref struct
{
return (T)((RuntimeType)typeof(T)).CreateInstanceOfT()!;
var rtType = (RuntimeType)typeof(T);
if (!rtType.IsValueType)
{
object o = rtType.CreateInstanceOfT()!;

// Casting the above object to T is technically invalid because
// T can be ByRefLike (that is, ref struct). Roslyn blocks the
// cast this in function with a "CS0030: Cannot convert type 'object' to 'T'",
// which is correct. However, since we are doing the IsValueType
// check above, we know this code path will only be taken with
// reference types and therefore the below Unsafe.As<> is safe.
return Unsafe.As<object, T>(ref o);
}
else
{
T t = default!;
rtType.CallDefaultStructConstructor(ref Unsafe.As<T, byte>(ref t));
return t;
}
}

private static T CreateDefaultInstance<T>() where T : struct => default;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,8 @@ public static T As<T>(object? o) where T : class?
[NonVersionable]
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static ref TTo As<TFrom, TTo>(ref TFrom source)
where TFrom : allows ref struct
where TTo : allows ref struct
{
throw new PlatformNotSupportedException();

Expand Down
4 changes: 2 additions & 2 deletions src/libraries/System.Runtime/ref/System.Runtime.cs
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,7 @@ public static partial class Activator
public static System.Runtime.Remoting.ObjectHandle? CreateInstanceFrom(string assemblyFile, string typeName, bool ignoreCase, System.Reflection.BindingFlags bindingAttr, System.Reflection.Binder? binder, object?[]? args, System.Globalization.CultureInfo? culture, object?[]? activationAttributes) { throw null; }
[System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("Type and its constructor could be removed")]
public static System.Runtime.Remoting.ObjectHandle? CreateInstanceFrom(string assemblyFile, string typeName, object?[]? activationAttributes) { throw null; }
public static T CreateInstance<[System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembersAttribute(System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)]T>() { throw null; }
public static T CreateInstance<[System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembersAttribute(System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)]T>() where T : allows ref struct { throw null; }
}
public partial class AggregateException : System.Exception
{
Expand Down Expand Up @@ -13357,7 +13357,7 @@ public static partial class Unsafe
public static ref T AsRef<T>(scoped ref readonly T source) { throw null; }
[return: System.Diagnostics.CodeAnalysis.NotNullIfNotNullAttribute("o")]
public static T? As<T>(object? o) where T : class? { throw null; }
public static ref TTo As<TFrom, TTo>(ref TFrom source) { throw null; }
public static ref TTo As<TFrom, TTo> (ref TFrom source) where TFrom : allows ref struct where TTo : allows ref struct { throw null; }
public static TTo BitCast<TFrom, TTo>(TFrom source) { throw null; }
public static System.IntPtr ByteOffset<T>([System.Diagnostics.CodeAnalysis.AllowNull] ref readonly T origin, [System.Diagnostics.CodeAnalysis.AllowNull] ref readonly T target) { throw null; }
[System.CLSCompliantAttribute(false)]
Expand Down
41 changes: 39 additions & 2 deletions src/mono/System.Private.CoreLib/src/System/RuntimeType.Mono.cs
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ internal enum TypeNameFormatFlags
FormatFullInst
}

internal partial class RuntimeType
internal unsafe partial class RuntimeType
{
#region Definitions

Expand Down Expand Up @@ -1607,14 +1607,51 @@ private void CreateInstanceCheckThis()
return CreateInstanceMono(!publicOnly, wrapExceptions);
}

// Specialized version of the above for Activator.CreateInstance<T>()
// Specialized version of CreateInstanceDefaultCtor() for Activator.CreateInstance<T>()
[DebuggerStepThroughAttribute]
[Diagnostics.DebuggerHidden]
internal object? CreateInstanceOfT()
{
return CreateInstanceMono(false, true);
}

// Specialized version of CreateInstanceDefaultCtor() for Activator.CreateInstance<T>()
[DebuggerStepThroughAttribute]
[Diagnostics.DebuggerHidden]
internal void CallDefaultStructConstructor(ref byte value)
{
Debug.Assert(IsValueType);

RuntimeConstructorInfo? ctor = GetDefaultConstructor();
if (ctor == null)
{
return;
}

if (!ctor.IsPublic)
{
throw new MissingMethodException(SR.Format(SR.Arg_NoDefCTor, this));
}

// Important: when using the interpreter, GetFunctionPointer is an intrinsic that
// returns a function descriptor suitable for casting to a managed function pointer.
// Other ways of obtaining a function pointer might not work.
IntPtr ptr = ctor.MethodHandle.GetFunctionPointer();
delegate*<ref byte, void> valueCtor = (delegate*<ref byte, void>)ptr;
if (valueCtor == null)
{
throw new ExecutionEngineException();
}

try
{
valueCtor(ref value);
}
catch (Exception e)
{
throw new TargetInvocationException(e);
}
}
#endregion

private TypeCache? cache;
Expand Down
Loading