Skip to content

Commit b306971

Browse files
Permit ByRefLike types with ctor to be activated. (#117940)
* Permit ByRefLike types with ctor to be activated. This was an oversight from PR #102636. Added tests.
1 parent f4bbedf commit b306971

File tree

5 files changed

+45
-17
lines changed

5 files changed

+45
-17
lines changed

src/coreclr/System.Private.CoreLib/src/System/RuntimeType.CoreCLR.cs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3974,6 +3974,11 @@ internal object GetUninitializedObject()
39743974
throw new MissingMethodException(SR.Format(SR.Arg_NoDefCTor, this));
39753975
}
39763976

3977+
if (IsByRefLike)
3978+
{
3979+
throw new NotSupportedException(SR.NotSupported_ByRefLike);
3980+
}
3981+
39773982
// Compat: allocation always takes place outside the try block so that OOMs
39783983
// bubble up to the caller; the ctor invocation is within the try block so
39793984
// that it can be wrapped in TIE if needed.
@@ -3996,6 +4001,8 @@ internal object GetUninitializedObject()
39964001
[DebuggerHidden]
39974002
internal object? CreateInstanceOfT()
39984003
{
4004+
Debug.Assert(!IsValueType);
4005+
39994006
ActivatorCache cache = GetOrCreateCacheEntry<ActivatorCache>();
40004007

40014008
if (!cache.CtorIsPublic)

src/coreclr/vm/reflectioninvocation.cpp

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1475,11 +1475,13 @@ extern "C" void QCALLTYPE ReflectionInvocation_GetGuid(MethodTable* pMT, GUID* r
14751475
* doesn't guarantee that a ctor will succeed, only that the VM is able
14761476
* to support an instance of this type on the heap.
14771477
* ==========
1478+
* The 'allowByRefLike' parameter controls whether the type should be validated as not ByRefLike.
14781479
* The 'fForGetUninitializedInstance' parameter controls the type of
14791480
* exception that is thrown if a check fails.
14801481
*/
1481-
void RuntimeTypeHandle::ValidateTypeAbleToBeInstantiated(
1482+
static void ValidateTypeAbleToBeInstantiated(
14821483
TypeHandle typeHandle,
1484+
bool allowByRefLike,
14831485
bool fGetUninitializedObject)
14841486
{
14851487
STANDARD_VM_CONTRACT;
@@ -1535,14 +1537,14 @@ void RuntimeTypeHandle::ValidateTypeAbleToBeInstantiated(
15351537
}
15361538

15371539
// Don't allow ref structs
1538-
if (pMT->IsByRefLike())
1540+
if (!allowByRefLike && pMT->IsByRefLike())
15391541
{
15401542
COMPlusThrow(kNotSupportedException, W("NotSupported_ByRefLike"));
15411543
}
15421544
}
15431545

15441546
/*
1545-
* Given a RuntimeType, queries info on how to instantiate the object.
1547+
* Given a RuntimeType, queries info on how to instantiate the type.
15461548
* pRuntimeType - [required] the RuntimeType object
15471549
* ppfnAllocator - [required, null-init] fnptr to the allocator
15481550
* mgd sig: void* -> object
@@ -1593,7 +1595,7 @@ extern "C" void QCALLTYPE RuntimeTypeHandle_GetActivationInfo(
15931595
typeHandle = ((REFLECTCLASSBASEREF)pRuntimeType.Get())->GetType();
15941596
}
15951597

1596-
RuntimeTypeHandle::ValidateTypeAbleToBeInstantiated(typeHandle, false /* fGetUninitializedObject */);
1598+
ValidateTypeAbleToBeInstantiated(typeHandle, true /* allowByRefLike */, false /* fGetUninitializedObject */);
15971599

15981600
MethodTable* pMT = typeHandle.AsMethodTable();
15991601
_ASSERTE(pMT != NULL);
@@ -1746,7 +1748,8 @@ extern "C" void QCALLTYPE ReflectionSerialization_GetCreateUninitializedObjectIn
17461748

17471749
TypeHandle type = pType.AsTypeHandle();
17481750

1749-
RuntimeTypeHandle::ValidateTypeAbleToBeInstantiated(type, true /* fForGetUninitializedInstance */);
1751+
// ByRefLike types can't be boxed (allocated as an uninitialized object).
1752+
ValidateTypeAbleToBeInstantiated(type, false /* allowRefLike */, true /* fForGetUninitializedInstance */);
17501753

17511754
MethodTable* pMT = type.AsMethodTable();
17521755

@@ -1919,7 +1922,8 @@ extern "C" void QCALLTYPE ReflectionInvocation_GetBoxInfo(
19191922

19201923
TypeHandle type = pType.AsTypeHandle();
19211924

1922-
RuntimeTypeHandle::ValidateTypeAbleToBeInstantiated(type, true /* fForGetUninitializedInstance */);
1925+
// ByRefLike types can't be boxed.
1926+
ValidateTypeAbleToBeInstantiated(type, false /* allowRefLike */, true /* fForGetUninitializedInstance */);
19231927

19241928
MethodTable* pMT = type.AsMethodTable();
19251929

src/coreclr/vm/runtimehandles.h

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -130,10 +130,6 @@ class RuntimeTypeHandle
130130

131131
static FCDECL1(MethodDesc *, GetFirstIntroducedMethod, ReflectClassBaseObject* pType);
132132
static FCDECL1(void, GetNextIntroducedMethod, MethodDesc **ppMethod);
133-
134-
// Helper methods not called by managed code
135-
136-
static void ValidateTypeAbleToBeInstantiated(TypeHandle typeHandle, bool fGetUninitializedObject);
137133
};
138134

139135
extern "C" void QCALLTYPE RuntimeTypeHandle_GetRuntimeTypeFromHandleSlow(void* typeHandleRaw, QCall::ObjectHandleOnStack result);

src/libraries/System.Private.CoreLib/src/System/Activator.RuntimeType.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -145,7 +145,7 @@ public static partial class Activator
145145

146146
// Casting the above object to T is technically invalid because
147147
// T can be ByRefLike (that is, ref struct). Roslyn blocks the
148-
// cast this in function with a "CS0030: Cannot convert type 'object' to 'T'",
148+
// cast in this function with a "CS0030: Cannot convert type 'object' to 'T'",
149149
// which is correct. However, since we are doing the IsValueType
150150
// check above, we know this code path will only be taken with
151151
// reference types and therefore the below Unsafe.As<> is safe.

src/libraries/System.Runtime/tests/System.Runtime.Tests/System/ActivatorTests.cs

Lines changed: 27 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
using System.IO;
99
using System.Reflection;
1010
using System.Reflection.Emit;
11+
using System.Runtime.InteropServices;
1112
using System.Runtime.Remoting;
1213
using Microsoft.DotNet.RemoteExecutor;
1314
using Xunit;
@@ -94,6 +95,28 @@ public void CreateInstance_NonPublicTypeWithPrivateDefaultConstructor_Success()
9495
Assert.Equal(-1, c2.Property);
9596
}
9697

98+
// Add attribute to avoid unused field warning
99+
[StructLayout(LayoutKind.Sequential)]
100+
ref struct RSNoCtor
101+
{
102+
public int Value;
103+
}
104+
105+
ref struct RSCtor
106+
{
107+
public int Value;
108+
public RSCtor() => Value = 10;
109+
}
110+
111+
[Fact]
112+
public void CreateInstance_ByRefTypeAsGenericParameter_Success()
113+
{
114+
Assert.Equal(0, Activator.CreateInstance<Span<int>>().Length);
115+
Assert.Equal(0, Activator.CreateInstance<ReadOnlySpan<string>>().Length);
116+
Assert.Equal(0, Activator.CreateInstance<RSNoCtor>().Value);
117+
Assert.Equal(10, Activator.CreateInstance<RSCtor>().Value);
118+
}
119+
97120
[Fact]
98121
public void CreateInstance_PublicOnlyTypeWithPrivateDefaultConstructor_ThrowsMissingMethodException()
99122
{
@@ -318,17 +341,15 @@ public void CreateInstance_AbstractClass_ThrowsMissingMemberException(Type type)
318341
[Theory]
319342
[InlineData(typeof(TypedReference))]
320343
[InlineData(typeof(RuntimeArgumentHandle))]
344+
[InlineData(typeof(Span<int>))]
345+
[InlineData(typeof(ReadOnlySpan<string>))]
346+
[InlineData(typeof(RSNoCtor))]
347+
[InlineData(typeof(RSCtor))]
321348
public void CreateInstance_BoxedByRefType_ThrowsNotSupportedException(Type type)
322349
{
323350
Assert.Throws<NotSupportedException>(() => Activator.CreateInstance(type));
324351
}
325352

326-
[Fact]
327-
public void CreateInstance_Span_ThrowsNotSupportedException()
328-
{
329-
CreateInstance_BoxedByRefType_ThrowsNotSupportedException(typeof(Span<int>));
330-
}
331-
332353
[Fact]
333354
public void CreateInstance_InterfaceType_ThrowsMissingMemberException()
334355
{

0 commit comments

Comments
 (0)