Skip to content
Draft
Show file tree
Hide file tree
Changes from all 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 @@ -39,6 +39,12 @@ private static partial IntPtr CreateReferenceTrackingHandleInternal(
out int memInSizeT,
out IntPtr mem);

[LibraryImport(RuntimeHelpers.QCall, EntryPoint = "ObjCMarshal_GetOrCreateTaggedMemory")]
private static partial void GetOrCreateTaggedMemoryInternal(
ObjectHandleOnStack obj,
out int memInSizeT,
out IntPtr mem);

[UnmanagedCallersOnly]
internal static unsafe void* InvokeUnhandledExceptionPropagation(Exception* pExceptionArg, IntPtr methodDesc, IntPtr* pContext, Exception* pException)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,16 @@ private static IntPtr CreateReferenceTrackingHandleInternal(
object obj,
out int memInSizeT,
out IntPtr mem)
{
// Rely on GetOrCreateTaggedMemoryInternal for state checking.
GetOrCreateTaggedMemoryInternal(obj, out memInSizeT, out mem);
return RuntimeImports.RhHandleAllocRefCounted(obj);
}

private static void GetOrCreateTaggedMemoryInternal(
object obj,
out int memInSizeT,
out IntPtr mem)
{
if (!s_initialized)
{
Expand All @@ -139,7 +149,6 @@ private static IntPtr CreateReferenceTrackingHandleInternal(
var trackerInfo = s_objects.GetOrAdd(obj, static o => new ObjcTrackingInformation());
trackerInfo.EnsureInitialized(obj);
trackerInfo.GetTaggedMemory(out memInSizeT, out mem);
return RuntimeImports.RhHandleAllocRefCounted(obj);
}

internal class ObjcTrackingInformation
Expand Down
5 changes: 5 additions & 0 deletions src/coreclr/vm/interoplibinterface.h
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,11 @@ extern "C" void* QCALLTYPE ObjCMarshal_CreateReferenceTrackingHandle(
_Out_ int* memInSizeT,
_Outptr_ void** mem);

extern "C" void QCALLTYPE ObjCMarshal_GetOrCreateTaggedMemory(
_In_ QCall::ObjectHandleOnStack obj,
_Out_ int* memInSizeT,
_Outptr_ void** mem);

extern "C" BOOL QCALLTYPE ObjCMarshal_TrySetGlobalMessageSendCallback(
_In_ ObjCMarshalNative::MessageSendFunction msgSendFunction,
_In_ void* fptr);
Expand Down
103 changes: 74 additions & 29 deletions src/coreclr/vm/interoplibinterface_objc.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,62 @@ extern "C" BOOL QCALLTYPE ObjCMarshal_TryInitializeReferenceTracker(
return success;
}

namespace
{
void* TaggedMemoryForObjectHelper(
_In_ QCall::ObjectHandleOnStack obj,
_Out_ size_t* memInSizeT,
_Out_opt_ OBJECTHANDLE* instHandle)
{
Comment on lines +62 to +66
CONTRACTL
{
THROWS;
GC_TRIGGERS;
MODE_PREEMPTIVE;
PRECONDITION(CheckPointer(memInSizeT));
}
CONTRACTL_END;

// The reference tracking system must be initialized.
if (!g_ReferenceTrackerInitialized)
COMPlusThrow(kInvalidOperationException, W("InvalidOperation_ObjectiveCMarshalNotInitialized"));
Comment on lines +76 to +78

void* taggedMemoryLocal;

// Switch to Cooperative mode since object references
// are being manipulated.
{
GCX_COOP();

struct
{
OBJECTREF objRef;
} gc;
gc.objRef = NULL;
GCPROTECT_BEGIN(gc);

gc.objRef = obj.Get();

// The object's type must be marked appropriately and with a finalizer.
if (!gc.objRef->GetMethodTable()->IsTrackedReferenceWithFinalizer())
COMPlusThrow(kInvalidOperationException, W("InvalidOperation_ObjectiveCTypeNoFinalizer"));

// Initialize the syncblock for this instance.
SyncBlock* syncBlock = gc.objRef->GetSyncBlock();
InteropSyncBlockInfo* interopInfo = syncBlock->GetInteropInfo();
taggedMemoryLocal = interopInfo->AllocTaggedMemory(memInSizeT);
_ASSERTE(taggedMemoryLocal != NULL);

if (instHandle != NULL)
*instHandle = GetAppDomain()->CreateTypedHandle(gc.objRef, HNDTYPE_REFCOUNTED);

GCPROTECT_END();
}

return taggedMemoryLocal;
}
}

extern "C" void* QCALLTYPE ObjCMarshal_CreateReferenceTrackingHandle(
_In_ QCall::ObjectHandleOnStack obj,
_Out_ int* memInSizeT,
Expand All @@ -72,44 +128,33 @@ extern "C" void* QCALLTYPE ObjCMarshal_CreateReferenceTrackingHandle(

BEGIN_QCALL;

// The reference tracking system must be initialized.
if (!g_ReferenceTrackerInitialized)
COMPlusThrow(kInvalidOperationException, W("InvalidOperation_ObjectiveCMarshalNotInitialized"));

// Switch to Cooperative mode since object references
// are being manipulated.
{
GCX_COOP();

struct
{
OBJECTREF objRef;
} gc;
gc.objRef = NULL;
GCPROTECT_BEGIN(gc);

gc.objRef = obj.Get();
taggedMemoryLocal = TaggedMemoryForObjectHelper(obj, &memInSizeTLocal, &instHandle);
END_QCALL;

// The object's type must be marked appropriately and with a finalizer.
if (!gc.objRef->GetMethodTable()->IsTrackedReferenceWithFinalizer())
COMPlusThrow(kInvalidOperationException, W("InvalidOperation_ObjectiveCTypeNoFinalizer"));
*memInSizeT = (int)memInSizeTLocal;
*mem = taggedMemoryLocal;
return (void*)instHandle;
}

// Initialize the syncblock for this instance.
SyncBlock* syncBlock = gc.objRef->GetSyncBlock();
InteropSyncBlockInfo* interopInfo = syncBlock->GetInteropInfo();
taggedMemoryLocal = interopInfo->AllocTaggedMemory(&memInSizeTLocal);
_ASSERTE(taggedMemoryLocal != NULL);
extern "C" void QCALLTYPE ObjCMarshal_GetOrCreateTaggedMemory(
_In_ QCall::ObjectHandleOnStack obj,
_Out_ int* memInSizeT,
_Outptr_ void** mem)
{
QCALL_CONTRACT;
_ASSERTE(memInSizeT != NULL);
_ASSERTE(mem != NULL);

instHandle = GetAppDomain()->CreateTypedHandle(gc.objRef, HNDTYPE_REFCOUNTED);
size_t memInSizeTLocal;
void* taggedMemoryLocal;

GCPROTECT_END();
}
BEGIN_QCALL;

taggedMemoryLocal = TaggedMemoryForObjectHelper(obj, &memInSizeTLocal, NULL);
END_QCALL;

*memInSizeT = (int)memInSizeTLocal;
*mem = taggedMemoryLocal;
return (void*)instHandle;
}

namespace
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 @@ -441,6 +441,7 @@ static const Entry s_QCall[] =
DllImportEntry(ObjCMarshal_TrySetGlobalMessageSendCallback)
DllImportEntry(ObjCMarshal_TryInitializeReferenceTracker)
DllImportEntry(ObjCMarshal_CreateReferenceTrackingHandle)
DllImportEntry(ObjCMarshal_GetOrCreateTaggedMemory)
#endif
#if defined(FEATURE_JAVAMARSHAL)
DllImportEntry(JavaMarshal_Initialize)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,9 @@ public static GCHandle CreateReferenceTrackingHandle(
out Span<IntPtr> taggedMemory)
=> throw new PlatformNotSupportedException();

public static Span<IntPtr> GetOrCreateTaggedMemory(object obj)
=> throw new PlatformNotSupportedException();

/// <summary>
/// Objective-C msgSend function override options.
/// </summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,8 @@ public static unsafe void Initialize(
/// return a new handle each time but the same tagged memory will be returned. The
/// tagged memory is only guaranteed to be zero initialized on the first call.
///
/// The tagged memory returned is the same as the memory returned from <see cref="GetOrCreateTaggedMemory" />.
///
/// The caller is responsible for freeing the returned <see cref="GCHandle"/>.
/// </remarks>
public static GCHandle CreateReferenceTrackingHandle(
Expand All @@ -126,6 +128,50 @@ public static GCHandle CreateReferenceTrackingHandle(
return GCHandle.FromIntPtr(refCountHandle);
}

/// <summary>
/// Request a pointer to memory tagged for the supplied object.
/// </summary>
/// <param name="obj">The object whose tagged memory to return.</param>
/// <returns>A pointer to memory tagged to the object.</returns>
/// <exception cref="InvalidOperationException">Thrown if the ObjectiveCMarshal API has not been initialized.</exception>
/// <remarks>
/// The <see cref="Initialize" /> function must be called prior to calling this function.
///
/// The <paramref name="obj"/> parameter must have a type in its hierarchy marked with
/// <see cref="ObjectiveCTrackedTypeAttribute"/>.
///
/// The "Is Referenced" callback passed to <see cref="Initialize" />
/// will be passed the memory returned from this function.
/// The memory it points at is defined by the length in the <see cref="Span{IntPtr}"/> and
/// will be zeroed out. It will be available until <paramref name="obj"/> is collected by the GC.
/// The returned memory can be used for any purpose by the caller of this function and usable
/// during the "Is Referenced" callback.
///
/// Calling this function multiple times with the same <paramref name="obj"/> will
/// return the same tagged memory. It is only guaranteed to be zero initialized on
/// the first call.
///
/// The return value is the same as the tagged memory returned from <see cref="CreateReferenceTrackingHandle" />.
/// </remarks>
public static Span<IntPtr> GetOrCreateTaggedMemory(object obj)
{
ArgumentNullException.ThrowIfNull(obj);

GetOrCreateTaggedMemoryInternal(
#if NATIVEAOT
obj,
#else
ObjectHandleOnStack.Create(ref obj),
#endif
out int memInSizeT,
out IntPtr mem);

unsafe
{
return new Span<IntPtr>(mem.ToPointer(), memInSizeT);
}
}

/// <summary>
/// Objective-C msgSend function override options.
/// </summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2448,6 +2448,7 @@ public static unsafe void Initialize(
public static GCHandle CreateReferenceTrackingHandle(
object obj,
out System.Span<System.IntPtr> taggedMemory) => throw null;
public static System.Span<System.IntPtr> GetOrCreateTaggedMemory(object obj) => throw null;
public enum MessageSendFunction
{
MsgSend,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,5 +33,10 @@ private static IntPtr CreateReferenceTrackingHandleInternal(
ObjectHandleOnStack obj,
out int memInSizeT,
out IntPtr mem) => throw new NotImplementedException();

private static void GetOrCreateTaggedMemoryInternal(
ObjectHandleOnStack obj,
out int memInSizeT,
out IntPtr mem) => throw new NotImplementedException();
}
}
Loading
Loading