Skip to content
Merged
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ public partial class Object
// so that other object may only call this method on themselves. It is intended to
// support the ICloneable interface.
[Intrinsic]
protected unsafe object MemberwiseClone()
protected internal unsafe object MemberwiseClone()
{
object clone = RuntimeHelpers.AllocateUninitializedClone(this);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,9 +37,23 @@ public static partial class RuntimeHelpers
// cloned when you pass them around, and are always passed by value.
// Of course, reference types are not cloned.
//
[MethodImpl(MethodImplOptions.InternalCall)]
[return: NotNullIfNotNull(nameof(obj))]
public static extern object? GetObjectValue(object? obj);
public static unsafe object? GetObjectValue(object? obj)
{
if (obj == null)
return null;

MethodTable* pMT = GetMethodTable(obj);

if (!pMT->IsValueType || pMT->IsPrimitive)
return obj;

// Technically we could return boxed DateTimes and Decimals without
// copying them here, but VB realized that this would be a breaking change
// for their customers. So copy them.

return obj.MemberwiseClone();
}

// RunClassConstructor causes the class constructor for the given type to be triggered
// in the current domain. After this call returns, the class constructor is guaranteed to
Expand Down Expand Up @@ -487,6 +501,8 @@ internal unsafe struct MethodTable
private const uint enum_flag_Category_Mask = 0x000F0000;
private const uint enum_flag_Category_ValueType = 0x00040000;
private const uint enum_flag_Category_Nullable = 0x00050000;
private const uint enum_flag_Category_PrimitiveValueType = 0x00060000; // sub-category of ValueType, Enum or primitive value type
private const uint enum_flag_Category_TruePrimitive = 0x00070000; // sub-category of ValueType, Primitive (ELEMENT_TYPE_I, etc.)
private const uint enum_flag_Category_ValueType_Mask = 0x000C0000;
private const uint enum_flag_Category_Interface = 0x000C0000;
// Types that require non-trivial interface cast have this bit set in the category
Expand Down Expand Up @@ -565,6 +581,9 @@ public int MultiDimensionalArrayRank

public bool IsByRefLike => (Flags & (enum_flag_HasComponentSize | enum_flag_IsByRefLike)) == enum_flag_IsByRefLike;

// Warning! UNLIKE the similarly named Reflection api, this method also returns "true" for Enums.
public bool IsPrimitive => (Flags & enum_flag_Category_Mask) is enum_flag_Category_PrimitiveValueType or enum_flag_Category_TruePrimitive;

public bool HasInstantiation => (Flags & enum_flag_HasComponentSize) == 0 && (Flags & enum_flag_GenericsMask) != enum_flag_GenericsMask_NonGeneric;

public bool IsGenericTypeDefinition => (Flags & (enum_flag_HasComponentSize | enum_flag_GenericsMask)) == enum_flag_GenericsMask_TypicalInst;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
// The .NET Foundation licenses this file to you under the MIT license.

using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;

namespace System.Runtime
{
Expand All @@ -26,7 +27,7 @@ namespace System.Runtime
/// properties are instead thread-safe, and safe to use if <see cref="Dispose"/> is not concurrently invoked as well.
/// </para>
/// </remarks>
public struct DependentHandle : IDisposable
public partial struct DependentHandle : IDisposable
{
// =========================================================================================
// This struct collects all operations on native DependentHandles. The DependentHandle
Expand Down Expand Up @@ -62,16 +63,18 @@ public struct DependentHandle : IDisposable
/// <param name="dependent">The dependent object instance to associate with <paramref name="target"/>.</param>
public DependentHandle(object? target, object? dependent)
{
// no need to check for null result: InternalInitialize expected to throw OOM.
_handle = InternalInitialize(target, dependent);
IntPtr handle = InternalAlloc(target, dependent);
if (handle == 0)
handle = InternalAllocWithGCTransition(target, dependent);
_handle = handle;
}

/// <summary>
/// Gets a value indicating whether this instance was constructed with
/// <see cref="DependentHandle(object?, object?)"/> and has not yet been disposed.
/// </summary>
/// <remarks>This property is thread-safe.</remarks>
public readonly bool IsAllocated => (nint)_handle != 0;
public readonly bool IsAllocated => _handle != 0;

/// <summary>
/// Gets or sets the target object instance for the current handle. The target can only be set to a <see langword="null"/> value
Expand All @@ -87,7 +90,7 @@ readonly get
{
IntPtr handle = _handle;

if ((nint)handle == 0)
if (handle == 0)
{
ThrowHelper.ThrowInvalidOperationException();
}
Expand All @@ -98,7 +101,7 @@ readonly get
{
IntPtr handle = _handle;

if ((nint)handle == 0 || value is not null)
if (handle == 0 || value is not null)
{
ThrowHelper.ThrowInvalidOperationException();
}
Expand All @@ -124,7 +127,7 @@ readonly get
{
IntPtr handle = _handle;

if ((nint)handle == 0)
if (handle == 0)
{
ThrowHelper.ThrowInvalidOperationException();
}
Expand All @@ -135,7 +138,7 @@ readonly get
{
IntPtr handle = _handle;

if ((nint)handle == 0)
if (handle == 0)
{
ThrowHelper.ThrowInvalidOperationException();
}
Expand All @@ -160,7 +163,7 @@ public readonly (object? Target, object? Dependent) TargetAndDependent
{
IntPtr handle = _handle;

if ((nint)handle == 0)
if (handle == 0)
{
ThrowHelper.ThrowInvalidOperationException();
}
Expand Down Expand Up @@ -222,16 +225,26 @@ public void Dispose()
// (if not already there) and frees the handle if needed.
IntPtr handle = _handle;

if ((nint)handle != 0)
if (handle != 0)
{
_handle = IntPtr.Zero;
_handle = 0;

InternalFree(handle);
if (!InternalFree(handle))
{
InternalFreeWithGCTransition(handle);
}
}
}

[MethodImpl(MethodImplOptions.InternalCall)]
private static extern IntPtr InternalInitialize(object? target, object? dependent);
private static extern IntPtr InternalAlloc(object? target, object? dependent);

[MethodImpl(MethodImplOptions.NoInlining)]
private static IntPtr InternalAllocWithGCTransition(object? target, object? dependent)
=> _InternalAllocWithGCTransition(ObjectHandleOnStack.Create(ref target), ObjectHandleOnStack.Create(ref dependent));

[LibraryImport(RuntimeHelpers.QCall, EntryPoint = "DependentHandle_InternalAllocWithGCTransition")]
private static partial IntPtr _InternalAllocWithGCTransition(ObjectHandleOnStack target, ObjectHandleOnStack dependent);

#if DEBUG
[MethodImpl(MethodImplOptions.InternalCall)]
Expand All @@ -258,6 +271,13 @@ public void Dispose()
private static extern void InternalSetTargetToNull(IntPtr dependentHandle);

[MethodImpl(MethodImplOptions.InternalCall)]
private static extern void InternalFree(IntPtr dependentHandle);
private static extern bool InternalFree(IntPtr dependentHandle);

[MethodImpl(MethodImplOptions.NoInlining)]
private static void InternalFreeWithGCTransition(IntPtr dependentHandle)
=> _InternalFreeWithGCTransition(dependentHandle);

[LibraryImport(RuntimeHelpers.QCall, EntryPoint = "DependentHandle_InternalFreeWithGCTransition")]
private static partial void _InternalFreeWithGCTransition(IntPtr dependentHandle);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,39 @@ namespace System.Runtime.InteropServices
{
public partial struct GCHandle
{
internal static IntPtr InternalAlloc(object? value, GCHandleType type)
{
IntPtr handle = _InternalAlloc(value, type);
if (handle == 0)
handle = InternalAllocWithGCTransition(value, type);
return handle;
}

[MethodImpl(MethodImplOptions.InternalCall)]
internal static extern IntPtr InternalAlloc(object? value, GCHandleType type);
private static extern IntPtr _InternalAlloc(object? value, GCHandleType type);

[MethodImpl(MethodImplOptions.NoInlining)]
private static IntPtr InternalAllocWithGCTransition(object? value, GCHandleType type)
=> _InternalAllocWithGCTransition(ObjectHandleOnStack.Create(ref value), type);

[LibraryImport(RuntimeHelpers.QCall, EntryPoint = "GCHandle_InternalAllocWithGCTransition")]
private static partial IntPtr _InternalAllocWithGCTransition(ObjectHandleOnStack value, GCHandleType type);

internal static void InternalFree(IntPtr handle)
{
if (!_InternalFree(handle))
InternalFreeWithGCTransition(handle);
}

[MethodImpl(MethodImplOptions.InternalCall)]
internal static extern void InternalFree(IntPtr handle);
private static extern bool _InternalFree(IntPtr handle);

[MethodImpl(MethodImplOptions.NoInlining)]
private static void InternalFreeWithGCTransition(IntPtr dependentHandle)
=> _InternalFreeWithGCTransition(dependentHandle);

[LibraryImport(RuntimeHelpers.QCall, EntryPoint = "GCHandle_InternalFreeWithGCTransition")]
private static partial void _InternalFreeWithGCTransition(IntPtr dependentHandle);

#if DEBUG
// The runtime performs additional checks in debug builds
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,11 @@ public static partial class Marshal
internal static Guid IID_IUnknown = new Guid(0, 0, 0, 0xC0, 0, 0, 0, 0, 0, 0, 0x46);
#endif //FEATURE_COMINTEROP

[MethodImpl(MethodImplOptions.InternalCall)]
internal static extern int SizeOfHelper(Type t, bool throwIfNotMarshalable);
internal static int SizeOfHelper(RuntimeType t, [MarshalAs(UnmanagedType.Bool)] bool throwIfNotMarshalable)
=> SizeOfHelper(new QCallTypeHandle(ref t), throwIfNotMarshalable);

[LibraryImport(RuntimeHelpers.QCall, EntryPoint = "MarshalNative_SizeOfHelper")]
private static partial int SizeOfHelper(QCallTypeHandle t, [MarshalAs(UnmanagedType.Bool)] bool throwIfNotMarshalable);

[UnconditionalSuppressMessage("ReflectionAnalysis", "IL2070:UnrecognizedReflectionPattern",
Justification = "Trimming doesn't affect types eligible for marshalling. Different exception for invalid inputs doesn't matter.")]
Expand Down Expand Up @@ -234,24 +237,95 @@ private static void PrelinkCore(MethodInfo m)
/// true, this routine will call DestroyStructure() first.
/// </summary>
[RequiresDynamicCode("Marshalling code for the object might not be available. Use the StructureToPtr<T> overload instead.")]
[MethodImpl(MethodImplOptions.InternalCall)]
[EditorBrowsable(EditorBrowsableState.Never)]
public static extern void StructureToPtr(object structure, IntPtr ptr, bool fDeleteOld);
public static unsafe void StructureToPtr(object structure, IntPtr ptr, bool fDeleteOld)
{
ArgumentNullException.ThrowIfNull(ptr);
ArgumentNullException.ThrowIfNull(structure);

MethodTable* pMT = RuntimeHelpers.GetMethodTable(structure);

if (pMT->HasInstantiation)
throw new ArgumentException(SR.Argument_NeedNonGenericObject, nameof(structure));

delegate*<ref byte, byte*, int, ref CleanupWorkListElement, void> structMarshalStub;
nuint size;
if (!TryGetStructMarshalStub((IntPtr)pMT, &structMarshalStub, &size))
throw new ArgumentException(SR.Argument_MustHaveLayoutOrBeBlittable, nameof(structure));

if (structMarshalStub != null)
{
if (fDeleteOld)
{
structMarshalStub(ref structure.GetRawData(), (byte*)ptr, MarshalOperation.Cleanup, ref Unsafe.NullRef<CleanupWorkListElement>());
}

structMarshalStub(ref structure.GetRawData(), (byte*)ptr, MarshalOperation.Marshal, ref Unsafe.NullRef<CleanupWorkListElement>());
}
else
{
Buffer.Memmove(ref *(byte*)ptr, ref structure.GetRawData(), size);
}
}

/// <summary>
/// Helper function to copy a pointer into a preallocated structure.
/// </summary>
[MethodImpl(MethodImplOptions.InternalCall)]
private static extern void PtrToStructureHelper(IntPtr ptr, object structure, bool allowValueClasses);
private static unsafe void PtrToStructureHelper(IntPtr ptr, object structure, bool allowValueClasses)
{
MethodTable* pMT = RuntimeHelpers.GetMethodTable(structure);

if (!allowValueClasses && pMT->IsValueType)
throw new ArgumentException(SR.Argument_StructMustNotBeValueClass, nameof(structure));

delegate*<ref byte, byte*, int, ref CleanupWorkListElement, void> structMarshalStub;
nuint size;
if (!TryGetStructMarshalStub((IntPtr)pMT, &structMarshalStub, &size))
throw new ArgumentException(SR.Argument_MustHaveLayoutOrBeBlittable, nameof(structure));

if (structMarshalStub != null)
{
structMarshalStub(ref structure.GetRawData(), (byte*)ptr, MarshalOperation.Unmarshal, ref Unsafe.NullRef<CleanupWorkListElement>());
}
else
{
Buffer.Memmove(ref structure.GetRawData(), ref *(byte*)ptr, size);
}
}

/// <summary>
/// Frees all substructures pointed to by the native memory block.
/// "structuretype" is used to provide layout information.
/// nameof(structuretype) is used to provide layout information.
/// </summary>
[RequiresDynamicCode("Marshalling code for the object might not be available. Use the DestroyStructure<T> overload instead.")]
[MethodImpl(MethodImplOptions.InternalCall)]
[EditorBrowsable(EditorBrowsableState.Never)]
public static extern void DestroyStructure(IntPtr ptr, Type structuretype);
public static unsafe void DestroyStructure(IntPtr ptr, Type structuretype)
{
ArgumentNullException.ThrowIfNull(ptr);
ArgumentNullException.ThrowIfNull(structuretype);

if (structuretype is not RuntimeType rt)
throw new ArgumentException(SR.Argument_MustBeRuntimeType, nameof(structuretype));

if (rt.IsGenericType)
throw new ArgumentException(SR.Argument_NeedNonGenericType, nameof(structuretype));

delegate*<ref byte, byte*, int, ref CleanupWorkListElement, void> structMarshalStub;
nuint size;
if (!TryGetStructMarshalStub(rt.GetUnderlyingNativeHandle(), &structMarshalStub, &size))
throw new ArgumentException(SR.Argument_MustHaveLayoutOrBeBlittable, nameof(structuretype));

GC.KeepAlive(rt);

if (structMarshalStub != null)
{
structMarshalStub(ref Unsafe.NullRef<byte>(), (byte*)ptr, MarshalOperation.Cleanup, ref Unsafe.NullRef<CleanupWorkListElement>());
}
}

[LibraryImport(RuntimeHelpers.QCall, EntryPoint = "MarshalNative_TryGetStructMarshalStub")]
[return: MarshalAs(UnmanagedType.Bool)]
private static unsafe partial bool TryGetStructMarshalStub(IntPtr th, delegate*<ref byte, byte*, int, ref CleanupWorkListElement, void>* structMarshalStub, nuint* size);

// Note: Callers are required to keep obj alive
internal static unsafe bool IsPinnable(object? obj)
Expand Down
Loading