Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement RuntimeHelpers.SizeOf #100618

Merged
merged 17 commits into from
Apr 9, 2024
Merged
Show file tree
Hide file tree
Changes from 8 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -416,6 +416,23 @@ private static unsafe void DispatchTailCalls(
}
}
}

[LibraryImport(QCall, EntryPoint = "ReflectionInvocation_SizeOf")]
[SuppressGCTransition]
private static partial int SizeOf(QCallTypeHandle handle);

public static int SizeOf(RuntimeTypeHandle type)
{
if (type.IsNullHandle())
ThrowHelper.ThrowArgumentNullException(ExceptionArgument.type);

int result = SizeOf(new QCallTypeHandle(ref type));

if (result <= 0)
throw new ArgumentException(SR.Arg_TypeNotSupported);

return result;
}
}
// Helper class to assist with unsafe pinning of arbitrary objects.
// It's used by VM code.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -361,6 +361,27 @@ public static unsafe object GetUninitializedObject(

return RuntimeImports.RhNewObject(mt);
}

public static unsafe int SizeOf(RuntimeTypeHandle type)
{
if (type.IsNull)
ThrowHelper.ThrowArgumentNullException(ExceptionArgument.type);

MethodTable* mt = type.ToMethodTable();

if (mt->ElementType == EETypeElementType.Void
|| mt->IsGenericTypeDefinition)
{
throw new ArgumentException(SR.Arg_TypeNotSupported);
}

if (mt->IsValueType)
{
return (int)mt->ValueTypeSize;
}

return nint.Size;
}
}

// CLR arrays are laid out in memory as follows (multidimensional array bounds are optional):
Expand Down
1 change: 1 addition & 0 deletions src/coreclr/vm/qcallentrypoints.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -337,6 +337,7 @@ static const Entry s_QCall[] =
DllImportEntry(ReflectionInvocation_RunModuleConstructor)
DllImportEntry(ReflectionInvocation_CompileMethod)
DllImportEntry(ReflectionInvocation_PrepareMethod)
DllImportEntry(ReflectionInvocation_SizeOf)
DllImportEntry(ReflectionSerialization_GetCreateUninitializedObjectInfo)
#if defined(FEATURE_COMWRAPPERS)
DllImportEntry(ComWrappers_GetIUnknownImpl)
Expand Down
7 changes: 7 additions & 0 deletions src/coreclr/vm/reflectioninvocation.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2120,3 +2120,10 @@ FCIMPL2_IV(Object*, ReflectionEnum::InternalBoxEnum, ReflectClassBaseObject* tar
return OBJECTREFToObject(ret);
}
FCIMPLEND

extern "C" int32_t QCALLTYPE ReflectionInvocation_SizeOf(QCall::TypeHandle pType)
{
QCALL_CONTRACT_NO_GC_TRANSITION;

return pType.AsTypeHandle().GetSize();
}
2 changes: 2 additions & 0 deletions src/coreclr/vm/reflectioninvocation.h
Original file line number Diff line number Diff line change
Expand Up @@ -82,4 +82,6 @@ class ReflectionEnum {

extern "C" void QCALLTYPE Enum_GetValuesAndNames(QCall::TypeHandle pEnumType, QCall::ObjectHandleOnStack pReturnValues, QCall::ObjectHandleOnStack pReturnNames, BOOL fGetNames);

extern "C" int32_t QCALLTYPE ReflectionInvocation_SizeOf(QCall::TypeHandle pType);

#endif // _REFLECTIONINVOCATION_H_
1 change: 1 addition & 0 deletions src/libraries/System.Runtime/ref/System.Runtime.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13175,6 +13175,7 @@ public static void ProbeForSufficientStack() { }
[System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("Trimmer can't guarantee existence of class constructor")]
public static void RunClassConstructor(System.RuntimeTypeHandle type) { }
public static void RunModuleConstructor(System.ModuleHandle module) { }
public static int SizeOf(System.RuntimeTypeHandle type) { throw null; }
public static bool TryEnsureSufficientExecutionStack() { throw null; }
public delegate void CleanupCode(object? userData, bool exceptionThrown);
public delegate void TryCode(object? userData);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -437,6 +437,34 @@ public static void FixedAddressValueTypeTest()

Assert.Equal(fixedPtr1, fixedPtr2);
}

[InlineArray(3)]
private struct Byte3
{
public byte b1;
}

[Fact]
public static unsafe void SizeOf()
{
Assert.Equal(1, RuntimeHelpers.SizeOf(typeof(sbyte).TypeHandle));
Assert.Equal(1, RuntimeHelpers.SizeOf(typeof(byte).TypeHandle));
Assert.Equal(2, RuntimeHelpers.SizeOf(typeof(short).TypeHandle));
Assert.Equal(2, RuntimeHelpers.SizeOf(typeof(ushort).TypeHandle));
Assert.Equal(4, RuntimeHelpers.SizeOf(typeof(int).TypeHandle));
Assert.Equal(4, RuntimeHelpers.SizeOf(typeof(uint).TypeHandle));
Assert.Equal(8, RuntimeHelpers.SizeOf(typeof(long).TypeHandle));
Assert.Equal(8, RuntimeHelpers.SizeOf(typeof(ulong).TypeHandle));
Assert.Equal(4, RuntimeHelpers.SizeOf(typeof(float).TypeHandle));
Assert.Equal(8, RuntimeHelpers.SizeOf(typeof(double).TypeHandle));
Assert.Equal(3, RuntimeHelpers.SizeOf(typeof(Byte3).TypeHandle));
Assert.Equal(nint.Size, RuntimeHelpers.SizeOf(typeof(void*).TypeHandle));
jkoritzinsky marked this conversation as resolved.
Show resolved Hide resolved
Assert.Equal(nint.Size, RuntimeHelpers.SizeOf(typeof(delegate* <void>).TypeHandle));
Assert.Throws<ArgumentNullException>(() => RuntimeHelpers.SizeOf(default));
Assert.ThrowsAny<ArgumentException>(() => RuntimeHelpers.SizeOf(typeof(List<>).GetGenericArguments()[0].TypeHandle));
jkoritzinsky marked this conversation as resolved.
Show resolved Hide resolved
Assert.ThrowsAny<ArgumentException>(() => RuntimeHelpers.SizeOf(typeof(List<>).TypeHandle));
Assert.ThrowsAny<ArgumentException>(() => RuntimeHelpers.SizeOf(typeof(void).TypeHandle));
}
}

public struct Age
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -211,5 +211,20 @@ private static extern unsafe IntPtr GetSpanDataFrom(

[MethodImplAttribute(MethodImplOptions.InternalCall)]
private static extern bool SufficientExecutionStack();

[MethodImplAttribute(MethodImplOptions.InternalCall)]
private static extern int SizeOf(QCallTypeHandle handle);

public static int SizeOf(RuntimeTypeHandle type)
{
if (type.Value == IntPtr.Zero)
ThrowHelper.ThrowArgumentNullException(ExceptionArgument.type);

Type typeObj = Type.GetTypeFromHandle(type)!;
if (typeObj.IsGenericTypeDefinition || typeObj.IsGenericParameter || typeObj == typeof(void))
throw new ArgumentException(SR.Arg_TypeNotSupported);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this a limitation of mono runtime (which doesn't exist in coreclr)?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No, this is the same limitation in CoreCLR and NativeAOT (though the implementations are slightly different due to how the different type systems are implemented).


return SizeOf(new QCallTypeHandle(ref type));
}
}
}
1 change: 1 addition & 0 deletions src/mono/mono/metadata/icall-def.h
Original file line number Diff line number Diff line change
Expand Up @@ -439,6 +439,7 @@ HANDLES(RUNH_7, "InternalGetHashCode", ves_icall_System_Runtime_CompilerServices
HANDLES(RUNH_3a, "PrepareMethod", ves_icall_System_Runtime_CompilerServices_RuntimeHelpers_PrepareMethod, void, 3, (MonoMethod_ptr, gpointer, int))
HANDLES(RUNH_4, "RunClassConstructor", ves_icall_System_Runtime_CompilerServices_RuntimeHelpers_RunClassConstructor, void, 1, (MonoType_ptr))
HANDLES(RUNH_5, "RunModuleConstructor", ves_icall_System_Runtime_CompilerServices_RuntimeHelpers_RunModuleConstructor, void, 1, (MonoImage_ptr))
HANDLES(RUNH_9, "SizeOf", ves_icall_System_Runtime_CompilerServices_RuntimeHelpers_SizeOf, gint32, 1, (MonoQCallTypeHandle))
NOHANDLES(ICALL(RUNH_5h, "SufficientExecutionStack", ves_icall_System_Runtime_CompilerServices_RuntimeHelpers_SufficientExecutionStack))

ICALL_TYPE(GCH, "System.Runtime.InteropServices.GCHandle", GCH_1)
Expand Down
7 changes: 7 additions & 0 deletions src/mono/mono/metadata/icall.c
Original file line number Diff line number Diff line change
Expand Up @@ -1210,6 +1210,13 @@ ves_icall_System_Runtime_CompilerServices_RuntimeHelpers_PrepareMethod (MonoMeth
// FIXME: Implement
}

gint32
ves_icall_System_Runtime_CompilerServices_RuntimeHelpers_SizeOf (MonoQCallTypeHandle type, MonoError* error)
{
int align;
return mono_type_size (type.type, &align);
}

MonoObjectHandle
ves_icall_System_Object_MemberwiseClone (MonoObjectHandle this_obj, MonoError *error)
{
Expand Down
Loading