Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/dotnet/runtime.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/coreclr/System.Private.CoreLib/System.Private.CoreLib.csproj3
-rw-r--r--src/coreclr/System.Private.CoreLib/src/System/Runtime/InteropServices/ObjectiveCMarshal.CoreCLR.cs64
-rw-r--r--src/coreclr/System.Private.CoreLib/src/System/StubHelpers.cs18
-rw-r--r--src/coreclr/clrdefinitions.cmake13
-rw-r--r--src/coreclr/clrfeatures.cmake8
-rw-r--r--src/coreclr/debug/daccess/daccess.cpp9
-rw-r--r--src/coreclr/debug/daccess/dacdbiimpl.cpp14
-rw-r--r--src/coreclr/debug/daccess/dacimpl.h2
-rw-r--r--src/coreclr/debug/daccess/request.cpp40
-rw-r--r--src/coreclr/dlls/mscoree/unixinterface.cpp2
-rw-r--r--src/coreclr/gc/env/gcenv.ee.h6
-rw-r--r--src/coreclr/gc/gc.cpp6
-rw-r--r--src/coreclr/gc/gcenv.ee.standalone.inl8
-rw-r--r--src/coreclr/gc/gcinterface.ee.h10
-rw-r--r--src/coreclr/gc/objecthandle.cpp65
-rw-r--r--src/coreclr/gc/sample/gcenv.ee.cpp4
-rw-r--r--src/coreclr/inc/pinvokeoverride.h12
-rw-r--r--src/coreclr/inc/utilcode.h4
-rw-r--r--src/coreclr/interop/CMakeLists.txt4
-rw-r--r--src/coreclr/pal/inc/pal.h22
-rw-r--r--src/coreclr/utilcode/util.cpp6
-rw-r--r--src/coreclr/vm/CMakeLists.txt162
-rw-r--r--src/coreclr/vm/amd64/unixasmhelpers.S1
-rw-r--r--src/coreclr/vm/corelib.cpp3
-rw-r--r--src/coreclr/vm/corelib.h11
-rw-r--r--src/coreclr/vm/crossgen/CMakeLists.txt3
-rw-r--r--src/coreclr/vm/dllimport.cpp47
-rw-r--r--src/coreclr/vm/dllimport.h3
-rw-r--r--src/coreclr/vm/ecalllist.h16
-rw-r--r--src/coreclr/vm/exceptionhandling.cpp154
-rw-r--r--src/coreclr/vm/gcenv.ee.cpp61
-rw-r--r--src/coreclr/vm/gcenv.ee.h2
-rw-r--r--src/coreclr/vm/gcenv.ee.standalone.cpp2
-rw-r--r--src/coreclr/vm/gcenv.ee.static.cpp2
-rw-r--r--src/coreclr/vm/interoplibinterface.cpp76
-rw-r--r--src/coreclr/vm/interoplibinterface.h83
-rw-r--r--src/coreclr/vm/interoplibinterface_objc.cpp394
-rw-r--r--src/coreclr/vm/interoplibinterface_shared.cpp144
-rw-r--r--src/coreclr/vm/metasig.h8
-rw-r--r--src/coreclr/vm/methodtable.cpp12
-rw-r--r--src/coreclr/vm/methodtable.h5
-rw-r--r--src/coreclr/vm/methodtablebuilder.cpp27
-rw-r--r--src/coreclr/vm/namespace.h2
-rw-r--r--src/coreclr/vm/pinvokeoverride.cpp29
-rw-r--r--src/coreclr/vm/rcwrefcache.cpp2
-rw-r--r--src/coreclr/vm/syncblk.h36
-rw-r--r--src/coreclr/vm/wellknownattributes.h3
-rw-r--r--src/libraries/System.Private.CoreLib/src/Resources/Strings.resx12
-rw-r--r--src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems18
-rw-r--r--src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/ObjectiveC/ObjectiveCMarshal.PlatformNotSupported.cs146
-rw-r--r--src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/ObjectiveC/ObjectiveCMarshal.cs178
-rw-r--r--src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/ObjectiveC/ObjectiveCTrackedTypeAttribute.cs20
-rw-r--r--src/libraries/System.Runtime.InteropServices/ref/System.Runtime.InteropServices.cs37
-rw-r--r--src/libraries/System.Runtime.InteropServices/tests/System.Runtime.InteropServices.Tests.csproj4
-rw-r--r--src/libraries/System.Runtime.InteropServices/tests/System/Runtime/InteropServices/ObjectiveC/LibObjC.cs74
-rw-r--r--src/libraries/System.Runtime.InteropServices/tests/System/Runtime/InteropServices/ObjectiveC/MessageSendTests.cs148
-rw-r--r--src/libraries/System.Runtime.InteropServices/tests/System/Runtime/InteropServices/ObjectiveC/PendingExceptionTests.cs97
-rw-r--r--src/mono/System.Private.CoreLib/System.Private.CoreLib.csproj2
-rw-r--r--src/mono/System.Private.CoreLib/src/System/Runtime/InteropServices/ObjectiveCMarshal.Mono.cs37
-rw-r--r--src/tests/Interop/CMakeLists.txt3
-rw-r--r--src/tests/Interop/ObjectiveC/AutoReleaseTest/AutoReleaseTest.csproj2
-rw-r--r--src/tests/Interop/ObjectiveC/AutoReleaseTest/CMakeLists.txt (renamed from src/tests/Interop/ObjectiveC/CMakeLists.txt)0
-rw-r--r--src/tests/Interop/ObjectiveC/AutoReleaseTest/autorelease.mm (renamed from src/tests/Interop/ObjectiveC/autorelease.mm)0
-rw-r--r--src/tests/Interop/ObjectiveC/ObjectiveCMarshalAPI/CMakeLists.txt16
-rw-r--r--src/tests/Interop/ObjectiveC/ObjectiveCMarshalAPI/Directory.Build.props7
-rw-r--r--src/tests/Interop/ObjectiveC/ObjectiveCMarshalAPI/NativeObjCMarshalTests.cpp142
-rw-r--r--src/tests/Interop/ObjectiveC/ObjectiveCMarshalAPI/ObjectiveCMarshalAPI.csproj14
-rw-r--r--src/tests/Interop/ObjectiveC/ObjectiveCMarshalAPI/Program.cs326
-rw-r--r--src/tests/issues.targets3
69 files changed, 2590 insertions, 304 deletions
diff --git a/src/coreclr/System.Private.CoreLib/System.Private.CoreLib.csproj b/src/coreclr/System.Private.CoreLib/System.Private.CoreLib.csproj
index 79ea886ffaa..3da68ee9cbf 100644
--- a/src/coreclr/System.Private.CoreLib/System.Private.CoreLib.csproj
+++ b/src/coreclr/System.Private.CoreLib/System.Private.CoreLib.csproj
@@ -297,6 +297,9 @@
</Compile>
<Compile Include="$(BclSourcesRoot)\System\Threading\ClrThreadPoolBoundHandle.Windows.cs" />
</ItemGroup>
+ <ItemGroup Condition="'$(FeatureObjCMarshal)' == 'true'">
+ <Compile Include="$(BclSourcesRoot)\System\Runtime\InteropServices\ObjectiveCMarshal.CoreCLR.cs" />
+ </ItemGroup>
<!-- Include additional sources shared files in the compilation -->
<Import Project="$(LibrariesProjectRoot)\System.Private.CoreLib\src\System.Private.CoreLib.Shared.projitems" Label="Shared" />
diff --git a/src/coreclr/System.Private.CoreLib/src/System/Runtime/InteropServices/ObjectiveCMarshal.CoreCLR.cs b/src/coreclr/System.Private.CoreLib/src/System/Runtime/InteropServices/ObjectiveCMarshal.CoreCLR.cs
new file mode 100644
index 00000000000..8616a0f4ac5
--- /dev/null
+++ b/src/coreclr/System.Private.CoreLib/src/System/Runtime/InteropServices/ObjectiveCMarshal.CoreCLR.cs
@@ -0,0 +1,64 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System.Diagnostics;
+using System.Runtime.Versioning;
+using System.Runtime.CompilerServices;
+
+namespace System.Runtime.InteropServices.ObjectiveC
+{
+ public static partial class ObjectiveCMarshal
+ {
+ /// <summary>
+ /// Sets a pending exception to be thrown the next time the runtime is entered from an overridden msgSend P/Invoke.
+ /// </summary>
+ /// <param name="exception">The exception.</param>
+ /// <remarks>
+ /// If <c>null</c> is supplied any pending exception is discarded.
+ /// </remarks>
+ public static void SetMessageSendPendingException(Exception? exception)
+ {
+ System.StubHelpers.StubHelpers.SetPendingExceptionObject(exception);
+ }
+
+ [DllImport(RuntimeHelpers.QCall)]
+ private static extern bool TrySetGlobalMessageSendCallback(
+ MessageSendFunction msgSendFunction,
+ IntPtr func);
+
+ [DllImport(RuntimeHelpers.QCall)]
+ private static unsafe extern bool TryInitializeReferenceTracker(
+ delegate* unmanaged<void> beginEndCallback,
+ delegate* unmanaged<IntPtr, int> isReferencedCallback,
+ delegate* unmanaged<IntPtr, void> trackedObjectEnteredFinalization);
+
+ [DllImport(RuntimeHelpers.QCall)]
+ private static extern IntPtr CreateReferenceTrackingHandleInternal(
+ ObjectHandleOnStack obj,
+ out int memInSizeT,
+ out IntPtr mem);
+
+ internal static bool AvailableUnhandledExceptionPropagation()
+ {
+ return s_unhandledExceptionPropagationHandler != null;
+ }
+
+ internal static unsafe void* InvokeUnhandledExceptionPropagation(
+ Exception exception,
+ object methodInfoStub,
+ out IntPtr context)
+ {
+ context = IntPtr.Zero;
+ if (s_unhandledExceptionPropagationHandler == null)
+ return null;
+
+ Debug.Assert(methodInfoStub is RuntimeMethodInfoStub);
+ var runtimeHandle = new RuntimeMethodHandle((RuntimeMethodInfoStub)methodInfoStub);
+ var callback = s_unhandledExceptionPropagationHandler(exception, runtimeHandle, out context);
+ if (callback != null)
+ return callback;
+
+ return null;
+ }
+ }
+} \ No newline at end of file
diff --git a/src/coreclr/System.Private.CoreLib/src/System/StubHelpers.cs b/src/coreclr/System.Private.CoreLib/src/System/StubHelpers.cs
index 433c06957c0..33435681e9e 100644
--- a/src/coreclr/System.Private.CoreLib/src/System/StubHelpers.cs
+++ b/src/coreclr/System.Private.CoreLib/src/System/StubHelpers.cs
@@ -1229,6 +1229,24 @@ namespace System.StubHelpers
#endif // FEATURE_COMINTEROP
+ [ThreadStatic]
+ private static Exception? s_pendingExceptionObject;
+
+ internal static Exception? GetPendingExceptionObject()
+ {
+ Exception? ex = s_pendingExceptionObject;
+ if (ex != null)
+ ex.InternalPreserveStackTrace();
+
+ s_pendingExceptionObject = null;
+ return ex;
+ }
+
+ internal static void SetPendingExceptionObject(Exception? exception)
+ {
+ s_pendingExceptionObject = exception;
+ }
+
[MethodImpl(MethodImplOptions.InternalCall)]
internal static extern IntPtr CreateCustomMarshalerHelper(IntPtr pMD, int paramToken, IntPtr hndManagedType);
diff --git a/src/coreclr/clrdefinitions.cmake b/src/coreclr/clrdefinitions.cmake
index dfc2f617b3b..425d3d84d6a 100644
--- a/src/coreclr/clrdefinitions.cmake
+++ b/src/coreclr/clrdefinitions.cmake
@@ -87,16 +87,11 @@ add_compile_definitions($<$<NOT:$<BOOL:$<TARGET_PROPERTY:CROSSGEN_COMPONENT>>>:F
add_definitions(-DFEATURE_COLLECTIBLE_TYPES)
if(CLR_CMAKE_TARGET_WIN32)
- add_definitions(-DFEATURE_COMWRAPPERS)
add_definitions(-DFEATURE_COMINTEROP)
add_definitions(-DFEATURE_COMINTEROP_APARTMENT_SUPPORT)
add_definitions(-DFEATURE_COMINTEROP_UNMANAGED_ACTIVATION)
endif(CLR_CMAKE_TARGET_WIN32)
-if(CLR_CMAKE_TARGET_OSX OR CLR_CMAKE_TARGET_MACCATALYST OR CLR_CMAKE_TARGET_IOS OR CLR_CMAKE_TARGET_TVOS)
- add_definitions(-DFEATURE_OBJCMARSHAL)
-endif(CLR_CMAKE_TARGET_OSX OR CLR_CMAKE_TARGET_MACCATALYST OR CLR_CMAKE_TARGET_IOS OR CLR_CMAKE_TARGET_TVOS)
-
add_definitions(-DFEATURE_BASICFREEZE)
add_definitions(-DFEATURE_CORECLR)
add_definitions(-DFEATURE_CORESYSTEM)
@@ -162,6 +157,14 @@ else()
add_compile_definitions($<$<BOOL:$<TARGET_PROPERTY:CROSSGEN_COMPONENT>>:FEATURE_PREJIT>)
endif(FEATURE_PREJIT)
+if(FEATURE_COMWRAPPERS)
+ add_compile_definitions(FEATURE_COMWRAPPERS)
+endif(FEATURE_COMWRAPPERS)
+
+if(FEATURE_OBJCMARSHAL)
+ add_compile_definitions(FEATURE_OBJCMARSHAL)
+endif()
+
add_compile_definitions($<$<AND:$<NOT:$<BOOL:$<TARGET_PROPERTY:CROSSGEN_COMPONENT>>>,$<NOT:$<BOOL:$<TARGET_PROPERTY:DAC_COMPONENT>>>>:FEATURE_PROFAPI_ATTACH_DETACH>)
add_definitions(-DFEATURE_READYTORUN)
diff --git a/src/coreclr/clrfeatures.cmake b/src/coreclr/clrfeatures.cmake
index 923c1d92fdf..5a9a161821c 100644
--- a/src/coreclr/clrfeatures.cmake
+++ b/src/coreclr/clrfeatures.cmake
@@ -31,3 +31,11 @@ endif(NOT DEFINED FEATURE_AUTO_TRACE)
if(NOT DEFINED FEATURE_SINGLE_FILE_DIAGNOSTICS)
set(FEATURE_SINGLE_FILE_DIAGNOSTICS 1)
endif(NOT DEFINED FEATURE_SINGLE_FILE_DIAGNOSTICS)
+
+if (CLR_CMAKE_TARGET_WIN32)
+ set(FEATURE_COMWRAPPERS 1)
+endif()
+
+if (CLR_CMAKE_TARGET_OSX OR CLR_CMAKE_TARGET_MACCATALYST OR CLR_CMAKE_TARGET_IOS OR CLR_CMAKE_TARGET_TVOS)
+ set(FEATURE_OBJCMARSHAL 1)
+endif()
diff --git a/src/coreclr/debug/daccess/daccess.cpp b/src/coreclr/debug/daccess/daccess.cpp
index 9152919b18e..9d3e9e587fc 100644
--- a/src/coreclr/debug/daccess/daccess.cpp
+++ b/src/coreclr/debug/daccess/daccess.cpp
@@ -8156,9 +8156,10 @@ void DacHandleWalker::GetRefCountedHandleInfo(
if (pIsPegged)
*pIsPegged = FALSE;
-#ifdef FEATURE_COMINTEROP
+#if defined(FEATURE_COMINTEROP) || defined(FEATURE_COMWRAPPERS) || defined(FEATURE_OBJCMARSHAL)
if (uType == HNDTYPE_REFCOUNTED)
{
+#if defined(FEATURE_COMINTEROP)
// get refcount from the CCW
PTR_ComCallWrapper pWrap = ComCallWrapper::GetWrapperForObject(oref);
if (pWrap != NULL)
@@ -8171,8 +8172,12 @@ void DacHandleWalker::GetRefCountedHandleInfo(
return;
}
+#endif
+#if defined(FEATURE_OBJCMARSHAL)
+ // [TODO] FEATURE_OBJCMARSHAL
+#endif // FEATURE_OBJCMARSHAL
}
-#endif // FEATURE_COMINTEROP
+#endif // FEATURE_COMINTEROP || FEATURE_COMWRAPPERS || FEATURE_OBJCMARSHAL
if (pRefCount)
*pRefCount = 0;
diff --git a/src/coreclr/debug/daccess/dacdbiimpl.cpp b/src/coreclr/debug/daccess/dacdbiimpl.cpp
index b7fb82756ce..27bfef83989 100644
--- a/src/coreclr/debug/daccess/dacdbiimpl.cpp
+++ b/src/coreclr/debug/daccess/dacdbiimpl.cpp
@@ -7556,13 +7556,14 @@ UINT32 DacRefWalker::GetHandleWalkerMask()
if (mHandleMask & CorHandleWeakLong)
result |= (1 << HNDTYPE_WEAK_LONG);
-#ifdef FEATURE_COMINTEROP
+#if defined(FEATURE_COMINTEROP) || defined(FEATURE_COMWRAPPERS) || defined(FEATURE_OBJCMARSHAL)
if ((mHandleMask & CorHandleWeakRefCount) || (mHandleMask & CorHandleStrongRefCount))
result |= (1 << HNDTYPE_REFCOUNTED);
-
+#endif // FEATURE_COMINTEROP || FEATURE_COMWRAPPERS || FEATURE_OBJCMARSHAL
+#if defined(FEATURE_COMINTEROP) || defined(FEATURE_COMWRAPPERS)
if (mHandleMask & CorHandleWeakNativeCom)
result |= (1 << HNDTYPE_WEAK_NATIVE_COM);
-#endif // FEATURE_COMINTEROP
+#endif // FEATURE_COMINTEROP || FEATURE_COMWRAPPERS
if (mHandleMask & CorHandleStrongDependent)
result |= (1 << HNDTYPE_DEPENDENT);
@@ -7729,17 +7730,18 @@ void CALLBACK DacHandleWalker::EnumCallbackDac(PTR_UNCHECKED_OBJECTREF handle, u
data.dwType = (DWORD)CorHandleWeakLong;
break;
-#ifdef FEATURE_COMINTEROP
+#if defined(FEATURE_COMINTEROP) || defined(FEATURE_COMWRAPPERS) || defined(FEATURE_OBJCMARSHAL)
case HNDTYPE_REFCOUNTED:
data.dwType = (DWORD)(data.i64ExtraData ? CorHandleStrongRefCount : CorHandleWeakRefCount);
GetRefCountedHandleInfo((OBJECTREF)*handle, param->Type, &refCnt, NULL, NULL, NULL);
data.i64ExtraData = refCnt;
break;
-
+#endif // FEATURE_COMINTEROP || FEATURE_COMWRAPPERS || FEATURE_OBJCMARSHAL
+#if defined(FEATURE_COMINTEROP) || defined(FEATURE_COMWRAPPERS)
case HNDTYPE_WEAK_NATIVE_COM:
data.dwType = (DWORD)CorHandleWeakNativeCom;
break;
-#endif
+#endif // FEATURE_COMINTEROP || FEATURE_COMWRAPPERS
case HNDTYPE_DEPENDENT:
data.dwType = (DWORD)CorHandleStrongDependent;
diff --git a/src/coreclr/debug/daccess/dacimpl.h b/src/coreclr/debug/daccess/dacimpl.h
index c3347200c3e..165dcb47fb1 100644
--- a/src/coreclr/debug/daccess/dacimpl.h
+++ b/src/coreclr/debug/daccess/dacimpl.h
@@ -1312,7 +1312,7 @@ public:
HRESULT DumpManagedObject(CLRDataEnumMemoryFlags flags, OBJECTREF objRef);
HRESULT DumpManagedExcepObject(CLRDataEnumMemoryFlags flags, OBJECTREF objRef);
HRESULT DumpManagedStackTraceStringObject(CLRDataEnumMemoryFlags flags, STRINGREF orefStackTrace);
-#ifdef FEATURE_COMINTEROP
+#if defined(FEATURE_COMINTEROP) || defined(FEATURE_COMWRAPPERS)
HRESULT DumpStowedExceptionObject(CLRDataEnumMemoryFlags flags, CLRDATA_ADDRESS ccwPtr);
HRESULT EnumMemStowedException(CLRDataEnumMemoryFlags flags);
#endif
diff --git a/src/coreclr/debug/daccess/request.cpp b/src/coreclr/debug/daccess/request.cpp
index 664f540bf62..6600f80ba2a 100644
--- a/src/coreclr/debug/daccess/request.cpp
+++ b/src/coreclr/debug/daccess/request.cpp
@@ -3282,9 +3282,12 @@ HRESULT ClrDataAccess::GetHandleEnum(ISOSHandleEnum **ppHandleEnum)
{
unsigned int types[] = {HNDTYPE_WEAK_SHORT, HNDTYPE_WEAK_LONG, HNDTYPE_STRONG, HNDTYPE_PINNED, HNDTYPE_VARIABLE, HNDTYPE_DEPENDENT,
HNDTYPE_ASYNCPINNED, HNDTYPE_SIZEDREF,
-#ifdef FEATURE_COMINTEROP
- HNDTYPE_REFCOUNTED, HNDTYPE_WEAK_NATIVE_COM
-#endif
+#if defined(FEATURE_COMINTEROP) || defined(FEATURE_COMWRAPPERS) || defined(FEATURE_OBJCMARSHAL)
+ HNDTYPE_REFCOUNTED,
+#endif // FEATURE_COMINTEROP || FEATURE_COMWRAPPERS || FEATURE_OBJCMARSHAL
+#if defined(FEATURE_COMINTEROP) || defined(FEATURE_COMWRAPPERS)
+ HNDTYPE_WEAK_NATIVE_COM
+#endif // FEATURE_COMINTEROP
};
return GetHandleEnumForTypes(types, _countof(types), ppHandleEnum);
@@ -3320,9 +3323,12 @@ HRESULT ClrDataAccess::GetHandleEnumForGC(unsigned int gen, ISOSHandleEnum **ppH
unsigned int types[] = {HNDTYPE_WEAK_SHORT, HNDTYPE_WEAK_LONG, HNDTYPE_STRONG, HNDTYPE_PINNED, HNDTYPE_VARIABLE, HNDTYPE_DEPENDENT,
HNDTYPE_ASYNCPINNED, HNDTYPE_SIZEDREF,
-#ifdef FEATURE_COMINTEROP
- HNDTYPE_REFCOUNTED, HNDTYPE_WEAK_NATIVE_COM
-#endif
+#if defined(FEATURE_COMINTEROP) || defined(FEATURE_COMWRAPPERS) || defined(FEATURE_OBJCMARSHAL)
+ HNDTYPE_REFCOUNTED,
+#endif // FEATURE_COMINTEROP || FEATURE_COMWRAPPERS || FEATURE_OBJCMARSHAL
+#if defined(FEATURE_COMINTEROP) || defined(FEATURE_COMWRAPPERS)
+ HNDTYPE_WEAK_NATIVE_COM
+#endif // FEATURE_COMINTEROP || FEATURE_COMWRAPPERS
};
DacHandleWalker *walker = new DacHandleWalker();
@@ -4849,7 +4855,7 @@ HRESULT ClrDataAccess::GetObjectComWrappersData(CLRDATA_ADDRESS objAddr, CLRDATA
{
comWrappers.Push(TO_CDADDR(iter->Value()));
++iter;
-
+
}
}
@@ -4885,7 +4891,7 @@ HRESULT ClrDataAccess::GetObjectComWrappersData(CLRDATA_ADDRESS objAddr, CLRDATA
return E_NOTIMPL;
#endif // FEATURE_COMWRAPPERS
}
-
+
HRESULT ClrDataAccess::IsComWrappersCCW(CLRDATA_ADDRESS ccw, BOOL *isComWrappersCCW)
{
#ifdef FEATURE_COMWRAPPERS
@@ -4895,12 +4901,12 @@ HRESULT ClrDataAccess::IsComWrappersCCW(CLRDATA_ADDRESS ccw, BOOL *isComWrappers
}
SOSDacEnter();
-
+
if (isComWrappersCCW != NULL)
{
TADDR managedObjectWrapperPtr = DACGetManagedObjectWrapperFromCCW(ccw);
*isComWrappersCCW = managedObjectWrapperPtr != NULL;
- hr = *isComWrappersCCW ? S_OK : S_FALSE;
+ hr = *isComWrappersCCW ? S_OK : S_FALSE;
}
SOSDacLeave();
@@ -4919,12 +4925,12 @@ HRESULT ClrDataAccess::GetComWrappersCCWData(CLRDATA_ADDRESS ccw, CLRDATA_ADDRES
}
SOSDacEnter();
-
+
TADDR managedObjectWrapperPtr = DACGetManagedObjectWrapperFromCCW(ccw);
if (managedObjectWrapperPtr != NULL)
{
PTR_ManagedObjectWrapper pMOW(managedObjectWrapperPtr);
-
+
if (managedObject != NULL)
{
OBJECTREF managedObjectRef;
@@ -4965,7 +4971,7 @@ HRESULT ClrDataAccess::IsComWrappersRCW(CLRDATA_ADDRESS rcw, BOOL *isComWrappers
}
SOSDacEnter();
-
+
if (isComWrappersRCW != NULL)
{
PTR_ExternalObjectContext pRCW(TO_TADDR(rcw));
@@ -4988,7 +4994,7 @@ HRESULT ClrDataAccess::IsComWrappersRCW(CLRDATA_ADDRESS rcw, BOOL *isComWrappers
PTR_InteropSyncBlockInfo pInfo = NULL;
if (stillValid)
- {
+ {
pInfo = pSyncBlk->GetInteropInfoNoCreate();
if(pInfo == NULL)
{
@@ -5002,11 +5008,11 @@ HRESULT ClrDataAccess::IsComWrappersRCW(CLRDATA_ADDRESS rcw, BOOL *isComWrappers
}
*isComWrappersRCW = stillValid;
- hr = *isComWrappersRCW ? S_OK : S_FALSE;
+ hr = *isComWrappersRCW ? S_OK : S_FALSE;
}
SOSDacLeave();
- return hr;
+ return hr;
#else // FEATURE_COMWRAPPERS
return E_NOTIMPL;
#endif // FEATURE_COMWRAPPERS
@@ -5021,7 +5027,7 @@ HRESULT ClrDataAccess::GetComWrappersRCWData(CLRDATA_ADDRESS rcw, CLRDATA_ADDRES
}
SOSDacEnter();
-
+
PTR_ExternalObjectContext pEOC(TO_TADDR(rcw));
if (identity != NULL)
{
diff --git a/src/coreclr/dlls/mscoree/unixinterface.cpp b/src/coreclr/dlls/mscoree/unixinterface.cpp
index 73ed20afeb9..c6ce9930246 100644
--- a/src/coreclr/dlls/mscoree/unixinterface.cpp
+++ b/src/coreclr/dlls/mscoree/unixinterface.cpp
@@ -223,7 +223,7 @@ int coreclr_initialize(
if (pinvokeOverride != nullptr)
{
- PInvokeOverride::SetPInvokeOverride(pinvokeOverride);
+ PInvokeOverride::SetPInvokeOverride(pinvokeOverride, PInvokeOverride::Source::RuntimeConfiguration);
}
ReleaseHolder<ICLRRuntimeHost4> host;
diff --git a/src/coreclr/gc/env/gcenv.ee.h b/src/coreclr/gc/env/gcenv.ee.h
index 8d4946b84a2..7ecbfdda4f2 100644
--- a/src/coreclr/gc/env/gcenv.ee.h
+++ b/src/coreclr/gc/env/gcenv.ee.h
@@ -26,13 +26,13 @@ public:
// start of GC call back - single threaded
static void GcStartWork(int condemned, int max_gen);
+ // Called before scanning roots
+ static void BeforeGcScanRoots(int condemned, bool is_bgc, bool is_concurrent);
+
//EE can perform post stack scanning action, while the
// user threads are still suspended
static void AfterGcScanRoots(int condemned, int max_gen, ScanContext* sc);
- // Called before BGC starts sweeping, the heap is walkable
- static void GcBeforeBGCSweepWork();
-
// post-gc callback.
static void GcDone(int condemned);
diff --git a/src/coreclr/gc/gc.cpp b/src/coreclr/gc/gc.cpp
index 3f47e41dd8c..fb8efb58fb2 100644
--- a/src/coreclr/gc/gc.cpp
+++ b/src/coreclr/gc/gc.cpp
@@ -23683,6 +23683,7 @@ void gc_heap::mark_phase (int condemned_gen_number, BOOL mark_only_p)
grow_mark_list_piece();
#endif //USE_REGIONS
+ GCToEEInterface::BeforeGcScanRoots(condemned_gen_number, /* is_bgc */ false, /* is_concurrent */ false);
num_sizedrefs = GCToEEInterface::GetTotalNumSizedRefHandles();
#ifdef MULTIPLE_HEAPS
@@ -31731,15 +31732,14 @@ void gc_heap::background_mark_phase ()
bgc_tuning::record_bgc_sweep_start();
#endif //BGC_SERVO_TUNING
+ GCToEEInterface::BeforeGcScanRoots(max_generation, /* is_bgc */ true, /* is_concurrent */ false);
+
#ifdef MULTIPLE_HEAPS
dprintf(3, ("Joining BGC threads after absorb"));
bgc_t_join.restart();
#endif //MULTIPLE_HEAPS
}
- // give VM a chance to do work
- GCToEEInterface::GcBeforeBGCSweepWork();
-
//reset the flag, indicating that the EE no longer expect concurrent
//marking
sc.concurrent = FALSE;
diff --git a/src/coreclr/gc/gcenv.ee.standalone.inl b/src/coreclr/gc/gcenv.ee.standalone.inl
index a7cdfec2777..c7e4dc4c98b 100644
--- a/src/coreclr/gc/gcenv.ee.standalone.inl
+++ b/src/coreclr/gc/gcenv.ee.standalone.inl
@@ -42,16 +42,16 @@ inline void GCToEEInterface::GcStartWork(int condemned, int max_gen)
g_theGCToCLR->GcStartWork(condemned, max_gen);
}
-inline void GCToEEInterface::AfterGcScanRoots(int condemned, int max_gen, ScanContext* sc)
+inline void GCToEEInterface::BeforeGcScanRoots(int condemned, bool is_bgc, bool is_concurrent)
{
assert(g_theGCToCLR != nullptr);
- g_theGCToCLR->AfterGcScanRoots(condemned, max_gen, sc);
+ g_theGCToCLR->BeforeGcScanRoots(condemned, is_bgc, is_concurrent);
}
-inline void GCToEEInterface::GcBeforeBGCSweepWork()
+inline void GCToEEInterface::AfterGcScanRoots(int condemned, int max_gen, ScanContext* sc)
{
assert(g_theGCToCLR != nullptr);
- g_theGCToCLR->GcBeforeBGCSweepWork();
+ g_theGCToCLR->AfterGcScanRoots(condemned, max_gen, sc);
}
inline void GCToEEInterface::GcDone(int condemned)
diff --git a/src/coreclr/gc/gcinterface.ee.h b/src/coreclr/gc/gcinterface.ee.h
index 152ccda167c..4b048271c0e 100644
--- a/src/coreclr/gc/gcinterface.ee.h
+++ b/src/coreclr/gc/gcinterface.ee.h
@@ -197,16 +197,16 @@ public:
virtual
void GcStartWork(int condemned, int max_gen) = 0;
+ // Callback from the GC informing the EE that the scanning of roots is about
+ // to begin.
+ virtual
+ void BeforeGcScanRoots(int condemned, bool is_bgc, bool is_concurrent) = 0;
+
// Callback from the GC informing the EE that it has completed the managed stack
// scan. User threads are still suspended at this point.
virtual
void AfterGcScanRoots(int condemned, int max_gen, ScanContext* sc) = 0;
- // Callback from the GC informing the EE that the background sweep phase of a BGC is
- // about to begin.
- virtual
- void GcBeforeBGCSweepWork() = 0;
-
// Callback from the GC informing the EE that a GC has completed.
virtual
void GcDone(int condemned) = 0;
diff --git a/src/coreclr/gc/objecthandle.cpp b/src/coreclr/gc/objecthandle.cpp
index 52c31b49c5a..208689bebd2 100644
--- a/src/coreclr/gc/objecthandle.cpp
+++ b/src/coreclr/gc/objecthandle.cpp
@@ -66,7 +66,7 @@ void CALLBACK VariableTraceDispatcher(_UNCHECKED_OBJECTREF *pObjRef, uintptr_t *
}
}
-#if defined(FEATURE_COMINTEROP) || defined(FEATURE_REDHAWK)
+#if defined(FEATURE_COMINTEROP) || defined(FEATURE_COMWRAPPERS) || defined(FEATURE_OBJCMARSHAL) || defined(FEATURE_REDHAWK)
/*
* Scan callback for tracing ref-counted handles.
*
@@ -102,7 +102,7 @@ void CALLBACK PromoteRefCounted(_UNCHECKED_OBJECTREF *pObjRef, uintptr_t *pExtra
// Assert this object wasn't relocated since we are passing a temporary object's address.
_ASSERTE(pOldObj == pObj);
}
-#endif // FEATURE_COMINTEROP || FEATURE_REDHAWK
+#endif // FEATURE_COMINTEROP || FEATURE_COMWRAPPERS || FEATURE_OBJCMARSHAL || FEATURE_REDHAWK
// Only used by profiling/ETW.
@@ -461,7 +461,7 @@ void CALLBACK ScanPointerForProfilerAndETW(_UNCHECKED_OBJECTREF *pObjRef, uintpt
#endif
break;
-#if defined(FEATURE_COMINTEROP) && !defined(FEATURE_REDHAWK)
+#if (defined(FEATURE_COMINTEROP) || defined(FEATURE_COMWRAPPERS) || defined(FEATURE_OBJCMARSHAL)) && !defined(FEATURE_REDHAWK)
case HNDTYPE_REFCOUNTED:
rootFlags |= kEtwGCRootFlagsRefCounted;
if (*pRef != NULL)
@@ -470,7 +470,7 @@ void CALLBACK ScanPointerForProfilerAndETW(_UNCHECKED_OBJECTREF *pObjRef, uintpt
rootFlags |= kEtwGCRootFlagsWeakRef;
}
break;
-#endif // FEATURE_COMINTEROP || FEATURE_REDHAWK
+#endif // (FEATURE_COMINTEROP || FEATURE_COMWRAPPERS || FEATURE_OBJCMARSHAL) && !FEATURE_REDHAWK
}
_UNCHECKED_OBJECTREF pSec = NULL;
@@ -1065,7 +1065,8 @@ void Ref_TraceNormalRoots(uint32_t condemned, uint32_t maxgen, ScanContext* sc,
// promote objects pointed to by variable handles whose dynamic type is VHT_STRONG
TraceVariableHandles(PromoteObject, uintptr_t(sc), uintptr_t(fn), VHT_STRONG, condemned, maxgen, flags);
-#if defined(FEATURE_COMINTEROP) || defined(FEATURE_REDHAWK)
+
+#if defined(FEATURE_COMINTEROP) || defined(FEATURE_COMWRAPPERS) || defined(FEATURE_OBJCMARSHAL) || defined(FEATURE_REDHAWK)
// don't scan ref-counted handles during concurrent phase as the clean-up of CCWs can race with AD unload and cause AV's
if (!sc->concurrent)
{
@@ -1084,13 +1085,13 @@ void Ref_TraceNormalRoots(uint32_t condemned, uint32_t maxgen, ScanContext* sc,
walk = walk->pNext;
}
}
-#endif // FEATURE_COMINTEROP || FEATURE_REDHAWK
+#endif // FEATURE_COMINTEROP || FEATURE_COMWRAPPERS || FEATURE_OBJCMARSHAL || FEATURE_REDHAWK
}
void Ref_TraceRefCountHandles(HANDLESCANPROC callback, uintptr_t lParam1, uintptr_t lParam2)
{
-#ifdef FEATURE_COMINTEROP
+#if defined(FEATURE_COMINTEROP) || defined(FEATURE_COMWRAPPERS) || defined(FEATURE_OBJCMARSHAL)
int max_slots = getNumberOfSlots();
uint32_t handleType = HNDTYPE_REFCOUNTED;
@@ -1115,7 +1116,7 @@ void Ref_TraceRefCountHandles(HANDLESCANPROC callback, uintptr_t lParam1, uintpt
UNREFERENCED_PARAMETER(callback);
UNREFERENCED_PARAMETER(lParam1);
UNREFERENCED_PARAMETER(lParam2);
-#endif // FEATURE_COMINTEROP
+#endif // FEATURE_COMINTEROP || FEATURE_COMWRAPPERS || FEATURE_OBJCMARSHAL
}
@@ -1131,9 +1132,9 @@ void Ref_CheckReachable(uint32_t condemned, uint32_t maxgen, uintptr_t lp1)
uint32_t types[] =
{
HNDTYPE_WEAK_LONG,
-#if defined(FEATURE_COMINTEROP) || defined(FEATURE_REDHAWK)
+#if defined(FEATURE_COMINTEROP) || defined(FEATURE_COMWRAPPERS) || defined(FEATURE_OBJCMARSHAL) || defined(FEATURE_REDHAWK)
HNDTYPE_REFCOUNTED,
-#endif // FEATURE_COMINTEROP || FEATURE_REDHAWK
+#endif // FEATURE_COMINTEROP || FEATURE_COMWRAPPERS || FEATURE_OBJCMARSHAL || FEATURE_REDHAWK
};
// check objects pointed to by short weak handles
@@ -1434,12 +1435,12 @@ void Ref_UpdatePointers(uint32_t condemned, uint32_t maxgen, ScanContext* sc, Re
HNDTYPE_WEAK_SHORT,
HNDTYPE_WEAK_LONG,
HNDTYPE_STRONG,
-#if defined(FEATURE_COMINTEROP) || defined(FEATURE_REDHAWK)
+#if defined(FEATURE_COMINTEROP) || defined(FEATURE_COMWRAPPERS) || defined(FEATURE_OBJCMARSHAL) || defined(FEATURE_REDHAWK)
HNDTYPE_REFCOUNTED,
-#endif // FEATURE_COMINTEROP || FEATURE_REDHAWK
-#ifdef FEATURE_COMINTEROP
+#endif // FEATURE_COMINTEROP || FEATURE_COMWRAPPERS || FEATURE_OBJCMARSHAL || FEATURE_REDHAWK
+#if defined(FEATURE_COMINTEROP) || defined(FEATURE_COMWRAPPERS)
HNDTYPE_WEAK_NATIVE_COM,
-#endif // FEATURE_COMINTEROP
+#endif // FEATURE_COMINTEROP || FEATURE_COMWRAPPERS
HNDTYPE_SIZEDREF,
};
@@ -1480,12 +1481,12 @@ void Ref_ScanHandlesForProfilerAndETW(uint32_t maxgen, uintptr_t lp1, handle_sca
HNDTYPE_WEAK_SHORT,
HNDTYPE_WEAK_LONG,
HNDTYPE_STRONG,
-#if defined(FEATURE_COMINTEROP) || defined(FEATURE_REDHAWK)
+#if defined(FEATURE_COMINTEROP) || defined(FEATURE_COMWRAPPERS) || defined(FEATURE_OBJCMARSHAL) || defined(FEATURE_REDHAWK)
HNDTYPE_REFCOUNTED,
-#endif // FEATURE_COMINTEROP || FEATURE_REDHAWK
-#ifdef FEATURE_COMINTEROP
+#endif // FEATURE_COMINTEROP || FEATURE_COMWRAPPERS || FEATURE_OBJCMARSHAL || FEATURE_REDHAWK
+#if defined(FEATURE_COMINTEROP) || defined(FEATURE_COMWRAPPERS)
HNDTYPE_WEAK_NATIVE_COM,
-#endif // FEATURE_COMINTEROP
+#endif // FEATURE_COMINTEROP || FEATURE_COMWRAPPERS
HNDTYPE_PINNED,
// HNDTYPE_VARIABLE,
HNDTYPE_ASYNCPINNED,
@@ -1549,9 +1550,9 @@ void Ref_ScanPointers(uint32_t condemned, uint32_t maxgen, ScanContext* sc, Ref_
HNDTYPE_WEAK_SHORT,
HNDTYPE_WEAK_LONG,
HNDTYPE_STRONG,
-#if defined(FEATURE_COMINTEROP) || defined(FEATURE_REDHAWK)
+#if defined(FEATURE_COMINTEROP) || defined(FEATURE_COMWRAPPERS) || defined(FEATURE_OBJCMARSHAL) || defined(FEATURE_REDHAWK)
HNDTYPE_REFCOUNTED,
-#endif // FEATURE_COMINTEROP || FEATURE_REDHAWK
+#endif // FEATURE_COMINTEROP || FEATURE_COMWRAPPERS || FEATURE_OBJCMARSHAL || FEATURE_REDHAWK
HNDTYPE_PINNED,
HNDTYPE_ASYNCPINNED,
HNDTYPE_SIZEDREF,
@@ -1626,12 +1627,12 @@ void Ref_AgeHandles(uint32_t condemned, uint32_t maxgen, uintptr_t lp1)
HNDTYPE_PINNED,
HNDTYPE_VARIABLE,
-#if defined(FEATURE_COMINTEROP) || defined(FEATURE_REDHAWK)
+#if defined(FEATURE_COMINTEROP) || defined(FEATURE_COMWRAPPERS) || defined(FEATURE_OBJCMARSHAL) || defined(FEATURE_REDHAWK)
HNDTYPE_REFCOUNTED,
-#endif // FEATURE_COMINTEROP || FEATURE_REDHAWK
-#ifdef FEATURE_COMINTEROP
+#endif // FEATURE_COMINTEROP || FEATURE_COMWRAPPERS || FEATURE_OBJCMARSHAL || FEATURE_REDHAWK
+#if defined(FEATURE_COMINTEROP) || defined(FEATURE_COMWRAPPERS)
HNDTYPE_WEAK_NATIVE_COM,
-#endif // FEATURE_COMINTEROP
+#endif // FEATURE_COMINTEROP || FEATURE_COMWRAPPERS
HNDTYPE_ASYNCPINNED,
HNDTYPE_SIZEDREF,
};
@@ -1669,12 +1670,12 @@ void Ref_RejuvenateHandles(uint32_t condemned, uint32_t maxgen, uintptr_t lp1)
HNDTYPE_PINNED,
HNDTYPE_VARIABLE,
-#if defined(FEATURE_COMINTEROP) || defined(FEATURE_REDHAWK)
+#if defined(FEATURE_COMINTEROP) || defined(FEATURE_COMWRAPPERS) || defined(FEATURE_OBJCMARSHAL) || defined(FEATURE_REDHAWK)
HNDTYPE_REFCOUNTED,
-#endif // FEATURE_COMINTEROP || FEATURE_REDHAWK
-#ifdef FEATURE_COMINTEROP
+#endif // FEATURE_COMINTEROP || FEATURE_COMWRAPPERS || FEATURE_OBJCMARSHAL || FEATURE_REDHAWK
+#if defined(FEATURE_COMINTEROP) || defined(FEATURE_COMWRAPPERS)
HNDTYPE_WEAK_NATIVE_COM,
-#endif // FEATURE_COMINTEROP
+#endif // FEATURE_COMINTEROP || FEATURE_COMWRAPPERS
HNDTYPE_ASYNCPINNED,
HNDTYPE_SIZEDREF,
};
@@ -1711,12 +1712,12 @@ void Ref_VerifyHandleTable(uint32_t condemned, uint32_t maxgen, ScanContext* sc)
HNDTYPE_PINNED,
HNDTYPE_VARIABLE,
-#if defined(FEATURE_COMINTEROP) || defined(FEATURE_REDHAWK)
+#if defined(FEATURE_COMINTEROP) || defined(FEATURE_COMWRAPPERS) || defined(FEATURE_OBJCMARSHAL) || defined(FEATURE_REDHAWK)
HNDTYPE_REFCOUNTED,
-#endif // FEATURE_COMINTEROP || FEATURE_REDHAWK
-#ifdef FEATURE_COMINTEROP
+#endif // FEATURE_COMINTEROP || FEATURE_COMWRAPPERS || FEATURE_OBJCMARSHAL || FEATURE_REDHAWK
+#if defined(FEATURE_COMINTEROP) || defined(FEATURE_COMWRAPPERS)
HNDTYPE_WEAK_NATIVE_COM,
-#endif // FEATURE_COMINTEROP
+#endif // FEATURE_COMINTEROP || FEATURE_COMWRAPPERS
HNDTYPE_ASYNCPINNED,
HNDTYPE_SIZEDREF,
HNDTYPE_DEPENDENT,
diff --git a/src/coreclr/gc/sample/gcenv.ee.cpp b/src/coreclr/gc/sample/gcenv.ee.cpp
index b689b50de33..060a5556e21 100644
--- a/src/coreclr/gc/sample/gcenv.ee.cpp
+++ b/src/coreclr/gc/sample/gcenv.ee.cpp
@@ -147,11 +147,11 @@ void GCToEEInterface::GcStartWork(int condemned, int max_gen)
{
}
-void GCToEEInterface::AfterGcScanRoots(int condemned, int max_gen, ScanContext* sc)
+void GCToEEInterface::BeforeGcScanRoots(int condemned, bool is_bgc, bool is_concurrent)
{
}
-void GCToEEInterface::GcBeforeBGCSweepWork()
+void GCToEEInterface::AfterGcScanRoots(int condemned, int max_gen, ScanContext* sc)
{
}
diff --git a/src/coreclr/inc/pinvokeoverride.h b/src/coreclr/inc/pinvokeoverride.h
index 525f976b3de..3cec8c5b498 100644
--- a/src/coreclr/inc/pinvokeoverride.h
+++ b/src/coreclr/inc/pinvokeoverride.h
@@ -15,8 +15,16 @@
class PInvokeOverride
{
public:
- static void SetPInvokeOverride(PInvokeOverrideFn* overrideImpl);
- static const void* GetMethodImpl(const char* libraryName, const char* entrypointName);
+ // Override source. This represents the priority order in which overrides will be called.
+ enum class Source
+ {
+ RuntimeConfiguration,
+ ObjectiveCInterop,
+ Last = ObjectiveCInterop,
+ };
+
+ static void SetPInvokeOverride(_In_ PInvokeOverrideFn* overrideImpl, _In_ Source source);
+ static const void* GetMethodImpl(_In_z_ const char* libraryName, _In_z_ const char* entrypointName);
};
#endif // _PINVOKEOVERRIDE_H_
diff --git a/src/coreclr/inc/utilcode.h b/src/coreclr/inc/utilcode.h
index 563fff829c9..a47034ee2e0 100644
--- a/src/coreclr/inc/utilcode.h
+++ b/src/coreclr/inc/utilcode.h
@@ -4715,12 +4715,12 @@ void* FindLocalizedFile(_In_z_ LPCWSTR wzResourceDllName, LocalizedFileHandler l
namespace Clr { namespace Util
{
-#ifdef FEATURE_COMINTEROP
+#ifdef HOST_WINDOWS
namespace Com
{
HRESULT FindInprocServer32UsingCLSID(REFCLSID rclsid, SString & ssInprocServer32Name);
}
-#endif // FEATURE_COMINTEROP
+#endif // HOST_WINDOWS
}}
diff --git a/src/coreclr/interop/CMakeLists.txt b/src/coreclr/interop/CMakeLists.txt
index 1642f55a04d..94207158781 100644
--- a/src/coreclr/interop/CMakeLists.txt
+++ b/src/coreclr/interop/CMakeLists.txt
@@ -19,14 +19,14 @@ set(INTEROP_HEADERS
${INTEROP_COMMON_HEADERS}
)
-if (WIN32)
+if (FEATURE_COMWRAPPERS)
list(APPEND INTEROP_SOURCES
${INTEROP_HEADERS}
comwrappers.cpp
comwrappers.hpp
trackerobjectmanager.cpp
referencetrackertypes.hpp)
-endif(WIN32)
+endif(FEATURE_COMWRAPPERS)
convert_to_absolute_path(INTEROP_SOURCES ${INTEROP_SOURCES})
diff --git a/src/coreclr/pal/inc/pal.h b/src/coreclr/pal/inc/pal.h
index 2f42a2c4fda..25f16e4a42e 100644
--- a/src/coreclr/pal/inc/pal.h
+++ b/src/coreclr/pal/inc/pal.h
@@ -4337,6 +4337,8 @@ private:
ExceptionPointers.ContextRecord = ex.ExceptionPointers.ContextRecord;
TargetFrameSp = ex.TargetFrameSp;
RecordsOnStack = ex.RecordsOnStack;
+ ManagedToNativeExceptionCallback = ex.ManagedToNativeExceptionCallback;
+ ManagedToNativeExceptionCallbackContext = ex.ManagedToNativeExceptionCallbackContext;
ex.Clear();
}
@@ -4357,12 +4359,17 @@ public:
SIZE_T TargetFrameSp;
bool RecordsOnStack;
+ void(*ManagedToNativeExceptionCallback)(void* context);
+ void* ManagedToNativeExceptionCallbackContext;
+
PAL_SEHException(EXCEPTION_RECORD *pExceptionRecord, CONTEXT *pContextRecord, bool onStack = false)
{
ExceptionPointers.ExceptionRecord = pExceptionRecord;
ExceptionPointers.ContextRecord = pContextRecord;
TargetFrameSp = NoTargetFrameSp;
RecordsOnStack = onStack;
+ ManagedToNativeExceptionCallback = NULL;
+ ManagedToNativeExceptionCallbackContext = NULL;
}
PAL_SEHException()
@@ -4399,6 +4406,8 @@ public:
ExceptionPointers.ContextRecord = NULL;
TargetFrameSp = NoTargetFrameSp;
RecordsOnStack = false;
+ ManagedToNativeExceptionCallback = NULL;
+ ManagedToNativeExceptionCallbackContext = NULL;
}
CONTEXT* GetContextRecord()
@@ -4420,6 +4429,19 @@ public:
{
TargetFrameSp = NoTargetFrameSp;
}
+
+ bool HasPropagateExceptionCallback()
+ {
+ return ManagedToNativeExceptionCallback != NULL;
+ }
+
+ void SetPropagateExceptionCallback(
+ void(*callback)(void*),
+ void* context)
+ {
+ ManagedToNativeExceptionCallback = callback;
+ ManagedToNativeExceptionCallbackContext = context;
+ }
};
typedef BOOL (*PHARDWARE_EXCEPTION_HANDLER)(PAL_SEHException* ex);
diff --git a/src/coreclr/utilcode/util.cpp b/src/coreclr/utilcode/util.cpp
index 1a90a6e7fcf..9f19ec53a02 100644
--- a/src/coreclr/utilcode/util.cpp
+++ b/src/coreclr/utilcode/util.cpp
@@ -237,7 +237,7 @@ namespace
StackSString ssDllName;
if ((wszDllPath == nullptr) || (wszDllPath[0] == W('\0')) || fIsDllPathPrefix)
{
-#ifndef TARGET_UNIX
+#ifdef HOST_WINDOWS
IfFailRet(Clr::Util::Com::FindInprocServer32UsingCLSID(rclsid, ssDllName));
EX_TRY
@@ -256,9 +256,9 @@ namespace
IfFailRet(hr);
wszDllPath = ssDllName.GetUnicode();
-#else // !TARGET_UNIX
+#else // HOST_WINDOWS
return E_FAIL;
-#endif // !TARGET_UNIX
+#endif // HOST_WINDOWS
}
_ASSERTE(wszDllPath != nullptr);
diff --git a/src/coreclr/vm/CMakeLists.txt b/src/coreclr/vm/CMakeLists.txt
index 20c16e37f5a..d38633d6951 100644
--- a/src/coreclr/vm/CMakeLists.txt
+++ b/src/coreclr/vm/CMakeLists.txt
@@ -552,77 +552,111 @@ if(FEATURE_STANDALONE_GC)
)
endif(FEATURE_STANDALONE_GC)
-if(CLR_CMAKE_TARGET_WIN32)
-
-set(VM_SOURCES_DAC_AND_WKS_WIN32
- amsi.cpp
- clrtocomcall.cpp
-)
-
-set(VM_HEADERS_DAC_AND_WKS_WIN32
- amsi.h
- clrtocomcall.h
-)
-
+#
+# Targeted interop scenarios
+#
+# Shared assets
list(APPEND VM_SOURCES_WKS
- ${VM_SOURCES_DAC_AND_WKS_WIN32}
- # These should not be included for Linux
- classcompat.cpp
- comcache.cpp
- comcallablewrapper.cpp
- comconnectionpoints.cpp
- cominterfacemarshaler.cpp
- commtmemberinfomap.cpp
- comtoclrcall.cpp
- dispatchinfo.cpp
- dispparammarshaler.cpp
- dwreport.cpp
- eventreporter.cpp
- interoplibinterface.cpp
- mngstdinterfaces.cpp
- notifyexternals.cpp
- olecontexthelpers.cpp
- rcwrefcache.cpp
- rtlfunctions.cpp
- runtimecallablewrapper.cpp
- stacksampler.cpp
- stdinterfaces.cpp
- stdinterfaces_wrapper.cpp
+ interoplibinterface_shared.cpp
)
-
list(APPEND VM_HEADERS_WKS
- ${VM_HEADERS_DAC_AND_WKS_WIN32}
- # These should not be included for Linux
- classcompat.h
- comcache.h
- comcallablewrapper.h
- comconnectionpoints.h
- cominterfacemarshaler.h
- commtmemberinfomap.h
- comtoclrcall.h
- dispatchinfo.h
- dispparammarshaler.h
- dwreport.h
- eventreporter.h
interoplibinterface.h
- mngstdinterfaces.h
- notifyexternals.h
- olecontexthelpers.h
- rcwrefcache.h
- rtlfunctions.h
- runtimecallablewrapper.h
- stacksampler.h
- stdinterfaces.h
- stdinterfaces_internal.h
)
+if(FEATURE_COMWRAPPERS OR FEATURE_OBJCMARSHAL)
+ if (FEATURE_COMWRAPPERS)
+ list(APPEND VM_SOURCES_WKS
+ interoplibinterface.cpp
+ )
+ endif (FEATURE_COMWRAPPERS)
-list(APPEND VM_SOURCES_DAC
- ${VM_SOURCES_DAC_AND_WKS_WIN32}
-)
+ if (FEATURE_OBJCMARSHAL)
+ list(APPEND VM_SOURCES_WKS
+ interoplibinterface_objc.cpp
+ )
+ endif (FEATURE_OBJCMARSHAL)
+endif(FEATURE_COMWRAPPERS OR FEATURE_OBJCMARSHAL)
-list(APPEND VM_HEADERS_DAC
- ${VM_HEADERS_DAC_AND_WKS_WIN32}
-)
+if(CLR_CMAKE_TARGET_WIN32)
+
+ set(VM_SOURCES_DAC_AND_WKS_WIN32
+ amsi.cpp
+ )
+
+ set(VM_HEADERS_DAC_AND_WKS_WIN32
+ amsi.h
+ )
+
+ # COM interop scenarios
+ list(APPEND VM_SOURCES_DAC_AND_WKS_WIN32
+ clrtocomcall.cpp
+ )
+ list(APPEND VM_HEADERS_DAC_AND_WKS_WIN32
+ clrtocomcall.h
+ )
+
+ list(APPEND VM_SOURCES_WKS
+ ${VM_SOURCES_DAC_AND_WKS_WIN32}
+ # These should not be included for Linux
+ dwreport.cpp
+ eventreporter.cpp
+ rtlfunctions.cpp
+ stacksampler.cpp
+ )
+
+ list(APPEND VM_HEADERS_WKS
+ ${VM_HEADERS_DAC_AND_WKS_WIN32}
+ # These should not be included for Linux
+ dwreport.h
+ eventreporter.h
+ rtlfunctions.h
+ stacksampler.h
+ )
+
+ # COM interop scenarios
+ list(APPEND VM_SOURCES_WKS
+ classcompat.cpp
+ comcache.cpp
+ comcallablewrapper.cpp
+ comconnectionpoints.cpp
+ cominterfacemarshaler.cpp
+ commtmemberinfomap.cpp
+ comtoclrcall.cpp
+ dispatchinfo.cpp
+ dispparammarshaler.cpp
+ mngstdinterfaces.cpp
+ notifyexternals.cpp
+ olecontexthelpers.cpp
+ rcwrefcache.cpp
+ runtimecallablewrapper.cpp
+ stdinterfaces.cpp
+ stdinterfaces_wrapper.cpp
+ )
+ list(APPEND VM_HEADERS_WKS
+ classcompat.h
+ comcache.h
+ comcallablewrapper.h
+ comconnectionpoints.h
+ cominterfacemarshaler.h
+ commtmemberinfomap.h
+ comtoclrcall.h
+ dispatchinfo.h
+ dispparammarshaler.h
+ mngstdinterfaces.h
+ notifyexternals.h
+ olecontexthelpers.h
+ rcwrefcache.h
+ runtimecallablewrapper.h
+ stdinterfaces.h
+ stdinterfaces_internal.h
+ )
+
+ list(APPEND VM_SOURCES_DAC
+ ${VM_SOURCES_DAC_AND_WKS_WIN32}
+ )
+
+ list(APPEND VM_HEADERS_DAC
+ ${VM_HEADERS_DAC_AND_WKS_WIN32}
+ )
endif(CLR_CMAKE_TARGET_WIN32)
diff --git a/src/coreclr/vm/amd64/unixasmhelpers.S b/src/coreclr/vm/amd64/unixasmhelpers.S
index 0399fc61dc8..5d9cd711df7 100644
--- a/src/coreclr/vm/amd64/unixasmhelpers.S
+++ b/src/coreclr/vm/amd64/unixasmhelpers.S
@@ -5,7 +5,6 @@
#include "unixasmmacros.inc"
#include "asmconstants.h"
-
//////////////////////////////////////////////////////////////////////////
//
// PrecodeFixupThunk
diff --git a/src/coreclr/vm/corelib.cpp b/src/coreclr/vm/corelib.cpp
index 8987aeaffca..f400d5ad0f9 100644
--- a/src/coreclr/vm/corelib.cpp
+++ b/src/coreclr/vm/corelib.cpp
@@ -65,9 +65,10 @@
#include "variant.h"
#include "oavariant.h"
#include "mngstdinterfaces.h"
-#include "interoplibinterface.h"
#endif // FEATURE_COMINTEROP
+#include "interoplibinterface.h"
+
#include "stubhelpers.h"
#include "ilmarshalers.h"
diff --git a/src/coreclr/vm/corelib.h b/src/coreclr/vm/corelib.h
index 86b99fcb832..f9e0de12410 100644
--- a/src/coreclr/vm/corelib.h
+++ b/src/coreclr/vm/corelib.h
@@ -435,7 +435,9 @@ DEFINE_METHOD(DYNAMICINTERFACECASTABLEHELPERS, GET_INTERFACE_IMPLEMENTATION,
DEFINE_CLASS(ICUSTOM_QUERYINTERFACE, Interop, ICustomQueryInterface)
DEFINE_METHOD(ICUSTOM_QUERYINTERFACE, GET_INTERFACE, GetInterface, IM_RefGuid_OutIntPtr_RetCustomQueryInterfaceResult)
DEFINE_CLASS(CUSTOMQUERYINTERFACERESULT, Interop, CustomQueryInterfaceResult)
+#endif //FEATURE_COMINTEROP
+#ifdef FEATURE_COMWRAPPERS
DEFINE_CLASS(COMWRAPPERS, Interop, ComWrappers)
DEFINE_CLASS(CREATECOMINTERFACEFLAGS, Interop, CreateComInterfaceFlags)
DEFINE_CLASS(CREATEOBJECTFLAGS, Interop, CreateObjectFlags)
@@ -444,7 +446,13 @@ DEFINE_METHOD(COMWRAPPERS, COMPUTE_VTABLES, CallComputeVtables,
DEFINE_METHOD(COMWRAPPERS, CREATE_OBJECT, CallCreateObject, SM_Scenario_ComWrappers_IntPtr_CreateFlags_RetObj)
DEFINE_METHOD(COMWRAPPERS, RELEASE_OBJECTS, CallReleaseObjects, SM_ComWrappers_IEnumerable_RetVoid)
DEFINE_METHOD(COMWRAPPERS, CALL_ICUSTOMQUERYINTERFACE, CallICustomQueryInterface, SM_Obj_RefGuid_RefIntPtr_RetInt)
-#endif //FEATURE_COMINTEROP
+#endif //FEATURE_COMWRAPPERS
+
+#ifdef FEATURE_OBJCMARSHAL
+DEFINE_CLASS(OBJCMARSHAL, ObjectiveC, ObjectiveCMarshal)
+DEFINE_METHOD(OBJCMARSHAL, AVAILABLEUNHANDLEDEXCEPTIONPROPAGATION, AvailableUnhandledExceptionPropagation, SM_RetBool)
+DEFINE_METHOD(OBJCMARSHAL, INVOKEUNHANDLEDEXCEPTIONPROPAGATION, InvokeUnhandledExceptionPropagation, SM_Exception_Obj_RefIntPtr_RetVoidPtr)
+#endif // FEATURE_OBJCMARSHAL
DEFINE_CLASS(IENUMERATOR, Collections, IEnumerator)
@@ -989,6 +997,7 @@ DEFINE_METHOD(STUBHELPERS, ADD_TO_CLEANUP_LIST_SAFEHANDLE, AddToClea
DEFINE_METHOD(STUBHELPERS, KEEP_ALIVE_VIA_CLEANUP_LIST, KeepAliveViaCleanupList, SM_RefCleanupWorkListElement_Obj_RetVoid)
DEFINE_METHOD(STUBHELPERS, DESTROY_CLEANUP_LIST, DestroyCleanupList, SM_RefCleanupWorkListElement_RetVoid)
DEFINE_METHOD(STUBHELPERS, GET_HR_EXCEPTION_OBJECT, GetHRExceptionObject, SM_Int_RetException)
+DEFINE_METHOD(STUBHELPERS, GET_PENDING_EXCEPTION_OBJECT, GetPendingExceptionObject, SM_RetException)
DEFINE_METHOD(STUBHELPERS, CREATE_CUSTOM_MARSHALER_HELPER, CreateCustomMarshalerHelper, SM_IntPtr_Int_IntPtr_RetIntPtr)
DEFINE_METHOD(STUBHELPERS, CHECK_STRING_LENGTH, CheckStringLength, SM_Int_RetVoid)
diff --git a/src/coreclr/vm/crossgen/CMakeLists.txt b/src/coreclr/vm/crossgen/CMakeLists.txt
index 280a2931883..deb6d49e176 100644
--- a/src/coreclr/vm/crossgen/CMakeLists.txt
+++ b/src/coreclr/vm/crossgen/CMakeLists.txt
@@ -232,13 +232,14 @@ else()
endif()
if (CLR_CMAKE_TARGET_WIN32)
+
+ # COM interop scenarios
list(APPEND VM_CROSSGEN_SOURCES
../classcompat.cpp
../comtoclrcall.cpp
../clrtocomcall.cpp
../runtimecallablewrapper.cpp
)
-
list(APPEND VM_CROSSGEN_HEADERS
../classcompat.h
../clrtocomcall.h
diff --git a/src/coreclr/vm/dllimport.cpp b/src/coreclr/vm/dllimport.cpp
index 77031b182f4..d842d894717 100644
--- a/src/coreclr/vm/dllimport.cpp
+++ b/src/coreclr/vm/dllimport.cpp
@@ -36,6 +36,7 @@
#include "fieldmarshaler.h"
#include "pinvokeoverride.h"
#include "nativelibrary.h"
+#include "interoplibinterface.h"
#include <formattype.h>
#include "../md/compiler/custattr.h"
@@ -725,7 +726,7 @@ public:
ILCodeLabel* pSkipThrowLabel = pcsDispatch->NewCodeLabel();
pcsDispatch->EmitDUP();
- pcsDispatch->EmitLDC(0);
+ pcsDispatch->EmitLDC(0); // Compare against S_OK (i.e. 0).
pcsDispatch->EmitBGE(pSkipThrowLabel);
#ifdef FEATURE_COMINTEROP
@@ -749,6 +750,20 @@ public:
}
}
+ if (SF_IsCheckPendingException(m_dwStubFlags)
+ && SF_IsForwardStub(m_dwStubFlags))
+ {
+ ILCodeLabel* pSkipThrowLabel = pcsDispatch->NewCodeLabel();
+
+ pcsDispatch->EmitCALL(METHOD__STUBHELPERS__GET_PENDING_EXCEPTION_OBJECT, 0, 1);
+ pcsDispatch->EmitDUP();
+ pcsDispatch->EmitBRFALSE(pSkipThrowLabel);
+ pcsDispatch->EmitTHROW();
+ pcsDispatch->EmitLDC(0); // keep the IL stack balanced across the branch and the fall-through
+ pcsDispatch->EmitLabel(pSkipThrowLabel);
+ pcsDispatch->EmitPOP();
+ }
+
m_slIL.End(m_dwStubFlags);
if (!hasTryCatchForHRESULT) // we will 'leave' the try scope and then 'ret' from outside
{
@@ -1360,6 +1375,10 @@ private:
{
dwStubFlags |= NDIRECTSTUB_FL_SUPPRESSGCTRANSITION;
}
+ if (HasCheckForPendingException(pTargetMD))
+ {
+ dwStubFlags |= NDIRECTSTUB_FL_CHECK_PENDING_EXCEPTION;
+ }
return dwStubFlags;
}
@@ -1386,6 +1405,22 @@ private:
{
return SF_IsForwardStub(dwStubFlags) && pTargetMD && pTargetMD->ShouldSuppressGCTransition();
}
+
+ static BOOL HasCheckForPendingException(MethodDesc* pTargetMD)
+ {
+#ifdef CROSSGEN_COMPILE
+ return FALSE;
+#else
+ if (pTargetMD == NULL || !pTargetMD->IsNDirect())
+ return FALSE;
+
+ auto pNMD = (NDirectMethodDesc*)pTargetMD;
+ if (!Interop::ShouldCheckForPendingException(pNMD))
+ return FALSE;
+
+ return TRUE;
+#endif // !CROSSGEN_COMPILE
+ }
};
#ifdef FEATURE_COMINTEROP
@@ -3088,10 +3123,10 @@ BOOL NDirect::MarshalingRequired(
if (pMD != NULL)
{
+ // HRESULT swapping is handled by stub
if (pMD->IsNDirect() || pMD->IsComPlusCall())
{
- // HRESULT swapping is handled by stub
- if ((pMD->GetImplAttrs() & miPreserveSig) == 0)
+ if (!IsMiPreserveSig(pMD->GetImplAttrs()))
return TRUE;
}
@@ -3120,6 +3155,12 @@ BOOL NDirect::MarshalingRequired(
// Make sure running cctor can be handled by stub
if (pNMD->IsClassConstructorTriggeredByILStub())
return TRUE;
+
+#ifndef CROSSGEN_COMPILE
+ // Pending exceptions are handled by stub
+ if (Interop::ShouldCheckForPendingException(pNMD))
+ return TRUE;
+#endif // !CROSSGEN_COMPILE
}
callConv = sigInfo.GetCallConv();
diff --git a/src/coreclr/vm/dllimport.h b/src/coreclr/vm/dllimport.h
index b48d4aee821..80c1ddf18e8 100644
--- a/src/coreclr/vm/dllimport.h
+++ b/src/coreclr/vm/dllimport.h
@@ -143,7 +143,7 @@ enum NDirectStubFlags
NDIRECTSTUB_FL_SUPPRESSGCTRANSITION = 0x00008000,
NDIRECTSTUB_FL_STUB_HAS_THIS = 0x00010000,
NDIRECTSTUB_FL_TARGET_HAS_THIS = 0x00020000,
- // unused = 0x00040000,
+ NDIRECTSTUB_FL_CHECK_PENDING_EXCEPTION = 0x00040000,
// unused = 0x00080000,
// unused = 0x00100000,
// unused = 0x00200000,
@@ -202,6 +202,7 @@ inline bool SF_IsCALLIStub (DWORD dwStubFlags) { LIMITED_METHOD_CONT
inline bool SF_IsStubWithCctorTrigger (DWORD dwStubFlags) { LIMITED_METHOD_CONTRACT; return (dwStubFlags < NDIRECTSTUB_FL_INVALID && 0 != (dwStubFlags & NDIRECTSTUB_FL_TRIGGERCCTOR)); }
inline bool SF_IsForNumParamBytes (DWORD dwStubFlags) { LIMITED_METHOD_CONTRACT; return (dwStubFlags < NDIRECTSTUB_FL_INVALID && 0 != (dwStubFlags & NDIRECTSTUB_FL_FOR_NUMPARAMBYTES)); }
inline bool SF_IsStructMarshalStub (DWORD dwStubFlags) { LIMITED_METHOD_CONTRACT; return (dwStubFlags < NDIRECTSTUB_FL_INVALID && 0 != (dwStubFlags & NDIRECTSTUB_FL_STRUCT_MARSHAL)); }
+inline bool SF_IsCheckPendingException (DWORD dwStubFlags) { LIMITED_METHOD_CONTRACT; return (dwStubFlags < NDIRECTSTUB_FL_INVALID && 0 != (dwStubFlags & NDIRECTSTUB_FL_CHECK_PENDING_EXCEPTION)); }
#ifdef FEATURE_ARRAYSTUB_AS_IL
inline bool SF_IsArrayOpStub (DWORD dwStubFlags) { LIMITED_METHOD_CONTRACT; return ((dwStubFlags == ILSTUB_ARRAYOP_GET) ||
diff --git a/src/coreclr/vm/ecalllist.h b/src/coreclr/vm/ecalllist.h
index eacef79a9b9..168e8b4d8c5 100644
--- a/src/coreclr/vm/ecalllist.h
+++ b/src/coreclr/vm/ecalllist.h
@@ -930,6 +930,14 @@ FCFuncStart(gComWrappersFuncs)
FCFuncEnd()
#endif // FEATURE_COMWRAPPERS
+#ifdef FEATURE_OBJCMARSHAL
+FCFuncStart(gObjCMarshalFuncs)
+ QCFuncElement("TrySetGlobalMessageSendCallback", ObjCMarshalNative::TrySetGlobalMessageSendCallback)
+ QCFuncElement("TryInitializeReferenceTracker", ObjCMarshalNative::TryInitializeReferenceTracker)
+ QCFuncElement("CreateReferenceTrackingHandleInternal", ObjCMarshalNative::CreateReferenceTrackingHandle)
+FCFuncEnd()
+#endif // FEATURE_OBJCMARSHAL
+
FCFuncStart(gMngdRefCustomMarshalerFuncs)
FCFuncElement("CreateMarshaler", MngdRefCustomMarshaler::CreateMarshaler)
FCFuncElement("ConvertContentsToNative", MngdRefCustomMarshaler::ConvertContentsToNative)
@@ -1121,9 +1129,9 @@ FCClassElement("AssemblyName", "System.Reflection", gAssemblyNameFuncs)
FCClassElement("Buffer", "System", gBufferFuncs)
FCClassElement("CLRConfig", "System", gClrConfig)
FCClassElement("CastHelpers", "System.Runtime.CompilerServices", gCastHelpers)
-#ifdef FEATURE_COMINTEROP
+#ifdef FEATURE_COMWRAPPERS
FCClassElement("ComWrappers", "System.Runtime.InteropServices", gComWrappersFuncs)
-#endif // FEATURE_COMINTEROP
+#endif // FEATURE_COMWRAPPERS
FCClassElement("CompatibilitySwitch", "System.Runtime.Versioning", gCompatibilitySwitchFuncs)
FCClassElement("CustomAttribute", "System.Reflection", gCOMCustomAttributeFuncs)
FCClassElement("CustomAttributeEncodedArgument", "System.Reflection", gCustomAttributeEncodedArgument)
@@ -1180,6 +1188,9 @@ FCClassElement("Object", "System", gObjectFuncs)
#ifdef FEATURE_COMINTEROP
FCClassElement("ObjectMarshaler", "System.StubHelpers", gObjectMarshalerFuncs)
#endif
+#ifdef FEATURE_OBJCMARSHAL
+FCClassElement("ObjectiveCMarshal", "System.Runtime.InteropServices.ObjectiveC", gObjCMarshalFuncs)
+#endif // FEATURE_OBJCMARSHAL
FCClassElement("OverlappedData", "System.Threading", gOverlappedFuncs)
@@ -1216,7 +1227,6 @@ FCClassElement("Variant", "System", gVariantFuncs)
FCClassElement("WaitHandle", "System.Threading", gWaitHandleFuncs)
FCClassElement("WeakReference", "System", gWeakReferenceFuncs)
FCClassElement("WeakReference`1", "System", gWeakReferenceOfTFuncs)
-
#if defined(TARGET_X86) || defined(TARGET_AMD64)
FCClassElement("X86Base", "System.Runtime.Intrinsics.X86", gX86BaseFuncs)
#endif // defined(TARGET_X86) || defined(TARGET_AMD64)
diff --git a/src/coreclr/vm/exceptionhandling.cpp b/src/coreclr/vm/exceptionhandling.cpp
index 11d0072dc70..963df734959 100644
--- a/src/coreclr/vm/exceptionhandling.cpp
+++ b/src/coreclr/vm/exceptionhandling.cpp
@@ -15,6 +15,7 @@
#include "eventtrace.h"
#include "virtualcallstub.h"
#include "utilcode.h"
+#include "interoplibinterface.h"
#if defined(TARGET_X86)
#define USE_CURRENT_CONTEXT_IN_FILTER
@@ -1212,28 +1213,23 @@ lExit: ;
EECodeInfo codeInfo(pDispatcherContext->ControlPc);
if (codeInfo.IsValid())
{
+ bool invalidRevPInvoke;
#ifdef USE_GC_INFO_DECODER
GcInfoDecoder gcInfoDecoder(codeInfo.GetGCInfoToken(), DECODE_REVERSE_PINVOKE_VAR);
- if (gcInfoDecoder.GetReversePInvokeFrameStackSlot() != NO_REVERSE_PINVOKE_FRAME)
- {
- // Exception is being propagated from a method marked UnmanagedCallersOnlyAttribute into its native caller.
- // The explicit frame chain needs to be unwound at this boundary.
- bool fIsSO = pExceptionRecord->ExceptionCode == STATUS_STACK_OVERFLOW;
- CleanUpForSecondPass(pThread, fIsSO, (void*)MemoryStackFp, (void*)MemoryStackFp);
- }
+ invalidRevPInvoke = gcInfoDecoder.GetReversePInvokeFrameStackSlot() != NO_REVERSE_PINVOKE_FRAME;
#else // USE_GC_INFO_DECODER
hdrInfo gcHdrInfo;
-
DecodeGCHdrInfo(gcInfoToken, 0, &gcHdrInfo);
+ invalidRevPInvoke = gcHdrInfo.revPInvokeOffset != INVALID_REV_PINVOKE_OFFSET;
+#endif // USE_GC_INFO_DECODER
- if (gcHdrInfo.revPInvokeOffset != INVALID_REV_PINVOKE_OFFSET)
+ if (invalidRevPInvoke)
{
// Exception is being propagated from a method marked UnmanagedCallersOnlyAttribute into its native caller.
// The explicit frame chain needs to be unwound at this boundary.
bool fIsSO = pExceptionRecord->ExceptionCode == STATUS_STACK_OVERFLOW;
CleanUpForSecondPass(pThread, fIsSO, (void*)MemoryStackFp, (void*)MemoryStackFp);
}
-#endif // USE_GC_INFO_DECODER
}
}
@@ -4312,6 +4308,60 @@ static void DoEHLog(
//---------------------------------------------------------------------------------------
//
+// Function to update the current context for exception propagation.
+//
+// Arguments:
+// exception - the PAL_SEHException representing the propagating exception.
+// currentContext - the current context to update.
+//
+static VOID UpdateContextForPropagationCallback(
+ PAL_SEHException& ex,
+ CONTEXT* startContext)
+{
+ _ASSERTE(ex.ManagedToNativeExceptionCallback != NULL);
+
+#ifdef TARGET_AMD64
+
+ // Don't restore the stack pointer to exact same context. Leave the
+ // return IP on the stack to let the unwinder work if the callback throws
+ // an exception as opposed to failing fast.
+ startContext->Rsp -= sizeof(void*);
+
+ // Pass the context for the callback as the first argument.
+ startContext->Rdi = (DWORD64)ex.ManagedToNativeExceptionCallbackContext;
+
+#elif defined(TARGET_ARM64)
+
+ // Reset the linked return register to the current function to let the
+ // unwinder work if the callback throws an exception as opposed to failing fast.
+ startContext->Lr = GetIP(startContext);
+
+ // Pass the context for the callback as the first argument.
+ startContext->X0 = (DWORD64)ex.ManagedToNativeExceptionCallbackContext;
+
+#elif defined(TARGET_ARM)
+
+ // Reset the linked return register to the current function to let the
+ // unwinder work if the callback throws an exception as opposed to failing fast.
+ startContext->Lr = GetIP(startContext);
+
+ // Pass the context for the callback as the first argument.
+ startContext->R0 = (DWORD)ex.ManagedToNativeExceptionCallbackContext;
+
+#else
+
+ EEPOLICY_HANDLE_FATAL_ERROR_WITH_MESSAGE(
+ COR_E_FAILFAST,
+ W("Managed exception propagation not supported for platform."));
+
+#endif
+
+ // The last thing to do is set the supplied callback function.
+ SetIP(startContext, (PCODE)ex.ManagedToNativeExceptionCallback);
+}
+
+//---------------------------------------------------------------------------------------
+//
// This functions performs an unwind procedure for a managed exception. The stack is unwound
// until the target frame is reached. For each frame we use its PC value to find
// a handler using information that has been built by JIT.
@@ -4438,13 +4488,28 @@ VOID UnwindManagedExceptionPass2(PAL_SEHException& ex, CONTEXT* unwindStartConte
// We also need to reset the scanned stack range since the scanned frames will be
// obsolete after the unwind of the native frames completes.
ExceptionTracker* pTracker = GetThread()->GetExceptionState()->GetCurrentExceptionTracker();
- pTracker->CleanupBeforeNativeFramesUnwind();
+
+ // The tracker will only be non-null in the case when we handle the managed exception in
+ // the runtime native code.
+ if (pTracker != NULL)
+ pTracker->CleanupBeforeNativeFramesUnwind();
}
- // Now we need to unwind the native frames until we reach managed frames again or the exception is
- // handled in the native code.
- STRESS_LOG2(LF_EH, LL_INFO100, "Unwinding native frames starting at IP = %p, SP = %p \n", controlPc, sp);
- PAL_ThrowExceptionFromContext(currentFrameContext, &ex);
+ if (ex.HasPropagateExceptionCallback())
+ {
+ // A propagation callback was supplied.
+ STRESS_LOG3(LF_EH, LL_INFO100, "Deferring exception propagation to Callback = %p, IP = %p, SP = %p \n", ex.ManagedToNativeExceptionCallback, controlPc, sp);
+
+ UpdateContextForPropagationCallback(ex, currentFrameContext);
+ ExceptionTracker::ResumeExecution(currentFrameContext);
+ }
+ else
+ {
+ // Now we need to unwind the native frames until we reach managed frames again or the exception is
+ // handled in the native code.
+ STRESS_LOG2(LF_EH, LL_INFO100, "Unwinding native frames starting at IP = %p, SP = %p \n", controlPc, sp);
+ PAL_ThrowExceptionFromContext(currentFrameContext, &ex);
+ }
UNREACHABLE();
}
@@ -4569,37 +4634,56 @@ VOID DECLSPEC_NORETURN UnwindManagedExceptionPass1(PAL_SEHException& ex, CONTEXT
controlPc = Thread::VirtualUnwindLeafCallFrame(frameContext);
}
+ bool invalidRevPInvoke;
#ifdef USE_GC_INFO_DECODER
GcInfoDecoder gcInfoDecoder(codeInfo.GetGCInfoToken(), DECODE_REVERSE_PINVOKE_VAR);
-
- if (gcInfoDecoder.GetReversePInvokeFrameStackSlot() != NO_REVERSE_PINVOKE_FRAME)
- {
- // Propagating exception from a method marked by UnmanagedCallersOnly attribute is prohibited on Unix
- if (!GetThread()->HasThreadStateNC(Thread::TSNC_ProcessedUnhandledException))
- {
- LONG disposition = InternalUnhandledExceptionFilter_Worker(&ex.ExceptionPointers);
- _ASSERTE(disposition == EXCEPTION_CONTINUE_SEARCH);
- }
- CrashDumpAndTerminateProcess(1);
- UNREACHABLE();
- }
+ invalidRevPInvoke = gcInfoDecoder.GetReversePInvokeFrameStackSlot() != NO_REVERSE_PINVOKE_FRAME;
#else // USE_GC_INFO_DECODER
hdrInfo gcHdrInfo;
-
DecodeGCHdrInfo(gcInfoToken, 0, &gcHdrInfo);
+ invalidRevPInvoke = gcHdrInfo.revPInvokeOffset != INVALID_REV_PINVOKE_OFFSET;
+#endif // USE_GC_INFO_DECODER
- if (gcHdrInfo.revPInvokeOffset != INVALID_REV_PINVOKE_OFFSET)
+ if (invalidRevPInvoke)
{
- // Propagating exception from a method marked by UnmanagedCallersOnly attribute is prohibited on Unix
- if (!GetThread()->HasThreadStateNC(Thread::TSNC_ProcessedUnhandledException))
+ ExceptionTracker* pTracker = GetThread()->GetExceptionState()->GetCurrentExceptionTracker();
+
+ void* callbackCxt = NULL;
+ Interop::ManagedToNativeExceptionCallback callback = Interop::GetPropagatingExceptionCallback(
+ &codeInfo,
+ pTracker->GetThrowableAsHandle(),
+ &callbackCxt);
+
+ // If a callback doesn't exist we immediately crash.
+ if (callback == NULL)
{
- LONG disposition = InternalUnhandledExceptionFilter_Worker(&ex.ExceptionPointers);
- _ASSERTE(disposition == EXCEPTION_CONTINUE_SEARCH);
+ // Propagating exception from a method marked by UnmanagedCallersOnly attribute is prohibited on Unix
+ if (!GetThread()->HasThreadStateNC(Thread::TSNC_ProcessedUnhandledException))
+ {
+ LONG disposition = InternalUnhandledExceptionFilter_Worker(&ex.ExceptionPointers);
+ _ASSERTE(disposition == EXCEPTION_CONTINUE_SEARCH);
+ }
+ CrashDumpAndTerminateProcess(1);
}
- CrashDumpAndTerminateProcess(1);
+ else
+ {
+ ex.SetPropagateExceptionCallback(callback, callbackCxt);
+ _ASSERTE(ex.HasPropagateExceptionCallback());
+
+ BOOL success = PAL_VirtualUnwind(frameContext, NULL);
+ if (!success)
+ {
+ _ASSERTE(!"UnwindManagedExceptionPass1: PAL_VirtualUnwind failed for propagate exception scenario");
+ EEPOLICY_HANDLE_FATAL_ERROR(COR_E_EXECUTIONENGINE);
+ }
+
+ UINT_PTR sp = GetSP(frameContext);
+ ex.TargetFrameSp = sp;
+ UnwindManagedExceptionPass2(ex, &unwindStartContext);
+ }
+
UNREACHABLE();
}
-#endif // USE_GC_INFO_DECODER
// Check whether we are crossing managed-to-native boundary
while (!ExecutionManager::IsManagedCode(controlPc))
diff --git a/src/coreclr/vm/gcenv.ee.cpp b/src/coreclr/vm/gcenv.ee.cpp
index a77b0f489a5..650e91247f9 100644
--- a/src/coreclr/vm/gcenv.ee.cpp
+++ b/src/coreclr/vm/gcenv.ee.cpp
@@ -49,6 +49,27 @@ VOID GCToEEInterface::SyncBlockCacheWeakPtrScan(HANDLESCANPROC scanProc, uintptr
SyncBlockCache::GetSyncBlockCache()->GCWeakPtrScan(scanProc, lp1, lp2);
}
+void GCToEEInterface::BeforeGcScanRoots(int condemned, bool is_bgc, bool is_concurrent)
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ GC_NOTRIGGER;
+ }
+ CONTRACTL_END;
+
+#ifdef VERIFY_HEAP
+ if (is_bgc)
+ {
+ // Validate byrefs pinned by IL stubs since the last GC.
+ StubHelpers::ProcessByrefValidationList();
+ }
+#endif // VERIFY_HEAP
+
+ if (!is_concurrent)
+ Interop::OnBeforeGCScanRoots();
+}
+
//EE can perform post stack scanning action, while the
// user threads are still suspended
VOID GCToEEInterface::AfterGcScanRoots (int condemned, int max_gen,
@@ -66,6 +87,9 @@ VOID GCToEEInterface::AfterGcScanRoots (int condemned, int max_gen,
// the RCW cache from resurrecting them.
::GetAppDomain()->DetachRCWs();
#endif // FEATURE_COMINTEROP
+
+ if (!sc->concurrent)
+ Interop::OnAfterGCScanRoots();
}
/*
@@ -287,9 +311,7 @@ void GCToEEInterface::GcStartWork (int condemned, int max_gen)
ETW::TypeSystemLog::Cleanup();
#endif
-#ifdef FEATURE_COMINTEROP
Interop::OnGCStarted(condemned);
-#endif // FEATURE_COMINTEROP
if (condemned == max_gen)
{
@@ -306,9 +328,7 @@ void GCToEEInterface::GcDone(int condemned)
}
CONTRACTL_END;
-#ifdef FEATURE_COMINTEROP
Interop::OnGCFinished(condemned);
-#endif // FEATURE_COMINTEROP
}
bool GCToEEInterface::RefCountedHandleCallbacks(Object * pObject)
@@ -332,25 +352,18 @@ bool GCToEEInterface::RefCountedHandleCallbacks(Object * pObject)
if (ComWrappersNative::HasManagedObjectComWrapper((OBJECTREF)pObject, &isRooted))
return isRooted;
#endif
+#ifdef FEATURE_OBJCMARSHAL
+ bool isReferenced = false;
+ if (ObjCMarshalNative::IsTrackedReference((OBJECTREF)pObject, &isReferenced))
+ return isReferenced;
+#endif
+#if (defined(FEATURE_COMINTEROP) || defined(FEATURE_COMWRAPPERS)) && defined(FEATURE_OBJCMARSHAL)
+#error COM and Objective-C are not supported at the same time.
+#endif
return false;
}
-void GCToEEInterface::GcBeforeBGCSweepWork()
-{
- CONTRACTL
- {
- NOTHROW;
- GC_NOTRIGGER;
- }
- CONTRACTL_END;
-
-#ifdef VERIFY_HEAP
- // Validate byrefs pinned by IL stubs since the last GC.
- StubHelpers::ProcessByrefValidationList();
-#endif // VERIFY_HEAP
-}
-
void GCToEEInterface::SyncBlockCacheDemote(int max_gen)
{
CONTRACTL
@@ -441,7 +454,6 @@ bool GCToEEInterface::IsPreemptiveGCDisabled()
WRAPPER_NO_CONTRACT;
Thread* pThread = ::GetThreadNULLOk();
-
return (pThread && pThread->PreemptiveGCDisabled());
}
@@ -1090,6 +1102,13 @@ bool GCToEEInterface::EagerFinalized(Object* obj)
FinalizeWeakReference(obj);
return true;
}
+#ifdef FEATURE_OBJCMARSHAL
+ else if (pMT->IsTrackedReferenceWithFinalizer())
+ {
+ ObjCMarshalNative::OnEnteredFinalizerQueue((OBJECTREF)obj);
+ return false;
+ }
+#endif // FEATURE_OBJCMARSHAL
return false;
}
@@ -1623,7 +1642,7 @@ void GCToEEInterface::AnalyzeSurvivorsFinished(size_t gcIndex, int condemnedGene
DACNotify::DoGCNotification(gea);
}
}
-
+
if (gcGenAnalysisState == GcGenAnalysisState::Enabled)
{
#ifndef GEN_ANALYSIS_STRESS
diff --git a/src/coreclr/vm/gcenv.ee.h b/src/coreclr/vm/gcenv.ee.h
index dfdcd3ab979..6ac79248a6d 100644
--- a/src/coreclr/vm/gcenv.ee.h
+++ b/src/coreclr/vm/gcenv.ee.h
@@ -33,8 +33,8 @@ public:
void RestartEE(bool bFinishedGC);
void GcScanRoots(promote_func* fn, int condemned, int max_gen, ScanContext* sc);
void GcStartWork(int condemned, int max_gen);
+ void BeforeGcScanRoots(int condemned, bool is_bgc, bool is_concurrent);
void AfterGcScanRoots(int condemned, int max_gen, ScanContext* sc);
- void GcBeforeBGCSweepWork();
void GcDone(int condemned);
bool RefCountedHandleCallbacks(Object * pObject);
void SyncBlockCacheWeakPtrScan(HANDLESCANPROC scanProc, uintptr_t lp1, uintptr_t lp2);
diff --git a/src/coreclr/vm/gcenv.ee.standalone.cpp b/src/coreclr/vm/gcenv.ee.standalone.cpp
index 6e982fdd839..f9f2adaa6ce 100644
--- a/src/coreclr/vm/gcenv.ee.standalone.cpp
+++ b/src/coreclr/vm/gcenv.ee.standalone.cpp
@@ -6,10 +6,10 @@
#include "gcenv.ee.h"
#include "threadsuspend.h"
#include "nativeoverlapped.h"
+#include "interoplibinterface.h"
#ifdef FEATURE_COMINTEROP
#include "runtimecallablewrapper.h"
-#include "interoplibinterface.h"
#include "comcallablewrapper.h"
#endif // FEATURE_COMINTEROP
diff --git a/src/coreclr/vm/gcenv.ee.static.cpp b/src/coreclr/vm/gcenv.ee.static.cpp
index 375df5fac87..acabba90dec 100644
--- a/src/coreclr/vm/gcenv.ee.static.cpp
+++ b/src/coreclr/vm/gcenv.ee.static.cpp
@@ -6,10 +6,10 @@
#include "../gc/env/gcenv.ee.h"
#include "threadsuspend.h"
#include "nativeoverlapped.h"
+#include "interoplibinterface.h"
#ifdef FEATURE_COMINTEROP
#include "runtimecallablewrapper.h"
-#include "interoplibinterface.h"
#include "comcallablewrapper.h"
#endif // FEATURE_COMINTEROP
diff --git a/src/coreclr/vm/interoplibinterface.cpp b/src/coreclr/vm/interoplibinterface.cpp
index cc45bd7a520..f8804e32d25 100644
--- a/src/coreclr/vm/interoplibinterface.cpp
+++ b/src/coreclr/vm/interoplibinterface.cpp
@@ -1,6 +1,8 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
+#ifdef FEATURE_COMWRAPPERS
+
// Runtime headers
#include "common.h"
#include "rcwrefcache.h"
@@ -1296,8 +1298,6 @@ namespace InteropLibImports
}
}
-#ifdef FEATURE_COMWRAPPERS
-
BOOL QCALLTYPE ComWrappersNative::TryGetOrCreateComInterfaceForObject(
_In_ QCall::ObjectHandleOnStack comWrappersImpl,
_In_ INT64 wrapperId,
@@ -1751,9 +1751,7 @@ bool ComWrappersNative::HasManagedObjectComWrapper(_In_ OBJECTREF object, _Out_
return cxt.HasWrapper;
}
-#endif // FEATURE_COMWRAPPERS
-
-void Interop::OnGCStarted(_In_ int nCondemnedGeneration)
+void ComWrappersNative::OnFullGCStarted()
{
CONTRACTL
{
@@ -1762,42 +1760,26 @@ void Interop::OnGCStarted(_In_ int nCondemnedGeneration)
}
CONTRACTL_END;
-#ifdef FEATURE_COMWRAPPERS
- //
- // Note that we could get nested GCStart/GCEnd calls, such as :
- // GCStart for Gen 2 background GC
- // GCStart for Gen 0/1 foregorund GC
- // GCEnd for Gen 0/1 foreground GC
- // ....
- // GCEnd for Gen 2 background GC
- //
- // The nCondemnedGeneration >= 2 check takes care of this nesting problem
- //
- // See Interop::OnGCFinished()
- if (nCondemnedGeneration >= 2)
+ // If no cache exists, then there is nothing to do here.
+ ExtObjCxtCache* cache = ExtObjCxtCache::GetInstanceNoThrow();
+ if (cache != NULL)
{
- // If no cache exists, then there is nothing to do here.
- ExtObjCxtCache* cache = ExtObjCxtCache::GetInstanceNoThrow();
- if (cache != NULL)
- {
- STRESS_LOG0(LF_INTEROP, LL_INFO10000, "Begin Reference Tracking\n");
- ExtObjCxtRefCache* refCache = cache->GetRefCache();
+ STRESS_LOG0(LF_INTEROP, LL_INFO10000, "Begin Reference Tracking\n");
+ ExtObjCxtRefCache* refCache = cache->GetRefCache();
- // Reset the ref cache
- refCache->ResetDependentHandles();
+ // Reset the ref cache
+ refCache->ResetDependentHandles();
- // Create a call context for the InteropLib.
- InteropLibImports::RuntimeCallContext cxt(cache);
- (void)InteropLib::Com::BeginExternalObjectReferenceTracking(&cxt);
+ // Create a call context for the InteropLib.
+ InteropLibImports::RuntimeCallContext cxt(cache);
+ (void)InteropLib::Com::BeginExternalObjectReferenceTracking(&cxt);
- // Shrink cache and clear unused handles.
- refCache->ShrinkDependentHandles();
- }
+ // Shrink cache and clear unused handles.
+ refCache->ShrinkDependentHandles();
}
-#endif // FEATURE_COMWRAPPERS
}
-void Interop::OnGCFinished(_In_ int nCondemnedGeneration)
+void ComWrappersNative::OnFullGCFinished()
{
CONTRACTL
{
@@ -1806,26 +1788,12 @@ void Interop::OnGCFinished(_In_ int nCondemnedGeneration)
}
CONTRACTL_END;
-#ifdef FEATURE_COMWRAPPERS
- //
- // Note that we could get nested GCStart/GCEnd calls, such as :
- // GCStart for Gen 2 background GC
- // GCStart for Gen 0/1 foregorund GC
- // GCEnd for Gen 0/1 foreground GC
- // ....
- // GCEnd for Gen 2 background GC
- //
- // The nCondemnedGeneration >= 2 check takes care of this nesting problem
- //
- // See Interop::OnGCStarted()
- if (nCondemnedGeneration >= 2)
+ ExtObjCxtCache* cache = ExtObjCxtCache::GetInstanceNoThrow();
+ if (cache != NULL)
{
- ExtObjCxtCache* cache = ExtObjCxtCache::GetInstanceNoThrow();
- if (cache != NULL)
- {
- (void)InteropLib::Com::EndExternalObjectReferenceTracking();
- STRESS_LOG0(LF_INTEROP, LL_INFO10000, "End Reference Tracking\n");
- }
+ (void)InteropLib::Com::EndExternalObjectReferenceTracking();
+ STRESS_LOG0(LF_INTEROP, LL_INFO10000, "End Reference Tracking\n");
}
-#endif // FEATURE_COMWRAPPERS
}
+
+#endif // FEATURE_COMWRAPPERS
diff --git a/src/coreclr/vm/interoplibinterface.h b/src/coreclr/vm/interoplibinterface.h
index 1f379a32e86..b3bf554d5d9 100644
--- a/src/coreclr/vm/interoplibinterface.h
+++ b/src/coreclr/vm/interoplibinterface.h
@@ -59,6 +59,10 @@ public: // COM activation
public: // Unwrapping support
static IUnknown* GetIdentityForObject(_In_ OBJECTREF* objectPROTECTED, _In_ REFIID riid, _Out_ INT64* wrapperId);
static bool HasManagedObjectComWrapper(_In_ OBJECTREF object, _Out_ bool* isActive);
+
+public: // GC interaction
+ static void OnFullGCStarted();
+ static void OnFullGCFinished();
};
class GlobalComWrappersForMarshalling
@@ -82,7 +86,6 @@ public: // Functions operating on a registered global instance for marshalling
_Out_ OBJECTREF* objRef);
};
-
class GlobalComWrappersForTrackerSupport
{
public:
@@ -105,14 +108,86 @@ public: // Functions operating on a registered global instance for tracker suppo
#endif // FEATURE_COMWRAPPERS
+#ifdef FEATURE_OBJCMARSHAL
+
+class ObjCMarshalNative
+{
+public:
+ using BeginEndCallback = void(STDMETHODCALLTYPE *)(void);
+ using IsReferencedCallback = int(STDMETHODCALLTYPE *)(_In_ void*);
+ using EnteredFinalizationCallback = void(STDMETHODCALLTYPE *)(_In_ void*);
+
+ // See MessageSendFunction in ObjectiveCMarshal class
+ enum MessageSendFunction
+ {
+ MessageSendFunction_MsgSend = 0,
+ MessageSendFunction_MsgSendFpret = 1,
+ MessageSendFunction_MsgSendStret = 2,
+ MessageSendFunction_MsgSendSuper = 3,
+ MessageSendFunction_MsgSendSuperStret = 4,
+ Last = MessageSendFunction_MsgSendSuperStret,
+ };
+
+public: // static
+ static BOOL QCALLTYPE TryInitializeReferenceTracker(
+ _In_ BeginEndCallback beginEndCallback,
+ _In_ IsReferencedCallback isReferencedCallback,
+ _In_ EnteredFinalizationCallback trackedObjectEnteredFinalization);
+
+ static void* QCALLTYPE CreateReferenceTrackingHandle(
+ _In_ QCall::ObjectHandleOnStack obj,
+ _Out_ int* memInSizeT,
+ _Outptr_ void** mem);
+
+ static BOOL QCALLTYPE TrySetGlobalMessageSendCallback(
+ _In_ MessageSendFunction msgSendFunction,
+ _In_ void* fptr);
+
+public: // Instance inspection
+ static bool IsTrackedReference(_In_ OBJECTREF object, _Out_ bool* isReferenced);
+
+public: // Identification
+ static bool IsRuntimeMsgSendFunctionOverridden(
+ _In_z_ const char* libraryName,
+ _In_z_ const char* entrypointName);
+
+public: // Exceptions
+ static void* GetPropagatingExceptionCallback(
+ _In_ EECodeInfo* codeInfo,
+ _In_ OBJECTHANDLE throwable,
+ _Outptr_ void** context);
+
+public: // GC interaction
+ static void BeforeRefCountedHandleCallbacks();
+ static void AfterRefCountedHandleCallbacks();
+ static void OnEnteredFinalizerQueue(_In_ OBJECTREF object);
+};
+
+#endif // FEATURE_OBJCMARSHAL
+
class Interop
{
public:
- // Notify when GC started
- static void OnGCStarted(_In_ int nCondemnedGeneration);
+ // Check if pending exceptions are possible for the following native export.
+ static bool ShouldCheckForPendingException(_In_ NDirectMethodDesc* md);
- // Notify when GC finished
+ // A no return callback that is designed to help propagate a managed
+ // exception going from Managed to Native.
+ using ManagedToNativeExceptionCallback = /* no return */ void(*)(_In_ void* context);
+
+ static ManagedToNativeExceptionCallback GetPropagatingExceptionCallback(
+ _In_ EECodeInfo* codeInfo,
+ _In_ OBJECTHANDLE throwable,
+ _Outptr_ void** context);
+
+ // Notify started/finished when GC is running.
+ static void OnGCStarted(_In_ int nCondemnedGeneration);
static void OnGCFinished(_In_ int nCondemnedGeneration);
+
+ // Notify before/after when GC is scanning roots.
+ // Present assumption is that calls will never be nested.
+ static void OnBeforeGCScanRoots();
+ static void OnAfterGCScanRoots();
};
#endif // _INTEROPLIBINTERFACE_H_
diff --git a/src/coreclr/vm/interoplibinterface_objc.cpp b/src/coreclr/vm/interoplibinterface_objc.cpp
new file mode 100644
index 00000000000..131c3c277b8
--- /dev/null
+++ b/src/coreclr/vm/interoplibinterface_objc.cpp
@@ -0,0 +1,394 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+#ifdef FEATURE_OBJCMARSHAL
+
+// Runtime headers
+#include "common.h"
+
+// Interop library header
+#include <interoplibimports.h>
+
+#include "interoplibinterface.h"
+
+#include <pinvokeoverride.h>
+
+#define OBJC_MSGSEND "objc_msgSend"
+
+namespace
+{
+ BOOL g_ReferenceTrackerInitialized;
+ ObjCMarshalNative::BeginEndCallback g_BeginEndCallback;
+ ObjCMarshalNative::IsReferencedCallback g_IsReferencedCallback;
+ ObjCMarshalNative::EnteredFinalizationCallback g_TrackedObjectEnteredFinalizationCallback;
+}
+
+BOOL QCALLTYPE ObjCMarshalNative::TryInitializeReferenceTracker(
+ _In_ BeginEndCallback beginEndCallback,
+ _In_ IsReferencedCallback isReferencedCallback,
+ _In_ EnteredFinalizationCallback trackedObjectEnteredFinalization)
+{
+ QCALL_CONTRACT;
+ _ASSERTE(beginEndCallback != NULL
+ && isReferencedCallback != NULL
+ && trackedObjectEnteredFinalization != NULL);
+
+ BOOL success = FALSE;
+
+ BEGIN_QCALL;
+
+ // Switch to Cooperative mode since we are setting callbacks that
+ // will be used during a GC and we want to ensure a GC isn't occuring
+ // while they are being set.
+ {
+ GCX_COOP();
+ if (FastInterlockCompareExchange((LONG*)&g_ReferenceTrackerInitialized, TRUE, FALSE) == FALSE)
+ {
+ g_BeginEndCallback = beginEndCallback;
+ g_IsReferencedCallback = isReferencedCallback;
+ g_TrackedObjectEnteredFinalizationCallback = trackedObjectEnteredFinalization;
+
+ success = TRUE;
+ }
+ }
+
+ END_QCALL;
+
+ return success;
+}
+
+void* QCALLTYPE ObjCMarshalNative::CreateReferenceTrackingHandle(
+ _In_ QCall::ObjectHandleOnStack obj,
+ _Out_ int* memInSizeT,
+ _Outptr_ void** mem)
+{
+ QCALL_CONTRACT;
+ _ASSERTE(memInSizeT != NULL);
+ _ASSERTE(mem != NULL);
+
+ OBJECTHANDLE instHandle;
+ size_t memInSizeTLocal;
+ void* taggedMemoryLocal;
+
+ 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;
+ ::ZeroMemory(&gc, sizeof(gc));
+ 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(&memInSizeTLocal);
+ _ASSERTE(taggedMemoryLocal != NULL);
+
+ instHandle = GetAppDomain()->CreateTypedHandle(gc.objRef, HNDTYPE_REFCOUNTED);
+
+ GCPROTECT_END();
+ }
+
+ END_QCALL;
+
+ *memInSizeT = (int)memInSizeTLocal;
+ *mem = taggedMemoryLocal;
+ return (void*)instHandle;
+}
+
+namespace
+{
+ BOOL s_msgSendOverridden = FALSE;
+ void* s_msgSendOverrides[ObjCMarshalNative::MessageSendFunction::Last + 1] = {};
+
+ const char* ObjectiveCLibrary = "/usr/lib/libobjc.dylib";
+ const char* MsgSendEntryPoints[ObjCMarshalNative::MessageSendFunction::Last + 1] =
+ {
+ OBJC_MSGSEND,
+ OBJC_MSGSEND "_fpret",
+ OBJC_MSGSEND "_stret",
+ OBJC_MSGSEND "Super",
+ OBJC_MSGSEND "Super_stret",
+ };
+
+ const void* STDMETHODCALLTYPE MessageSendPInvokeOverride(_In_z_ const char* libraryName, _In_z_ const char* entrypointName)
+ {
+ // All overrides are in libobjc
+ if (strcmp(libraryName, ObjectiveCLibrary) != 0)
+ return nullptr;
+
+ // All overrides start with objc_msgSend
+ if (strncmp(entrypointName, OBJC_MSGSEND, _countof(OBJC_MSGSEND) -1) != 0)
+ return nullptr;
+
+ for (int i = 0; i < _countof(MsgSendEntryPoints); ++i)
+ {
+ void* funcMaybe = s_msgSendOverrides[i];
+ if (funcMaybe != nullptr
+ && strcmp(entrypointName, MsgSendEntryPoints[i]) == 0)
+ {
+ return funcMaybe;
+ }
+ }
+
+ return nullptr;
+ }
+}
+
+BOOL QCALLTYPE ObjCMarshalNative::TrySetGlobalMessageSendCallback(
+ _In_ MessageSendFunction msgSendFunction,
+ _In_ void* fptr)
+{
+ QCALL_CONTRACT;
+
+ bool success;
+
+ BEGIN_QCALL;
+
+ _ASSERTE(msgSendFunction >= 0 && msgSendFunction < _countof(s_msgSendOverrides));
+ success = FastInterlockCompareExchangePointer(&s_msgSendOverrides[msgSendFunction], fptr, NULL) == NULL;
+
+ // Set P/Invoke override callback if we haven't already
+ if (success && FALSE == FastInterlockCompareExchange((LONG*)&s_msgSendOverridden, TRUE, FALSE))
+ PInvokeOverride::SetPInvokeOverride(&MessageSendPInvokeOverride, PInvokeOverride::Source::ObjectiveCInterop);
+
+ END_QCALL;
+
+ return success ? TRUE : FALSE;
+}
+
+namespace
+{
+ bool TryGetTaggedMemory(_In_ OBJECTREF object, _Out_ void** tagged)
+ {
+ CONTRACTL
+ {
+ NOTHROW;
+ GC_NOTRIGGER;
+ PRECONDITION(CheckPointer(tagged));
+ }
+ CONTRACTL_END;
+
+ SyncBlock* syncBlock = object->PassiveGetSyncBlock();
+ if (syncBlock == NULL)
+ return false;
+
+ InteropSyncBlockInfo* interopInfo = syncBlock->GetInteropInfoNoCreate();
+ if (interopInfo == NULL)
+ return false;
+
+ // If no tagged memory is allocated, then the instance is not
+ // being tracked.
+ void* taggedLocal = interopInfo->GetTaggedMemory();
+ if (taggedLocal == NULL)
+ return false;
+
+ *tagged = taggedLocal;
+ return true;
+ }
+}
+
+bool ObjCMarshalNative::IsTrackedReference(_In_ OBJECTREF object, _Out_ bool* isReferenced)
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ GC_NOTRIGGER;
+ PRECONDITION(CheckPointer(isReferenced));
+ }
+ CONTRACTL_END;
+
+ *isReferenced = false;
+
+ void* taggedMemory;
+ if (!TryGetTaggedMemory(object, &taggedMemory))
+ return false;
+
+ _ASSERTE(g_IsReferencedCallback != NULL);
+ int result = g_IsReferencedCallback(taggedMemory);
+
+ *isReferenced = (result != 0);
+ return true;
+}
+
+bool ObjCMarshalNative::IsRuntimeMsgSendFunctionOverridden(
+ _In_z_ const char* libraryName,
+ _In_z_ const char* entrypointName)
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ GC_NOTRIGGER;
+ PRECONDITION(libraryName != NULL);
+ PRECONDITION(entrypointName != NULL);
+ }
+ CONTRACTL_END;
+
+ return MessageSendPInvokeOverride(libraryName, entrypointName) != NULL;
+}
+
+namespace
+{
+ bool CallAvailableUnhandledExceptionPropagation()
+ {
+ CONTRACTL
+ {
+ THROWS;
+ MODE_COOPERATIVE;
+ }
+ CONTRACTL_END;
+
+ MethodDescCallSite dispatch(METHOD__OBJCMARSHAL__AVAILABLEUNHANDLEDEXCEPTIONPROPAGATION);
+ return dispatch.Call_RetBool(NULL);
+ }
+
+ void* CallInvokeUnhandledExceptionPropagation(
+ _In_ OBJECTREF* exceptionPROTECTED,
+ _In_ REFLECTMETHODREF* methodRefPROTECTED,
+ _Outptr_ void** callbackContext)
+ {
+ CONTRACTL
+ {
+ THROWS;
+ MODE_COOPERATIVE;
+ PRECONDITION(exceptionPROTECTED != NULL);
+ PRECONDITION(methodRefPROTECTED != NULL);
+ PRECONDITION(callbackContext != NULL);
+ }
+ CONTRACTL_END;
+
+ void* callback = NULL;
+ *callbackContext = NULL;
+
+ PREPARE_NONVIRTUAL_CALLSITE(METHOD__OBJCMARSHAL__INVOKEUNHANDLEDEXCEPTIONPROPAGATION);
+ DECLARE_ARGHOLDER_ARRAY(args, 3);
+ args[ARGNUM_0] = OBJECTREF_TO_ARGHOLDER(*exceptionPROTECTED);
+ args[ARGNUM_1] = OBJECTREF_TO_ARGHOLDER(*methodRefPROTECTED);
+ args[ARGNUM_2] = PTR_TO_ARGHOLDER(callbackContext);
+ CALL_MANAGED_METHOD(callback, void*, args);
+
+ return callback;
+ }
+}
+
+void* ObjCMarshalNative::GetPropagatingExceptionCallback(
+ _In_ EECodeInfo* codeInfo,
+ _In_ OBJECTHANDLE throwable,
+ _Outptr_ void** context)
+{
+ CONTRACT(void*)
+ {
+ THROWS;
+ MODE_PREEMPTIVE;
+ PRECONDITION(codeInfo != NULL);
+ PRECONDITION(throwable != NULL);
+ PRECONDITION(context != NULL);
+ }
+ CONTRACT_END;
+
+ void* callback = NULL;
+ void* callbackContext = NULL;
+
+ MethodDesc* method = codeInfo->GetMethodDesc();
+
+ // If this is a dynamic method, let's see if we can
+ // resolve it to its target.
+ if (method->IsDynamicMethod())
+ {
+ DynamicMethodDesc* dynamicMethod = method->AsDynamicMethodDesc();
+ ILStubResolver* resolver = dynamicMethod->GetILStubResolver();
+ MethodDesc* methodMaybe = resolver->GetStubTargetMethodDesc();
+ if (methodMaybe != NULL)
+ method = methodMaybe;
+ }
+
+ {
+ GCX_COOP();
+ struct
+ {
+ OBJECTREF throwableRef;
+ REFLECTMETHODREF methodRef;
+ } gc;
+ ::ZeroMemory(&gc, sizeof(gc));
+ GCPROTECT_BEGIN(gc);
+
+ // Creating the StubMethodInfo isn't cheap, so check
+ // if there are any handlers prior to dispatching.
+ if (CallAvailableUnhandledExceptionPropagation())
+ {
+ gc.throwableRef = ObjectFromHandle(throwable);
+ gc.methodRef = method->GetStubMethodInfo();
+
+ callback = CallInvokeUnhandledExceptionPropagation(
+ &gc.throwableRef,
+ &gc.methodRef,
+ &callbackContext);
+ }
+
+ GCPROTECT_END();
+ }
+
+ *context = callbackContext;
+ RETURN callback;
+}
+
+void ObjCMarshalNative::BeforeRefCountedHandleCallbacks()
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ GC_NOTRIGGER;
+ }
+ CONTRACTL_END;
+
+ if (g_BeginEndCallback != NULL)
+ g_BeginEndCallback();
+}
+
+void ObjCMarshalNative::AfterRefCountedHandleCallbacks()
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ GC_NOTRIGGER;
+ }
+ CONTRACTL_END;
+
+ if (g_BeginEndCallback != NULL)
+ g_BeginEndCallback();
+}
+
+void ObjCMarshalNative::OnEnteredFinalizerQueue(_In_ OBJECTREF object)
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ GC_NOTRIGGER;
+ }
+ CONTRACTL_END;
+
+ void* taggedMemory;
+ if (!TryGetTaggedMemory(object, &taggedMemory))
+ return;
+
+ _ASSERTE(g_TrackedObjectEnteredFinalizationCallback != NULL);
+ g_TrackedObjectEnteredFinalizationCallback(taggedMemory);
+}
+
+#endif // FEATURE_OBJCMARSHAL
diff --git a/src/coreclr/vm/interoplibinterface_shared.cpp b/src/coreclr/vm/interoplibinterface_shared.cpp
new file mode 100644
index 00000000000..a36db92805a
--- /dev/null
+++ b/src/coreclr/vm/interoplibinterface_shared.cpp
@@ -0,0 +1,144 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+// Runtime headers
+#include "common.h"
+
+#include "interoplibinterface.h"
+
+using ManagedToNativeExceptionCallback = Interop::ManagedToNativeExceptionCallback;
+
+bool Interop::ShouldCheckForPendingException(_In_ NDirectMethodDesc* md)
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ GC_NOTRIGGER;
+ PRECONDITION(md != NULL);
+ }
+ CONTRACTL_END;
+
+#ifdef FEATURE_OBJCMARSHAL
+ PTR_CUTF8 libraryName = md->GetLibNameRaw();
+ PTR_CUTF8 entrypointName = md->GetEntrypointName();
+ if (libraryName == NULL || entrypointName == NULL)
+ return false;
+
+ if (ObjCMarshalNative::IsRuntimeMsgSendFunctionOverridden(libraryName, entrypointName))
+ return true;
+#endif // FEATURE_OBJCMARSHAL
+
+ return false;
+}
+
+ManagedToNativeExceptionCallback Interop::GetPropagatingExceptionCallback(
+ _In_ EECodeInfo* codeInfo,
+ _In_ OBJECTHANDLE throwable,
+ _Outptr_ void** context)
+{
+ CONTRACT(ManagedToNativeExceptionCallback)
+ {
+ NOTHROW;
+ MODE_PREEMPTIVE;
+ PRECONDITION(codeInfo != NULL);
+ PRECONDITION(throwable != NULL);
+ PRECONDITION(context != NULL);
+ }
+ CONTRACT_END;
+
+ ManagedToNativeExceptionCallback callback = NULL;
+ *context = NULL;
+
+#ifdef FEATURE_OBJCMARSHAL
+ EX_TRY
+ {
+ callback = (ManagedToNativeExceptionCallback)ObjCMarshalNative::GetPropagatingExceptionCallback(
+ codeInfo,
+ throwable,
+ context);
+ }
+ EX_CATCH
+ {
+ EEPOLICY_HANDLE_FATAL_ERROR_WITH_MESSAGE(
+ COR_E_EXECUTIONENGINE,
+ W("Unhandled managed exception handler threw an exception."));
+ }
+ EX_END_CATCH_UNREACHABLE;
+#endif // FEATURE_OBJCMARSHAL
+
+ RETURN callback;
+}
+
+void Interop::OnGCStarted(_In_ int nCondemnedGeneration)
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ GC_NOTRIGGER;
+ }
+ CONTRACTL_END;
+
+ //
+ // Note that we could get nested GCStart/GCEnd calls, such as:
+ // GCStart for Gen 2 background GC
+ // GCStart for Gen 0/1 foreground GC
+ // GCEnd for Gen 0/1 foreground GC
+ // ...
+ // GCEnd for Gen 2 background GC
+ //
+ // The nCondemnedGeneration >= 2 check takes care of this nesting problem
+ //
+ // See Interop::OnGCFinished()
+ if (nCondemnedGeneration >= 2)
+ {
+#ifdef FEATURE_COMWRAPPERS
+ ComWrappersNative::OnFullGCStarted();
+#endif // FEATURE_COMWRAPPERS
+ }
+}
+
+void Interop::OnGCFinished(_In_ int nCondemnedGeneration)
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ GC_NOTRIGGER;
+ }
+ CONTRACTL_END;
+
+ // See Interop::OnGCStarted()
+ if (nCondemnedGeneration >= 2)
+ {
+#ifdef FEATURE_COMWRAPPERS
+ ComWrappersNative::OnFullGCFinished();
+#endif // FEATURE_COMWRAPPERS
+ }
+}
+
+void Interop::OnBeforeGCScanRoots()
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ GC_NOTRIGGER;
+ }
+ CONTRACTL_END;
+
+#ifdef FEATURE_OBJCMARSHAL
+ ObjCMarshalNative::BeforeRefCountedHandleCallbacks();
+#endif // FEATURE_OBJCMARSHAL
+}
+
+void Interop::OnAfterGCScanRoots()
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ GC_NOTRIGGER;
+ }
+ CONTRACTL_END;
+
+#ifdef FEATURE_OBJCMARSHAL
+ ObjCMarshalNative::AfterRefCountedHandleCallbacks();
+#endif // FEATURE_OBJCMARSHAL
+}
diff --git a/src/coreclr/vm/metasig.h b/src/coreclr/vm/metasig.h
index 83af0763e2e..3716ca9fd7b 100644
--- a/src/coreclr/vm/metasig.h
+++ b/src/coreclr/vm/metasig.h
@@ -185,11 +185,16 @@ DEFINE_METASIG(SM(IntPtr_IntPtr_RefIntPtr_RetObj, I I r(I), j))
#ifdef FEATURE_COMINTEROP
DEFINE_METASIG(SM(Obj_IntPtr_RefIntPtr_RefBool_RetIntPtr, j I r(I) r(F), I))
DEFINE_METASIG(SM(Obj_IntPtr_RefIntPtr_RetIntPtr, j I r(I), I))
+#endif // FEATURE_COMINTEROP
+#ifdef FEATURE_COMWRAPPERS
DEFINE_METASIG_T(SM(Scenario_ComWrappers_Obj_CreateFlags_RefInt_RetPtrVoid, g(COMWRAPPERSSCENARIO) C(COMWRAPPERS) j g(CREATECOMINTERFACEFLAGS) r(i), P(v)))
DEFINE_METASIG_T(SM(Scenario_ComWrappers_IntPtr_CreateFlags_RetObj, g(COMWRAPPERSSCENARIO) C(COMWRAPPERS) I g(CREATEOBJECTFLAGS), j))
DEFINE_METASIG_T(SM(ComWrappers_IEnumerable_RetVoid, C(COMWRAPPERS) C(IENUMERABLE), v))
DEFINE_METASIG_T(SM(Obj_RefGuid_RefIntPtr_RetInt, j r(g(GUID)) r(I), i))
-#endif // FEATURE_COMINTEROP
+#endif // FEATURE_COMWRAPPERS
+#ifdef FEATURE_OBJCMARSHAL
+DEFINE_METASIG_T(SM(Exception_Obj_RefIntPtr_RetVoidPtr, C(EXCEPTION) j r(I), P(v)))
+#endif // FEATURE_OBJCMARSHAL
DEFINE_METASIG(SM(Int_RetVoid, i, v))
DEFINE_METASIG(SM(Int_Int_RetVoid, i i, v))
DEFINE_METASIG(SM(Str_RetIntPtr, s, I))
@@ -313,6 +318,7 @@ DEFINE_METASIG(SM(Dbl_RetLong, d, l))
DEFINE_METASIG(SM(IntPtr_RetObj, I, j))
DEFINE_METASIG_T(SM(Int_RetException, i, C(EXCEPTION)))
+DEFINE_METASIG_T(SM(RetException, _, C(EXCEPTION)))
DEFINE_METASIG(SM(Int_IntPtr_RetObj, i I, j))
DEFINE_METASIG(SM(IntPtr_IntPtr_Int_RetVoid, I I i, v))
DEFINE_METASIG_T(SM(Exception_RetInt, C(EXCEPTION), i))
diff --git a/src/coreclr/vm/methodtable.cpp b/src/coreclr/vm/methodtable.cpp
index a6f1048333e..9f112a4e8d4 100644
--- a/src/coreclr/vm/methodtable.cpp
+++ b/src/coreclr/vm/methodtable.cpp
@@ -617,6 +617,18 @@ BOOL MethodTable::IsIDynamicInterfaceCastable()
return GetFlag(enum_flag_IDynamicInterfaceCastable);
}
+void MethodTable::SetIsTrackedReferenceWithFinalizer()
+{
+ LIMITED_METHOD_CONTRACT;
+ SetFlag(enum_flag_IsTrackedReferenceWithFinalizer);
+}
+
+BOOL MethodTable::IsTrackedReferenceWithFinalizer()
+{
+ LIMITED_METHOD_DAC_CONTRACT;
+ return GetFlag(enum_flag_IsTrackedReferenceWithFinalizer);
+}
+
#endif // !DACCESS_COMPILE
//==========================================================================================
diff --git a/src/coreclr/vm/methodtable.h b/src/coreclr/vm/methodtable.h
index 4f05c6a1868..604ca40b34f 100644
--- a/src/coreclr/vm/methodtable.h
+++ b/src/coreclr/vm/methodtable.h
@@ -766,6 +766,9 @@ public:
void SetIDynamicInterfaceCastable();
BOOL IsIDynamicInterfaceCastable();
+ void SetIsTrackedReferenceWithFinalizer();
+ BOOL IsTrackedReferenceWithFinalizer();
+
#ifdef FEATURE_TYPEEQUIVALENCE
// mark the type as opted into type equivalence
void SetHasTypeEquivalence()
@@ -3602,7 +3605,7 @@ private:
enum_flag_HasTypeEquivalence = 0x02000000, // can be equivalent to another type
- // enum_flag_unused = 0x04000000,
+ enum_flag_IsTrackedReferenceWithFinalizer = 0x04000000,
enum_flag_HasCriticalFinalizer = 0x08000000, // finalizer must be run on Appdomain Unload
enum_flag_Collectible = 0x10000000,
diff --git a/src/coreclr/vm/methodtablebuilder.cpp b/src/coreclr/vm/methodtablebuilder.cpp
index ea8d9425889..d8793ce6842 100644
--- a/src/coreclr/vm/methodtablebuilder.cpp
+++ b/src/coreclr/vm/methodtablebuilder.cpp
@@ -1973,6 +1973,33 @@ MethodTableBuilder::BuildMethodTableThrowing(
}
}
+#ifdef FEATURE_OBJCMARSHAL
+ // Check if this type has a finalizer and then if it is a referenced tracked type.
+ if (pMT->HasFinalizer() && !IsValueClass() && !IsInterface() && !IsDelegate())
+ {
+ BOOL isTrackedReference = FALSE;
+ if (HasParent())
+ {
+ MethodTable * pParentClass = GetParentMethodTable();
+ PREFIX_ASSUME(pParentClass != NULL);
+ isTrackedReference = pParentClass->IsTrackedReferenceWithFinalizer();
+ }
+
+ if (!isTrackedReference)
+ {
+ HRESULT hr = GetCustomAttribute(bmtInternal->pType->GetTypeDefToken(),
+ WellKnownAttribute::ObjectiveCTrackedTypeAttribute,
+ NULL,
+ NULL);
+
+ isTrackedReference = hr == S_OK ? TRUE : FALSE;
+ }
+
+ if (isTrackedReference)
+ pMT->SetIsTrackedReferenceWithFinalizer();
+ }
+#endif // FEATURE_OBJCMARSHAL
+
// Grow the typedef ridmap in advance as we can't afford to
// fail once we set the resolve bit
pModule->EnsureTypeDefCanBeStored(bmtInternal->pType->GetTypeDefToken());
diff --git a/src/coreclr/vm/namespace.h b/src/coreclr/vm/namespace.h
index 1cb2b1b9234..3b1c409a9c5 100644
--- a/src/coreclr/vm/namespace.h
+++ b/src/coreclr/vm/namespace.h
@@ -32,7 +32,7 @@
#define g_ReflectionEmitNS g_ReflectionNS ".Emit"
#define g_InteropNS g_RuntimeNS ".InteropServices"
-#define g_InteropTCENS g_InteropNS ".TCEAdapterGen"
+#define g_ObjectiveCNS g_InteropNS ".ObjectiveC"
#define g_IntrinsicsNS g_RuntimeNS ".Intrinsics"
#define g_NumericsNS g_SystemNS ".Numerics"
diff --git a/src/coreclr/vm/pinvokeoverride.cpp b/src/coreclr/vm/pinvokeoverride.cpp
index 2f4f0924142..a2f32310fb4 100644
--- a/src/coreclr/vm/pinvokeoverride.cpp
+++ b/src/coreclr/vm/pinvokeoverride.cpp
@@ -12,7 +12,11 @@
extern "C" const void* GlobalizationResolveDllImport(const char* name);
-static PInvokeOverrideFn* s_overrideImpl = nullptr;
+namespace
+{
+ PInvokeOverrideFn* s_overrideImpls[(int)PInvokeOverride::Source::Last + 1] = {0};
+ bool s_hasOverrides = false;
+}
#if defined(_WIN32)
#define GLOBALIZATION_DLL_NAME "System.Globalization.Native"
@@ -31,20 +35,29 @@ static const void* DefaultResolveDllImport(const char* libraryName, const char*
return nullptr;
}
-void PInvokeOverride::SetPInvokeOverride(PInvokeOverrideFn* overrideImpl)
+void PInvokeOverride::SetPInvokeOverride(PInvokeOverrideFn* overrideImpl, Source source)
{
- s_overrideImpl = overrideImpl;
+ _ASSERTE(s_overrideImpls[(int)source] == NULL);
+ s_overrideImpls[(int)source] = overrideImpl;
+ s_hasOverrides = true;
}
const void* PInvokeOverride::GetMethodImpl(const char* libraryName, const char* entrypointName)
{
- if (s_overrideImpl != nullptr)
+ if (s_hasOverrides)
{
- const void* result = s_overrideImpl(libraryName, entrypointName);
- if (result != nullptr)
+ for (size_t i = 0; i < _countof(s_overrideImpls); ++i)
{
- LOG((LF_INTEROP, LL_INFO1000, "PInvoke overriden for: lib: %s, entry: %s \n", libraryName, entrypointName));
- return result;
+ PInvokeOverrideFn* overrideImpl = s_overrideImpls[i];
+ if (overrideImpl == nullptr)
+ continue;
+
+ const void* result = overrideImpl(libraryName, entrypointName);
+ if (result != nullptr)
+ {
+ LOG((LF_INTEROP, LL_INFO1000, "PInvoke overriden for: lib: %s, entry: %s \n", libraryName, entrypointName));
+ return result;
+ }
}
}
diff --git a/src/coreclr/vm/rcwrefcache.cpp b/src/coreclr/vm/rcwrefcache.cpp
index 61c0a90b352..4881691a0e2 100644
--- a/src/coreclr/vm/rcwrefcache.cpp
+++ b/src/coreclr/vm/rcwrefcache.cpp
@@ -16,8 +16,6 @@
#include "common.h"
-#include "comcallablewrapper.h"
-#include "runtimecallablewrapper.h"
#include "rcwrefcache.h"
// SHRINK_TOTAL_THRESHOLD - only shrink when the total number of handles exceed this number
diff --git a/src/coreclr/vm/syncblk.h b/src/coreclr/vm/syncblk.h
index 474e0f2b26e..8d001786cfc 100644
--- a/src/coreclr/vm/syncblk.h
+++ b/src/coreclr/vm/syncblk.h
@@ -772,6 +772,9 @@ public:
TADDR m_pRCW;
#endif
+#endif // FEATURE_COMINTEROP
+
+#if defined(FEATURE_COMWRAPPERS)
public:
bool TryGetManagedObjectComWrapper(_In_ INT64 wrapperId, _Out_ void** mocw)
{
@@ -882,8 +885,39 @@ private:
CrstExplicitInit m_managedObjectComWrapperLock;
NewHolder<ManagedObjectComWrapperByIdMap> m_managedObjectComWrapperMap;
-#endif // FEATURE_COMINTEROP
+#endif // FEATURE_COMWRAPPERS
+
+#ifdef FEATURE_OBJCMARSHAL
+public:
+ void* AllocTaggedMemory(_Out_ size_t* memoryInSizeT)
+ {
+ LIMITED_METHOD_CONTRACT;
+ _ASSERTE(memoryInSizeT != NULL);
+
+ *memoryInSizeT = _countof(m_taggedAlloc) / sizeof(SIZE_T);
+
+ // The allocation is meant to indicate that memory
+ // has been made available by the system. Calling the 'get'
+ // without allocating memory indicates there has been
+ // no request for reference tracking tagged memory.
+ m_taggedMemory = m_taggedAlloc;
+ return m_taggedMemory;
+ }
+
+ void* GetTaggedMemory()
+ {
+ LIMITED_METHOD_CONTRACT;
+ return m_taggedMemory;
+ }
+
+private:
+ void* m_taggedMemory;
+ // Two pointers worth of bytes of the requirement for
+ // the current consuming implementation so that is what
+ // is being allocated.
+ BYTE m_taggedAlloc[2 * sizeof(void*)];
+#endif // FEATURE_OBJCMARSHAL
};
typedef DPTR(InteropSyncBlockInfo) PTR_InteropSyncBlockInfo;
diff --git a/src/coreclr/vm/wellknownattributes.h b/src/coreclr/vm/wellknownattributes.h
index 1ab118e300a..8da3de283e1 100644
--- a/src/coreclr/vm/wellknownattributes.h
+++ b/src/coreclr/vm/wellknownattributes.h
@@ -35,6 +35,7 @@ enum class WellKnownAttribute : DWORD
ThreadStatic,
WinRTMarshalingBehaviorAttribute,
PreserveBaseOverridesAttribute,
+ ObjectiveCTrackedTypeAttribute,
CountOfWellKnownAttributes
};
@@ -101,6 +102,8 @@ inline const char *GetWellKnownAttributeName(WellKnownAttribute attribute)
return "Windows.Foundation.Metadata.MarshalingBehaviorAttribute";
case WellKnownAttribute::PreserveBaseOverridesAttribute:
return "System.Runtime.CompilerServices.PreserveBaseOverridesAttribute";
+ case WellKnownAttribute::ObjectiveCTrackedTypeAttribute:
+ return "System.Runtime.InteropServices.ObjectiveC.ObjectiveCTrackedTypeAttribute";
case WellKnownAttribute::CountOfWellKnownAttributes:
default:
break; // Silence compiler warnings
diff --git a/src/libraries/System.Private.CoreLib/src/Resources/Strings.resx b/src/libraries/System.Private.CoreLib/src/Resources/Strings.resx
index ebf8f105fe9..6e2123312c6 100644
--- a/src/libraries/System.Private.CoreLib/src/Resources/Strings.resx
+++ b/src/libraries/System.Private.CoreLib/src/Resources/Strings.resx
@@ -3622,6 +3622,18 @@
<data name="InvalidOperation_ResetGlobalComWrappersInstance" xml:space="preserve">
<value>Attempt to update previously set global instance.</value>
</data>
+ <data name="InvalidOperation_ResetGlobalObjectiveCMsgSend" xml:space="preserve">
+ <value>Attempt to update previously set Objective-C msgSend API overrides.</value>
+ </data>
+ <data name="InvalidOperation_ObjectiveCMarshalNotInitialized" xml:space="preserve">
+ <value>Attempt to track an Objective-C Type without initializing.</value>
+ </data>
+ <data name="InvalidOperation_ReinitializeObjectiveCMarshal" xml:space="preserve">
+ <value>Attempt to reinitialize ObjectiveCMarshal.</value>
+ </data>
+ <data name="InvalidOperation_ObjectiveCTypeNoFinalizer" xml:space="preserve">
+ <value>Attempt to track an Objective-C Type without a finalizer.</value>
+ </data>
<data name="InvalidOperation_SuppliedInnerMustBeMarkedAggregation" xml:space="preserve">
<value>Supplying a non-null inner should also be marked as Aggregated.</value>
</data>
diff --git a/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems b/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems
index 610a9ba9b8f..6272a772fe2 100644
--- a/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems
+++ b/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems
@@ -1935,8 +1935,6 @@
<Compile Include="$(CommonPath)Interop\OSX\Interop.Libraries.cs">
<Link>Common\Interop\OSX\Interop.Libraries.cs</Link>
</Compile>
- <Compile Include="$(CommonPath)Interop\OSX\System.Native\Interop.AutoreleasePool.cs"
- Link="Common\Interop\OSX\System.Native\Interop.AutoreleasePool.cs" />
</ItemGroup>
<ItemGroup Condition="'$(TargetsMacCatalyst)' == 'true'">
<Compile Include="$(CommonPath)Interop\OSX\System.Native\Interop.iOSSupportVersion.cs"
@@ -2016,6 +2014,18 @@
<Compile Include="$(MSBuildThisFileDirectory)System\Threading\LowLevelLifoSemaphore.Windows.cs" Condition="'$(TargetsWindows)' == 'true'" />
<Compile Include="$(MSBuildThisFileDirectory)System\Threading\RegisteredWaitHandle.Portable.cs" />
</ItemGroup>
+ <ItemGroup Condition="'$(FeatureObjCMarshal)' == 'true'">
+ <Compile Include="$(MSBuildThisFileDirectory)System\Runtime\InteropServices\ObjectiveC\ObjectiveCMarshal.cs" />
+ <Compile Include="$(MSBuildThisFileDirectory)System\Runtime\InteropServices\ObjectiveC\ObjectiveCTrackedTypeAttribute.cs" />
+ <Compile Include="$(CommonPath)Interop\OSX\System.Native\Interop.AutoreleasePool.cs"
+ Link="Common\Interop\OSX\System.Native\Interop.AutoreleasePool.cs" />
+ <Compile Include="$(MSBuildThisFileDirectory)System\Threading\AutoreleasePool.cs" />
+ <Compile Include="$(MSBuildThisFileDirectory)System\Threading\ThreadPoolWorkQueue.AutoreleasePool.OSX.cs" />
+ </ItemGroup>
+ <ItemGroup Condition="'$(FeatureObjCMarshal)' != 'true'">
+ <Compile Include="$(MSBuildThisFileDirectory)System\Runtime\InteropServices\ObjectiveC\ObjectiveCMarshal.PlatformNotSupported.cs" />
+ <Compile Include="$(MSBuildThisFileDirectory)System\Runtime\InteropServices\ObjectiveC\ObjectiveCTrackedTypeAttribute.cs" />
+ </ItemGroup>
<ItemGroup Condition="'$(FeatureCoreCLR)' != 'true' and ('$(TargetsUnix)' == 'true' or '$(TargetsBrowser)' == 'true')">
<Compile Include="$(MSBuildThisFileDirectory)System\Threading\Thread.Unix.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Microsoft\Win32\SafeHandles\SafeWaitHandle.Unix.cs" />
@@ -2035,8 +2045,4 @@
<Link>Interop\Windows\Kernel32\Interop.Threading.cs</Link>
</Compile>
</ItemGroup>
- <ItemGroup Condition="'$(IsOSXLike)' == 'true'">
- <Compile Include="$(MSBuildThisFileDirectory)System\Threading\AutoreleasePool.cs" />
- <Compile Include="$(MSBuildThisFileDirectory)System\Threading\ThreadPoolWorkQueue.AutoreleasePool.OSX.cs" />
- </ItemGroup>
</Project>
diff --git a/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/ObjectiveC/ObjectiveCMarshal.PlatformNotSupported.cs b/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/ObjectiveC/ObjectiveCMarshal.PlatformNotSupported.cs
new file mode 100644
index 00000000000..c9c94991676
--- /dev/null
+++ b/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/ObjectiveC/ObjectiveCMarshal.PlatformNotSupported.cs
@@ -0,0 +1,146 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System.Runtime.Versioning;
+using System.Runtime.CompilerServices;
+
+namespace System.Runtime.InteropServices.ObjectiveC
+{
+ /// <summary>
+ /// API to enable Objective-C marshalling.
+ /// </summary>
+ [SupportedOSPlatform("macos")]
+ [CLSCompliant(false)]
+ public static class ObjectiveCMarshal
+ {
+ /// <summary>
+ /// Handler for unhandled Exceptions crossing the managed -> native boundary (that is, Reverse P/Invoke).
+ /// </summary>
+ /// <param name="exception">Unhandled exception.</param>
+ /// <param name="lastMethod">Last managed method.</param>
+ /// <param name="context">Context provided to the returned function pointer.</param>
+ /// <returns>Exception propagation callback.</returns>
+ /// <remarks>
+ /// If the handler is able to propagate the managed Exception properly to the native environment an
+ /// unmanaged callback can be returned, otherwise <c>null</c>. The <see cref="RuntimeMethodHandle"/> is to the
+ /// last managed method that was executed prior to leaving the runtime. Along with the returned callback
+ /// the handler can return a context that will be passed to the callback during dispatch.
+ ///
+ /// The returned handler will be passed the context when called and it is the responsibility of the callback
+ /// to manage. The handler must not return and is expected to propagate the exception (for example, throw a native exception)
+ /// into the native environment or fail fast.
+ /// </remarks>
+ public unsafe delegate delegate* unmanaged<IntPtr, void> UnhandledExceptionPropagationHandler(
+ Exception exception,
+ RuntimeMethodHandle lastMethod,
+ out IntPtr context);
+
+ /// <summary>
+ /// Initialize the Objective-C marshalling API.
+ /// </summary>
+ /// <param name="beginEndCallback">Called when tracking begins and ends.</param>
+ /// <param name="isReferencedCallback">Called to determine if a managed object instance is referenced elsewhere, and must not be collected by the GC.</param>
+ /// <param name="trackedObjectEnteredFinalization">Called when a tracked object enters the finalization queue.</param>
+ /// <param name="unhandledExceptionPropagationHandler">Handler for the propagation of unhandled Exceptions across a managed -> native boundary (that is, Reverse P/Invoke).</param>
+ /// <exception cref="InvalidOperationException">Thrown if this API has already been called.</exception>
+ /// <remarks>
+ /// All unmanaged function pointers must be written in native code since they will be called by the GC and
+ /// managed code is not able to run at that time.
+ ///
+ /// The <paramref name="beginEndCallback"/> will be called when reference tracking begins and ends.
+ /// The associated begin/end pair will never be nested. When using Workstation GC, the begin/end pair
+ /// will be called on the same thread. When using Server GC, the begin/end pair is not guaranteed to
+ /// be called on the same thread.
+ ///
+ /// The <paramref name="isReferencedCallback"/> should return 0 for not reference or 1 for
+ /// referenced. Any other value has undefined behavior.
+ /// </remarks>
+ public static unsafe void Initialize(
+ delegate* unmanaged<void> beginEndCallback,
+ delegate* unmanaged<IntPtr, int> isReferencedCallback,
+ delegate* unmanaged<IntPtr, void> trackedObjectEnteredFinalization,
+ UnhandledExceptionPropagationHandler unhandledExceptionPropagationHandler)
+ => throw new PlatformNotSupportedException();
+
+ /// <summary>
+ /// Request native reference tracking for the supplied object.
+ /// </summary>
+ /// <param name="obj">The object to track.</param>
+ /// <param name="taggedMemory">A pointer to memory tagged to the object.</param>
+ /// <returns>Reference tracking GC handle.</returns>
+ /// <exception cref="InvalidOperationException">Thrown if the ObjectiveCMarshal API has not been initialized.</exception>
+ /// <remarks>
+ /// The Initialize() must be called prior to calling this function.
+ ///
+ /// The <paramref name="obj"/> must have a type in its hierarchy marked with
+ /// <see cref="ObjectiveCTrackedTypeAttribute"/>.
+ ///
+ /// The "Is Referenced" callback passed to Initialize()
+ /// will be passed the <paramref name="taggedMemory"/> 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 memory pointed to by <paramref name="taggedMemory"/> 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 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 caller is responsible for freeing the returned <see cref="GCHandle"/>.
+ /// </remarks>
+ public static GCHandle CreateReferenceTrackingHandle(
+ object obj,
+ out Span<IntPtr> taggedMemory)
+ => throw new PlatformNotSupportedException();
+
+ /// <summary>
+ /// Objective-C msgSend function override options.
+ /// </summary>
+ public enum MessageSendFunction
+ {
+ /// <summary>
+ /// Overrides the Objective-C runtime's <see href="https://developer.apple.com/documentation/objectivec/1456712-objc_msgsend">msgSend()</see>.
+ /// </summary>
+ MsgSend,
+ /// <summary>
+ /// Overrides the Objective-C runtime's <see href="https://developer.apple.com/documentation/objectivec/1456697-objc_msgsend_fpret">objc_msgSend_fpret()</see>.
+ /// </summary>
+ MsgSendFpret,
+ /// <summary>
+ /// Overrides the Objective-C runtime's <see href="https://developer.apple.com/documentation/objectivec/1456730-objc_msgsend_stret">objc_msgSend_stret()</see>.
+ /// </summary>
+ MsgSendStret,
+ /// <summary>
+ /// Overrides the Objective-C runtime's <see href="https://developer.apple.com/documentation/objectivec/1456716-objc_msgsendsuper">objc_msgSendSuper()</see>.
+ /// </summary>
+ MsgSendSuper,
+ /// <summary>
+ /// Overrides the Objective-C runtime's <see href="https://developer.apple.com/documentation/objectivec/1456722-objc_msgsendsuper_stret">objc_msgSendSuper_stret()</see>.
+ /// </summary>
+ MsgSendSuperStret,
+ }
+
+ /// <summary>
+ /// Set a function pointer override for an Objective-C runtime message passing export.
+ /// </summary>
+ /// <param name="msgSendFunction">The export to override.</param>
+ /// <param name="func">The function override.</param>
+ /// <exception cref="InvalidOperationException">Thrown if the msgSend function has already been overridden.</exception>
+ /// <remarks>
+ /// Providing an override can enable support for Objective-C
+ /// exception propagation and variadic argument support.
+ /// </remarks>
+ public static void SetMessageSendCallback(MessageSendFunction msgSendFunction, IntPtr func)
+ => throw new PlatformNotSupportedException();
+
+ /// <summary>
+ /// Sets a pending exception to be thrown the next time the runtime is entered from an overridden msgSend P/Invoke.
+ /// </summary>
+ /// <param name="exception">The exception.</param>
+ /// <remarks>
+ /// If <c>null</c> is supplied any pending exception is discarded.
+ /// </remarks>
+ public static void SetMessageSendPendingException(Exception? exception)
+ => throw new PlatformNotSupportedException();
+ }
+}
diff --git a/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/ObjectiveC/ObjectiveCMarshal.cs b/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/ObjectiveC/ObjectiveCMarshal.cs
new file mode 100644
index 00000000000..ddc1ff7327e
--- /dev/null
+++ b/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/ObjectiveC/ObjectiveCMarshal.cs
@@ -0,0 +1,178 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System.Runtime.Versioning;
+using System.Runtime.CompilerServices;
+
+namespace System.Runtime.InteropServices.ObjectiveC
+{
+ /// <summary>
+ /// API to enable Objective-C marshalling.
+ /// </summary>
+ [SupportedOSPlatform("macos")]
+ [CLSCompliant(false)]
+ public static partial class ObjectiveCMarshal
+ {
+ /// <summary>
+ /// Handler for unhandled Exceptions crossing the managed -> native boundary (that is, Reverse P/Invoke).
+ /// </summary>
+ /// <param name="exception">Unhandled exception.</param>
+ /// <param name="lastMethod">Last managed method.</param>
+ /// <param name="context">Context provided to the returned function pointer.</param>
+ /// <returns>Exception propagation callback.</returns>
+ /// <remarks>
+ /// If the handler is able to propagate the managed Exception properly to the native environment an
+ /// unmanaged callback can be returned, otherwise <c>null</c>. The <see cref="RuntimeMethodHandle"/> is to the
+ /// last managed method that was executed prior to leaving the runtime. Along with the returned callback
+ /// the handler can return a context that will be passed to the callback during dispatch.
+ ///
+ /// The returned handler will be passed the context when called and it is the responsibility of the callback
+ /// to manage. The handler must not return and is expected to propagate the exception (for example, throw a native exception)
+ /// into the native environment or fail fast.
+ /// </remarks>
+ public unsafe delegate delegate* unmanaged<IntPtr, void> UnhandledExceptionPropagationHandler(
+ Exception exception,
+ RuntimeMethodHandle lastMethod,
+ out IntPtr context);
+
+ private static UnhandledExceptionPropagationHandler? s_unhandledExceptionPropagationHandler;
+
+ /// <summary>
+ /// Initialize the Objective-C marshalling API.
+ /// </summary>
+ /// <param name="beginEndCallback">Called when tracking begins and ends.</param>
+ /// <param name="isReferencedCallback">Called to determine if a managed object instance is referenced elsewhere, and must not be collected by the GC.</param>
+ /// <param name="trackedObjectEnteredFinalization">Called when a tracked object enters the finalization queue.</param>
+ /// <param name="unhandledExceptionPropagationHandler">Handler for the propagation of unhandled Exceptions across a managed -> native boundary (that is, Reverse P/Invoke).</param>
+ /// <exception cref="InvalidOperationException">Thrown if this API has already been called.</exception>
+ /// <remarks>
+ /// All unmanaged function pointers must be written in native code since they will be called by the GC and
+ /// managed code is not able to run at that time.
+ ///
+ /// The <paramref name="beginEndCallback"/> will be called when reference tracking begins and ends.
+ /// The associated begin/end pair will never be nested. When using Workstation GC, the begin/end pair
+ /// will be called on the same thread. When using Server GC, the begin/end pair is not guaranteed to
+ /// be called on the same thread.
+ ///
+ /// The <paramref name="isReferencedCallback"/> should return 0 for not reference or 1 for
+ /// referenced. Any other value has undefined behavior.
+ /// </remarks>
+ public static unsafe void Initialize(
+ delegate* unmanaged<void> beginEndCallback,
+ delegate* unmanaged<IntPtr, int> isReferencedCallback,
+ delegate* unmanaged<IntPtr, void> trackedObjectEnteredFinalization,
+ UnhandledExceptionPropagationHandler unhandledExceptionPropagationHandler)
+ {
+ if (beginEndCallback is null)
+ throw new ArgumentNullException(nameof(beginEndCallback));
+ if (isReferencedCallback is null)
+ throw new ArgumentNullException(nameof(isReferencedCallback));
+ if (trackedObjectEnteredFinalization is null)
+ throw new ArgumentNullException(nameof(trackedObjectEnteredFinalization));
+ if (unhandledExceptionPropagationHandler is null)
+ throw new ArgumentNullException(nameof(unhandledExceptionPropagationHandler));
+
+ if (s_unhandledExceptionPropagationHandler != null
+ || !TryInitializeReferenceTracker(beginEndCallback, isReferencedCallback, trackedObjectEnteredFinalization))
+ {
+ throw new InvalidOperationException(SR.InvalidOperation_ReinitializeObjectiveCMarshal);
+ }
+ s_unhandledExceptionPropagationHandler = unhandledExceptionPropagationHandler;
+ }
+
+ /// <summary>
+ /// Request native reference tracking for the supplied object.
+ /// </summary>
+ /// <param name="obj">The object to track.</param>
+ /// <param name="taggedMemory">A pointer to memory tagged to the object.</param>
+ /// <returns>Reference tracking GC handle.</returns>
+ /// <exception cref="InvalidOperationException">Thrown if the ObjectiveCMarshal API has not been initialized.</exception>
+ /// <remarks>
+ /// The Initialize() must be called prior to calling this function.
+ ///
+ /// The <paramref name="obj"/> must have a type in its hierarchy marked with
+ /// <see cref="ObjectiveCTrackedTypeAttribute"/>.
+ ///
+ /// The "Is Referenced" callback passed to Initialize()
+ /// will be passed the <paramref name="taggedMemory"/> 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 memory pointed to by <paramref name="taggedMemory"/> 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 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 caller is responsible for freeing the returned <see cref="GCHandle"/>.
+ /// </remarks>
+ public static GCHandle CreateReferenceTrackingHandle(
+ object obj,
+ out Span<IntPtr> taggedMemory)
+ {
+ if (obj is null)
+ throw new ArgumentNullException(nameof(obj));
+
+ IntPtr refCountHandle = CreateReferenceTrackingHandleInternal(
+ ObjectHandleOnStack.Create(ref obj),
+ out int memInSizeT,
+ out IntPtr mem);
+
+ unsafe
+ {
+ taggedMemory = new Span<IntPtr>(mem.ToPointer(), memInSizeT);
+ }
+
+ return GCHandle.FromIntPtr(refCountHandle);
+ }
+
+ /// <summary>
+ /// Objective-C msgSend function override options.
+ /// </summary>
+ public enum MessageSendFunction
+ {
+ /// <summary>
+ /// Overrides the Objective-C runtime's <see href="https://developer.apple.com/documentation/objectivec/1456712-objc_msgsend">msgSend()</see>.
+ /// </summary>
+ MsgSend,
+ /// <summary>
+ /// Overrides the Objective-C runtime's <see href="https://developer.apple.com/documentation/objectivec/1456697-objc_msgsend_fpret">objc_msgSend_fpret()</see>.
+ /// </summary>
+ MsgSendFpret,
+ /// <summary>
+ /// Overrides the Objective-C runtime's <see href="https://developer.apple.com/documentation/objectivec/1456730-objc_msgsend_stret">objc_msgSend_stret()</see>.
+ /// </summary>
+ MsgSendStret,
+ /// <summary>
+ /// Overrides the Objective-C runtime's <see href="https://developer.apple.com/documentation/objectivec/1456716-objc_msgsendsuper">objc_msgSendSuper()</see>.
+ /// </summary>
+ MsgSendSuper,
+ /// <summary>
+ /// Overrides the Objective-C runtime's <see href="https://developer.apple.com/documentation/objectivec/1456722-objc_msgsendsuper_stret">objc_msgSendSuper_stret()</see>.
+ /// </summary>
+ MsgSendSuperStret,
+ }
+
+ /// <summary>
+ /// Set a function pointer override for an Objective-C runtime message passing export.
+ /// </summary>
+ /// <param name="msgSendFunction">The export to override.</param>
+ /// <param name="func">The function override.</param>
+ /// <exception cref="InvalidOperationException">Thrown if the msgSend function has already been overridden.</exception>
+ /// <remarks>
+ /// Providing an override can enable support for Objective-C
+ /// exception propagation and variadic argument support.
+ /// </remarks>
+ public static void SetMessageSendCallback(MessageSendFunction msgSendFunction, IntPtr func)
+ {
+ if (func == IntPtr.Zero)
+ throw new ArgumentNullException(nameof(func));
+
+ if (msgSendFunction < MessageSendFunction.MsgSend || msgSendFunction > MessageSendFunction.MsgSendSuperStret)
+ throw new ArgumentOutOfRangeException(nameof(msgSendFunction));
+
+ if (!TrySetGlobalMessageSendCallback(msgSendFunction, func))
+ throw new InvalidOperationException(SR.InvalidOperation_ResetGlobalObjectiveCMsgSend);
+ }
+ }
+}
diff --git a/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/ObjectiveC/ObjectiveCTrackedTypeAttribute.cs b/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/ObjectiveC/ObjectiveCTrackedTypeAttribute.cs
new file mode 100644
index 00000000000..48ace516677
--- /dev/null
+++ b/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/ObjectiveC/ObjectiveCTrackedTypeAttribute.cs
@@ -0,0 +1,20 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System.Runtime.Versioning;
+
+namespace System.Runtime.InteropServices.ObjectiveC
+{
+ /// <summary>
+ /// Attribute used to indicate a class represents a tracked Objective-C type.
+ /// </summary>
+ [System.Runtime.Versioning.SupportedOSPlatformAttribute("macos")]
+ [AttributeUsage(AttributeTargets.Class, Inherited = true, AllowMultiple = false)]
+ public sealed class ObjectiveCTrackedTypeAttribute : Attribute
+ {
+ /// <summary>
+ /// Instantiate a <see cref="ObjectiveCTrackedTypeAttribute"/> instance.
+ /// </summary>
+ public ObjectiveCTrackedTypeAttribute() { }
+ }
+} \ No newline at end of file
diff --git a/src/libraries/System.Runtime.InteropServices/ref/System.Runtime.InteropServices.cs b/src/libraries/System.Runtime.InteropServices/ref/System.Runtime.InteropServices.cs
index 237ca4bc389..7ea73701620 100644
--- a/src/libraries/System.Runtime.InteropServices/ref/System.Runtime.InteropServices.cs
+++ b/src/libraries/System.Runtime.InteropServices/ref/System.Runtime.InteropServices.cs
@@ -1802,6 +1802,43 @@ namespace System.Runtime.InteropServices.ComTypes
VAR_DISPATCH = 3,
}
}
+namespace System.Runtime.InteropServices.ObjectiveC
+{
+ [System.Runtime.Versioning.SupportedOSPlatformAttribute("macos")]
+ [AttributeUsage(AttributeTargets.Class, Inherited = true, AllowMultiple = false)]
+ public sealed class ObjectiveCTrackedTypeAttribute : System.Attribute
+ {
+ public ObjectiveCTrackedTypeAttribute() { }
+ }
+
+ [System.Runtime.Versioning.SupportedOSPlatformAttribute("macos")]
+ [System.CLSCompliantAttribute(false)]
+ public static class ObjectiveCMarshal
+ {
+ public unsafe delegate delegate* unmanaged<System.IntPtr, void> UnhandledExceptionPropagationHandler(
+ System.Exception exception,
+ System.RuntimeMethodHandle lastMethod,
+ out System.IntPtr context);
+ public static unsafe void Initialize(
+ delegate* unmanaged<void> beginEndCallback,
+ delegate* unmanaged<System.IntPtr, int> isReferencedCallback,
+ delegate* unmanaged<System.IntPtr, void> trackedObjectEnteredFinalization,
+ UnhandledExceptionPropagationHandler unhandledExceptionPropagationHandler) => throw null;
+ public static GCHandle CreateReferenceTrackingHandle(
+ object obj,
+ out System.Span<System.IntPtr> taggedMemory) => throw null;
+ public enum MessageSendFunction
+ {
+ MsgSend,
+ MsgSendFpret,
+ MsgSendStret,
+ MsgSendSuper,
+ MsgSendSuperStret,
+ }
+ public static void SetMessageSendCallback(MessageSendFunction msgSendFunction, System.IntPtr func) => throw null;
+ public static void SetMessageSendPendingException(Exception? exception) => throw null;
+ }
+}
namespace System.Security
{
public sealed partial class SecureString : System.IDisposable
diff --git a/src/libraries/System.Runtime.InteropServices/tests/System.Runtime.InteropServices.Tests.csproj b/src/libraries/System.Runtime.InteropServices/tests/System.Runtime.InteropServices.Tests.csproj
index 79c9de91710..e04dab60460 100644
--- a/src/libraries/System.Runtime.InteropServices/tests/System.Runtime.InteropServices.Tests.csproj
+++ b/src/libraries/System.Runtime.InteropServices/tests/System.Runtime.InteropServices.Tests.csproj
@@ -3,6 +3,7 @@
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<TargetFrameworks>$(NetCoreAppCurrent);$(NetCoreAppCurrent)-windows</TargetFrameworks>
<TestRuntime>true</TestRuntime>
+ <IncludeRemoteExecutor>true</IncludeRemoteExecutor>
</PropertyGroup>
<ItemGroup>
<Compile Include="AssemblyInfo.cs" />
@@ -145,6 +146,9 @@
<Compile Include="System\Runtime\InteropServices\Marshal\StringMarshalingTests.cs" />
<Compile Include="System\Runtime\InteropServices\Marshal\StructureToPtrTests.cs" />
<Compile Include="System\Runtime\InteropServices\Marshal\UnsafeAddrOfPinnedArrayElementTests.cs" />
+ <Compile Include="System\Runtime\InteropServices\ObjectiveC\MessageSendTests.cs" />
+ <Compile Include="System\Runtime\InteropServices\ObjectiveC\PendingExceptionTests.cs" />
+ <Compile Include="System\Runtime\InteropServices\ObjectiveC\LibObjC.cs" />
<Compile Include="System\Runtime\InteropServices\PrimaryInteropAssemblyAttributeTests.cs" />
<Compile Include="System\Runtime\InteropServices\ProgIdAttributeTests.cs" />
<Compile Include="System\Runtime\InteropServices\RuntimeEnvironmentTests.cs" />
diff --git a/src/libraries/System.Runtime.InteropServices/tests/System/Runtime/InteropServices/ObjectiveC/LibObjC.cs b/src/libraries/System.Runtime.InteropServices/tests/System/Runtime/InteropServices/ObjectiveC/LibObjC.cs
new file mode 100644
index 00000000000..799efd8c7af
--- /dev/null
+++ b/src/libraries/System.Runtime.InteropServices/tests/System/Runtime/InteropServices/ObjectiveC/LibObjC.cs
@@ -0,0 +1,74 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System;
+using System.Runtime.InteropServices;
+
+using Xunit;
+
+using static System.Runtime.InteropServices.ObjectiveC.ObjectiveCMarshal;
+
+namespace System.Runtime.InteropServices.Tests
+{
+ [PlatformSpecific(TestPlatforms.OSX)]
+ internal static class LibObjC
+ {
+ private const string LibName = "/usr/lib/libobjc.dylib";
+
+ [DllImport(LibName)]
+ public static extern IntPtr objc_msgSend(IntPtr self, IntPtr selector);
+
+ [DllImport(LibName)]
+ public static extern IntPtr objc_msgSend_fpret(IntPtr self, IntPtr selector);
+
+ [DllImport(LibName)]
+ public static extern void objc_msgSend_stret(out IntPtr ret, IntPtr self, IntPtr selector);
+
+ [DllImport(LibName)]
+ public static extern IntPtr objc_msgSendSuper(IntPtr super, IntPtr selector);
+
+ [DllImport(LibName)]
+ public static extern void objc_msgSendSuper_stret(out IntPtr ret, IntPtr super, IntPtr selector);
+
+ [DllImport(LibName)]
+ public static extern IntPtr objc_getClass(string className);
+
+ [DllImport(LibName)]
+ public static extern IntPtr sel_getUid(string selector);
+
+ // https://developer.apple.com/documentation/objectivec/objc_super
+ [StructLayout(LayoutKind.Sequential)]
+ public struct objc_super
+ {
+ public IntPtr receiver;
+ public IntPtr super_class;
+ }
+
+ public static IntPtr CallPInvoke(MessageSendFunction msgSend, IntPtr inst, IntPtr sel)
+ {
+ switch (msgSend)
+ {
+ case MessageSendFunction.MsgSend:
+ return LibObjC.objc_msgSend(inst, sel);
+ case MessageSendFunction.MsgSendFpret:
+ return LibObjC.objc_msgSend_fpret(inst, sel);
+ case MessageSendFunction.MsgSendStret:
+ {
+ IntPtr ret;
+ LibObjC.objc_msgSend_stret(out ret, inst, sel);
+ return ret;
+ }
+ case MessageSendFunction.MsgSendSuper:
+ return LibObjC.objc_msgSendSuper(inst, sel);
+ case MessageSendFunction.MsgSendSuperStret:
+ {
+ IntPtr ret;
+ LibObjC.objc_msgSendSuper_stret(out ret, inst, sel);
+ return ret;
+ }
+ default:
+ throw new ArgumentException($"Unknown {nameof(MessageSendFunction)}: {msgSend}");
+ }
+ }
+ }
+} \ No newline at end of file
diff --git a/src/libraries/System.Runtime.InteropServices/tests/System/Runtime/InteropServices/ObjectiveC/MessageSendTests.cs b/src/libraries/System.Runtime.InteropServices/tests/System/Runtime/InteropServices/ObjectiveC/MessageSendTests.cs
new file mode 100644
index 00000000000..4877317d592
--- /dev/null
+++ b/src/libraries/System.Runtime.InteropServices/tests/System/Runtime/InteropServices/ObjectiveC/MessageSendTests.cs
@@ -0,0 +1,148 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System;
+using System.Collections.Generic;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+using System.Runtime.InteropServices.ObjectiveC;
+
+using Microsoft.DotNet.RemoteExecutor;
+using Xunit;
+
+using static System.Runtime.InteropServices.ObjectiveC.ObjectiveCMarshal;
+
+namespace System.Runtime.InteropServices.Tests
+{
+ [PlatformSpecific(TestPlatforms.OSX)]
+ [SkipOnMono("Not currently implemented on Mono")]
+ public unsafe class MessageSendTests
+ {
+ private static int s_count = 1;
+ private static bool s_callbackInvoked = false;
+
+ [UnmanagedCallersOnly]
+ private static IntPtr ObjCMsgSend(IntPtr inst, IntPtr sel) => ReturnPtr(MessageSendFunction.MsgSend);
+
+ [UnmanagedCallersOnly]
+ private static IntPtr ObjCMsgSendFpret(IntPtr inst, IntPtr sel) => ReturnPtr(MessageSendFunction.MsgSendFpret);
+
+ [UnmanagedCallersOnly]
+ private static void ObjCMsgSendStret(IntPtr* ret, IntPtr inst, IntPtr sel) => *ret = ReturnPtr(MessageSendFunction.MsgSendStret);
+
+ [UnmanagedCallersOnly]
+ private static IntPtr ObjCMsgSendSuper(IntPtr inst, IntPtr sel) => ReturnPtr(MessageSendFunction.MsgSendSuper);
+
+ [UnmanagedCallersOnly]
+ private static void ObjCMsgSendSuperStret(IntPtr* ret, IntPtr inst, IntPtr sel) => *ret = ReturnPtr(MessageSendFunction.MsgSendSuperStret);
+
+ private static IntPtr ReturnPtr(MessageSendFunction msgSendFunc)
+ {
+ s_callbackInvoked = true;
+ return new IntPtr(s_count + (int)msgSendFunc);
+ }
+
+ private static (MessageSendFunction MsgSend, IntPtr Func)[] msgSendOverrides =
+ {
+ (MessageSendFunction.MsgSend, (IntPtr)(delegate* unmanaged<IntPtr, IntPtr, IntPtr>)&ObjCMsgSend),
+ (MessageSendFunction.MsgSendFpret, (IntPtr)(delegate* unmanaged<IntPtr, IntPtr, IntPtr>)&ObjCMsgSendFpret),
+ (MessageSendFunction.MsgSendStret, (IntPtr)(delegate* unmanaged<IntPtr*, IntPtr, IntPtr, void>)&ObjCMsgSendStret),
+ (MessageSendFunction.MsgSendSuper, (IntPtr)(delegate* unmanaged<IntPtr, IntPtr, IntPtr>)&ObjCMsgSendSuper),
+ (MessageSendFunction.MsgSendSuperStret, (IntPtr)(delegate* unmanaged<IntPtr*, IntPtr, IntPtr, void>)&ObjCMsgSendSuperStret),
+ };
+
+ [Fact]
+ public void SetMessageSendCallback_NullCallback()
+ {
+ Assert.Throws<ArgumentNullException>(() => ObjectiveCMarshal.SetMessageSendCallback(MessageSendFunction.MsgSend, IntPtr.Zero));
+ }
+
+ [Fact]
+ public void SetMessageSendCallback_InvalidMessageSendFunction()
+ {
+ Assert.Throws<ArgumentOutOfRangeException>(() => ObjectiveCMarshal.SetMessageSendCallback((MessageSendFunction)100, msgSendOverrides[0].Func));
+ }
+
+ [ConditionalFact(typeof(RemoteExecutor), nameof(RemoteExecutor.IsSupported))]
+ public void SetMessageSendCallback_AlreadySet()
+ {
+ RemoteExecutor.Invoke(() =>
+ {
+ var (msgSend, func) = msgSendOverrides[0];
+ ObjectiveCMarshal.SetMessageSendCallback(msgSend, func);
+ Assert.Throws<InvalidOperationException>(() => ObjectiveCMarshal.SetMessageSendCallback(msgSend, func));
+ }).Dispose();
+ }
+
+ public static IEnumerable<object[]> MessageSendFunctionsToOverride()
+ {
+ yield return new[] { (MessageSendFunction[])Enum.GetValues<MessageSendFunction>() };
+ yield return new[] { new MessageSendFunction[]{ MessageSendFunction.MsgSend, MessageSendFunction.MsgSendStret } };
+ }
+
+ [ConditionalTheory(typeof(RemoteExecutor), nameof(RemoteExecutor.IsSupported))]
+ [MemberData(nameof(MessageSendFunctionsToOverride))]
+ public void SetMessageSendCallback(MessageSendFunction[] funcsToOverride)
+ {
+ // Pass functions to override as a string for remote execution
+ RemoteExecutor.Invoke((string funcsToOverrideAsStr) =>
+ {
+ string[] msgSendStrArray = funcsToOverrideAsStr.Split(';');
+ MessageSendFunction[] msgSendArray = new MessageSendFunction[msgSendStrArray.Length];
+ for (int i = 0; i < msgSendStrArray.Length; i++)
+ {
+ MessageSendFunction msgSend = Enum.Parse<MessageSendFunction>(msgSendStrArray[i]);
+ Assert.True(Enum.IsDefined<MessageSendFunction>(msgSend));
+ msgSendArray[i] = msgSend;
+ }
+
+ SetMessageSendCallbackImpl(msgSendArray);
+ }, string.Join(';', funcsToOverride)).Dispose();
+ }
+
+ private static void SetMessageSendCallbackImpl(MessageSendFunction[] funcsToOverride)
+ {
+ foreach (var (msgSend, func) in msgSendOverrides)
+ {
+ bool shouldOverride = Array.IndexOf(funcsToOverride, msgSend) >= 0;
+
+ IntPtr expected;
+ IntPtr inst = IntPtr.Zero;
+ IntPtr sel = IntPtr.Zero;
+ if (shouldOverride)
+ {
+ // Override message send function
+ ObjectiveCMarshal.SetMessageSendCallback(msgSend, func);
+ expected = (IntPtr)(s_count + (int)msgSend);
+ }
+ else
+ {
+ if (msgSend == MessageSendFunction.MsgSendSuper || msgSend == MessageSendFunction.MsgSendSuperStret)
+ {
+ // Calling super message functions requires a valid superclass and selector
+ var super = new LibObjC.objc_super()
+ {
+ receiver = IntPtr.Zero,
+ super_class = LibObjC.objc_getClass("NSObject")
+ };
+ inst = (IntPtr)(&super);
+ sel = LibObjC.sel_getUid("self");
+ }
+
+ // Sending message to nil should return nil
+ expected = IntPtr.Zero;
+ }
+
+ // Call message send function through P/Invoke
+ IntPtr ret = LibObjC.CallPInvoke(msgSend, inst, sel);
+
+ Assert.Equal(shouldOverride, s_callbackInvoked);
+ Assert.Equal(expected, ret);
+
+ s_count++;
+ s_callbackInvoked = false;
+ }
+ }
+ }
+}
+
diff --git a/src/libraries/System.Runtime.InteropServices/tests/System/Runtime/InteropServices/ObjectiveC/PendingExceptionTests.cs b/src/libraries/System.Runtime.InteropServices/tests/System/Runtime/InteropServices/ObjectiveC/PendingExceptionTests.cs
new file mode 100644
index 00000000000..77e0274383e
--- /dev/null
+++ b/src/libraries/System.Runtime.InteropServices/tests/System/Runtime/InteropServices/ObjectiveC/PendingExceptionTests.cs
@@ -0,0 +1,97 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System;
+using System.Collections.Generic;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+using System.Runtime.InteropServices.ObjectiveC;
+
+using Microsoft.DotNet.RemoteExecutor;
+using Xunit;
+
+using static System.Runtime.InteropServices.ObjectiveC.ObjectiveCMarshal;
+
+namespace System.Runtime.InteropServices.Tests
+{
+ [PlatformSpecific(TestPlatforms.OSX)]
+ [SkipOnMono("Not currently implemented on Mono")]
+ public unsafe class PendingExceptionTests
+ {
+ private sealed class PendingException : Exception
+ {
+ public PendingException(string message) : base(message) { }
+ }
+
+ [UnmanagedCallersOnly]
+ private static IntPtr MsgSend(IntPtr inst, IntPtr sel) => SetPendingException();
+
+ [UnmanagedCallersOnly]
+ private static IntPtr MsgSendFpret(IntPtr inst, IntPtr sel) => SetPendingException();
+
+ [UnmanagedCallersOnly]
+ private static void MsgSendStret(IntPtr* ret, IntPtr inst, IntPtr sel) => *ret = SetPendingException();
+
+ [UnmanagedCallersOnly]
+ private static IntPtr MsgSendSuper(IntPtr inst, IntPtr sel) => SetPendingException();
+
+ [UnmanagedCallersOnly]
+ private static void MsgSendSuperStret(IntPtr* ret, IntPtr inst, IntPtr sel) => *ret = SetPendingException();
+
+ private static IntPtr SetPendingException([CallerMemberName] string callerName = "")
+ {
+ ObjectiveCMarshal.SetMessageSendPendingException(new PendingException(callerName));
+ return IntPtr.Zero;
+ }
+
+ private static (MessageSendFunction MsgSend, IntPtr Func)[] msgSendOverrides =
+ {
+ (MessageSendFunction.MsgSend, (IntPtr)(delegate* unmanaged<IntPtr, IntPtr, IntPtr>)&MsgSend),
+ (MessageSendFunction.MsgSendFpret, (IntPtr)(delegate* unmanaged<IntPtr, IntPtr, IntPtr>)&MsgSendFpret),
+ (MessageSendFunction.MsgSendStret, (IntPtr)(delegate* unmanaged<IntPtr*, IntPtr, IntPtr, void>)&MsgSendStret),
+ (MessageSendFunction.MsgSendSuper, (IntPtr)(delegate* unmanaged<IntPtr, IntPtr, IntPtr>)&MsgSendSuper),
+ (MessageSendFunction.MsgSendSuperStret, (IntPtr)(delegate* unmanaged<IntPtr*, IntPtr, IntPtr, void>)&MsgSendSuperStret),
+ };
+
+ [ConditionalTheory(typeof(RemoteExecutor), nameof(RemoteExecutor.IsSupported))]
+ [InlineData(MessageSendFunction.MsgSend)]
+ [InlineData(MessageSendFunction.MsgSendFpret)]
+ [InlineData(MessageSendFunction.MsgSendStret)]
+ [InlineData(MessageSendFunction.MsgSendSuper)]
+ [InlineData(MessageSendFunction.MsgSendSuperStret)]
+ public void ValidateSetMessageSendPendingException(MessageSendFunction func)
+ {
+ // Pass functions to override as a string for remote execution
+ RemoteExecutor.Invoke((string funcToOverrideAsStr) =>
+ {
+ MessageSendFunction msgSend = Enum.Parse<MessageSendFunction>(funcToOverrideAsStr);
+ Assert.True(Enum.IsDefined<MessageSendFunction>(msgSend));
+
+ ValidateSetMessageSendPendingExceptionImpl(msgSend);
+ }, func.ToString()).Dispose();
+ }
+
+ private static void ValidateSetMessageSendPendingExceptionImpl(MessageSendFunction funcToOverride)
+ {
+ foreach (var (msgSend, func) in msgSendOverrides)
+ {
+ if (funcToOverride != msgSend)
+ {
+ continue;
+ }
+
+ // Override message send function
+ ObjectiveCMarshal.SetMessageSendCallback(msgSend, func);
+
+ // Call message send function through P/Invoke
+ IntPtr inst = IntPtr.Zero;
+ IntPtr sel = IntPtr.Zero;
+
+ Exception ex = Assert.Throws<PendingException>(() => LibObjC.CallPInvoke(msgSend, inst, sel));
+ Assert.Equal(msgSend.ToString(), ex.Message);
+
+ break;
+ }
+ }
+ }
+} \ No newline at end of file
diff --git a/src/mono/System.Private.CoreLib/System.Private.CoreLib.csproj b/src/mono/System.Private.CoreLib/System.Private.CoreLib.csproj
index 1d64a813f15..be9b6eeef6e 100644
--- a/src/mono/System.Private.CoreLib/System.Private.CoreLib.csproj
+++ b/src/mono/System.Private.CoreLib/System.Private.CoreLib.csproj
@@ -250,6 +250,8 @@
<Compile Include="$(BclSourcesRoot)\System\Runtime\InteropServices\MarshalAsAttribute.Mono.cs" />
<Compile Include="$(BclSourcesRoot)\System\Runtime\InteropServices\NativeLibrary.Mono.cs" />
<Compile Include="$(BclSourcesRoot)\System\Runtime\InteropServices\SafeHandle.Mono.cs" />
+ <Compile Include="$(BclSourcesRoot)\System\Runtime\InteropServices\ObjectiveCMarshal.Mono.cs"
+ Condition="'$(FeatureObjCMarshal)' == 'true'"/>
<Compile Include="$(BclSourcesRoot)\System\Runtime\Intrinsics\X86\X86Base.Mono.cs" />
<Compile Include="$(BclSourcesRoot)\System\Runtime\Loader\AssemblyLoadContext.Mono.cs" />
<Compile Include="$(BclSourcesRoot)\System\Security\DynamicSecurityMethodAttribute.cs" />
diff --git a/src/mono/System.Private.CoreLib/src/System/Runtime/InteropServices/ObjectiveCMarshal.Mono.cs b/src/mono/System.Private.CoreLib/src/System/Runtime/InteropServices/ObjectiveCMarshal.Mono.cs
new file mode 100644
index 00000000000..d97624ac2a1
--- /dev/null
+++ b/src/mono/System.Private.CoreLib/src/System/Runtime/InteropServices/ObjectiveCMarshal.Mono.cs
@@ -0,0 +1,37 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System.Diagnostics;
+using System.Runtime.Versioning;
+using System.Runtime.CompilerServices;
+
+namespace System.Runtime.InteropServices.ObjectiveC
+{
+ public static partial class ObjectiveCMarshal
+ {
+ /// <summary>
+ /// Sets a pending exception to be thrown the next time the runtime is entered from an overridden msgSend P/Invoke.
+ /// </summary>
+ /// <param name="exception">The exception.</param>
+ /// <remarks>
+ /// If <c>null</c> is supplied any pending exception is discarded.
+ /// </remarks>
+ public static void SetMessageSendPendingException(Exception? exception)
+ => throw new NotImplementedException();
+
+ private static bool TrySetGlobalMessageSendCallback(
+ MessageSendFunction msgSendFunction,
+ IntPtr func) => throw new NotImplementedException();
+
+ private static unsafe bool TryInitializeReferenceTracker(
+ delegate* unmanaged<void> beginEndCallback,
+ delegate* unmanaged<IntPtr, int> isReferencedCallback,
+ delegate* unmanaged<IntPtr, void> trackedObjectEnteredFinalization)
+ => throw new NotImplementedException();
+
+ private static IntPtr CreateReferenceTrackingHandleInternal(
+ ObjectHandleOnStack obj,
+ out int memInSizeT,
+ out IntPtr mem) => throw new NotImplementedException();
+ }
+} \ No newline at end of file
diff --git a/src/tests/Interop/CMakeLists.txt b/src/tests/Interop/CMakeLists.txt
index 8622b4f4da4..7f8b926bd76 100644
--- a/src/tests/Interop/CMakeLists.txt
+++ b/src/tests/Interop/CMakeLists.txt
@@ -94,5 +94,6 @@ if(CLR_CMAKE_TARGET_WIN32)
endif(CLR_CMAKE_TARGET_WIN32)
if(CLR_CMAKE_TARGET_OSX OR CLR_CMAKE_TARGET_IOS OR CLR_CMAKE_TARGET_TVOS)
- add_subdirectory(ObjectiveC)
+ add_subdirectory(ObjectiveC/AutoReleaseTest)
+ add_subdirectory(ObjectiveC/ObjectiveCMarshalAPI)
endif()
diff --git a/src/tests/Interop/ObjectiveC/AutoReleaseTest/AutoReleaseTest.csproj b/src/tests/Interop/ObjectiveC/AutoReleaseTest/AutoReleaseTest.csproj
index 4287f1cc2a1..6bce9e19344 100644
--- a/src/tests/Interop/ObjectiveC/AutoReleaseTest/AutoReleaseTest.csproj
+++ b/src/tests/Interop/ObjectiveC/AutoReleaseTest/AutoReleaseTest.csproj
@@ -11,7 +11,7 @@
<Compile Include="*.cs" />
</ItemGroup>
<ItemGroup>
- <ProjectReference Include="../CMakeLists.txt" />
+ <ProjectReference Include="./CMakeLists.txt" />
<ProjectReference Include="$(TestSourceDir)Common/CoreCLRTestLibrary/CoreCLRTestLibrary.csproj" />
</ItemGroup>
</Project>
diff --git a/src/tests/Interop/ObjectiveC/CMakeLists.txt b/src/tests/Interop/ObjectiveC/AutoReleaseTest/CMakeLists.txt
index 26d1f2e7c8f..26d1f2e7c8f 100644
--- a/src/tests/Interop/ObjectiveC/CMakeLists.txt
+++ b/src/tests/Interop/ObjectiveC/AutoReleaseTest/CMakeLists.txt
diff --git a/src/tests/Interop/ObjectiveC/autorelease.mm b/src/tests/Interop/ObjectiveC/AutoReleaseTest/autorelease.mm
index dd24ed41725..dd24ed41725 100644
--- a/src/tests/Interop/ObjectiveC/autorelease.mm
+++ b/src/tests/Interop/ObjectiveC/AutoReleaseTest/autorelease.mm
diff --git a/src/tests/Interop/ObjectiveC/ObjectiveCMarshalAPI/CMakeLists.txt b/src/tests/Interop/ObjectiveC/ObjectiveCMarshalAPI/CMakeLists.txt
new file mode 100644
index 00000000000..9ff79679f4a
--- /dev/null
+++ b/src/tests/Interop/ObjectiveC/ObjectiveCMarshalAPI/CMakeLists.txt
@@ -0,0 +1,16 @@
+cmake_minimum_required(VERSION 3.16)
+set(CMAKE_OBJC_STANDARD 11)
+set(CMAKE_OBJC_STANDARD_REQUIRED TRUE)
+
+enable_language(OBJC)
+
+project (NativeObjCMarshalTests)
+include ("${CLR_INTEROP_TEST_ROOT}/Interop.cmake")
+set(SOURCES NativeObjCMarshalTests.cpp)
+
+# add the shared library
+add_library (NativeObjCMarshalTests SHARED ${SOURCES})
+target_link_libraries(NativeObjCMarshalTests ${LINK_LIBRARIES_ADDITIONAL})
+
+# add the install targets
+install (TARGETS NativeObjCMarshalTests DESTINATION bin)
diff --git a/src/tests/Interop/ObjectiveC/ObjectiveCMarshalAPI/Directory.Build.props b/src/tests/Interop/ObjectiveC/ObjectiveCMarshalAPI/Directory.Build.props
new file mode 100644
index 00000000000..84447b61f05
--- /dev/null
+++ b/src/tests/Interop/ObjectiveC/ObjectiveCMarshalAPI/Directory.Build.props
@@ -0,0 +1,7 @@
+<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <Import Project="../../Directory.Build.props" />
+
+ <!-- Properties for all ObjectiveC managed test assets -->
+ <PropertyGroup>
+ </PropertyGroup>
+</Project>
diff --git a/src/tests/Interop/ObjectiveC/ObjectiveCMarshalAPI/NativeObjCMarshalTests.cpp b/src/tests/Interop/ObjectiveC/ObjectiveCMarshalAPI/NativeObjCMarshalTests.cpp
new file mode 100644
index 00000000000..bfd4f16e07c
--- /dev/null
+++ b/src/tests/Interop/ObjectiveC/ObjectiveCMarshalAPI/NativeObjCMarshalTests.cpp
@@ -0,0 +1,142 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+#include <cstdlib>
+#include <cstdio>
+#include <cassert>
+#include <atomic>
+#include <exception>
+#include <platformdefines.h>
+
+using BeginEndCallback = void(STDMETHODCALLTYPE *)(void);
+using IsReferencedCallback = int(STDMETHODCALLTYPE *)(void*);
+using EnteredFinalizationCallback = void(STDMETHODCALLTYPE *)(void*);
+
+//#define REF_TRACKER_DEBUG_CALLBACKS
+
+namespace
+{
+ std::atomic_bool state = { false };
+
+ void STDMETHODCALLTYPE BeginEndCb()
+ {
+#ifdef REF_TRACKER_DEBUG_CALLBACKS
+ ::printf("BeginEndCb: %s -> %s\n",
+ state ? "true" : "false",
+ !state ? "true" : "false");
+#endif // REF_TRACKER_DEBUG_CALLBACKS
+
+ state = !state;
+ }
+
+ // See contract in managed portion of test.
+ struct Contract
+ {
+ size_t RefCountDown;
+ size_t RefCountUp;
+ };
+
+ int STDMETHODCALLTYPE IsRefCb(void* mem)
+ {
+#ifdef REF_TRACKER_DEBUG_CALLBACKS
+ ::printf("IsRefCb: %p\n", mem);
+#endif // REF_TRACKER_DEBUG_CALLBACKS
+
+ if (!state)
+ {
+ ::printf("Invalid callback state!\n");
+ ::abort();
+ }
+
+
+ assert(mem != nullptr);
+ auto cxt = (Contract*)mem;
+
+ cxt->RefCountDown--;
+ cxt->RefCountUp++;
+
+ if (cxt->RefCountDown == 0)
+ {
+ // No more references
+ return 0;
+ }
+ else
+ {
+ return 1;
+ }
+ }
+
+ void STDMETHODCALLTYPE EnteredFinalizerCb(void* mem)
+ {
+#ifdef REF_TRACKER_DEBUG_CALLBACKS
+ ::printf("EnteredFinalizerCb: %p\n", mem);
+#endif // REF_TRACKER_DEBUG_CALLBACKS
+
+ assert(mem != nullptr);
+ auto cxt = (Contract*)mem;
+
+ cxt->RefCountDown = (size_t)-1;
+ }
+}
+
+extern "C" DLL_EXPORT void GetExports(
+ BeginEndCallback* beginEnd,
+ IsReferencedCallback* isRef,
+ EnteredFinalizationCallback* enteredFinalizer)
+{
+ assert(beginEnd != nullptr);
+ assert(isRef != nullptr);
+ assert(enteredFinalizer != nullptr);
+
+ *beginEnd = BeginEndCb;
+ *isRef = IsRefCb;
+ *enteredFinalizer = EnteredFinalizerCb;
+}
+
+using propagation_func_t = void(*)(void*);
+
+namespace
+{
+ [[noreturn]]
+ void ThrowInt(void* cxt)
+ {
+ int val = (int)cxt;
+ throw val;
+ }
+
+ [[noreturn]]
+ void ThrowException(void*)
+ {
+ throw std::exception{};
+ }
+}
+
+extern "C" DLL_EXPORT propagation_func_t GetThrowInt()
+{
+ return ThrowInt;
+}
+
+extern "C" DLL_EXPORT propagation_func_t GetThrowException()
+{
+ return ThrowException;
+}
+
+using fptr_t = void(*)(int);
+
+extern "C" DLL_EXPORT int CallAndCatch(fptr_t fptr, int a)
+{
+ try
+ {
+ fptr(a);
+
+ std::printf("Function should not have returned.");
+ std::abort();
+ }
+ catch (int e)
+ {
+ return e;
+ }
+ catch (const std::exception &e)
+ {
+ return a;
+ }
+}
diff --git a/src/tests/Interop/ObjectiveC/ObjectiveCMarshalAPI/ObjectiveCMarshalAPI.csproj b/src/tests/Interop/ObjectiveC/ObjectiveCMarshalAPI/ObjectiveCMarshalAPI.csproj
new file mode 100644
index 00000000000..9a81c001eb8
--- /dev/null
+++ b/src/tests/Interop/ObjectiveC/ObjectiveCMarshalAPI/ObjectiveCMarshalAPI.csproj
@@ -0,0 +1,14 @@
+<Project Sdk="Microsoft.NET.Sdk">
+ <PropertyGroup>
+ <OutputType>Exe</OutputType>
+ <!-- Test unsupported outside of OSX -->
+ <CLRTestTargetUnsupported Condition="'$(TargetsOSX)' != 'true'">true</CLRTestTargetUnsupported>
+ <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
+ </PropertyGroup>
+ <ItemGroup>
+ <Compile Include="Program.cs" />
+ </ItemGroup>
+ <ItemGroup>
+ <ProjectReference Include="./CMakeLists.txt" />
+ </ItemGroup>
+</Project>
diff --git a/src/tests/Interop/ObjectiveC/ObjectiveCMarshalAPI/Program.cs b/src/tests/Interop/ObjectiveC/ObjectiveCMarshalAPI/Program.cs
new file mode 100644
index 00000000000..9064b0b7aac
--- /dev/null
+++ b/src/tests/Interop/ObjectiveC/ObjectiveCMarshalAPI/Program.cs
@@ -0,0 +1,326 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+namespace ObjectiveCMarshalAPI
+{
+ using System;
+ using System.Collections.Generic;
+ using System.Reflection;
+ using System.Runtime.InteropServices;
+ using System.Runtime.InteropServices.ObjectiveC;
+
+ using TestLibrary;
+
+ class NativeObjCMarshalTests
+ {
+ [DllImport(nameof(NativeObjCMarshalTests))]
+ public static extern unsafe void GetExports(
+ out delegate* unmanaged<void> beginEndCallback,
+ out delegate* unmanaged<IntPtr, int> isReferencedCallback,
+ out delegate* unmanaged<IntPtr, void> trackedObjectEnteredFinalization);
+
+ [DllImport(nameof(NativeObjCMarshalTests))]
+ public static extern int CallAndCatch(IntPtr fptr, int a);
+
+ [DllImport(nameof(NativeObjCMarshalTests))]
+ public static extern IntPtr GetThrowInt();
+
+ [DllImport(nameof(NativeObjCMarshalTests))]
+ public static extern IntPtr GetThrowException();
+ }
+
+ unsafe class Program
+ {
+ static void Validate_ReferenceTrackingAPIs_InvalidArgs()
+ {
+ Console.WriteLine($"Running {nameof(Validate_ReferenceTrackingAPIs_InvalidArgs)}...");
+
+ delegate* unmanaged<void> beginEndCallback;
+ delegate* unmanaged<IntPtr, int> isReferencedCallback;
+ delegate* unmanaged<IntPtr, void> trackedObjectEnteredFinalization;
+ NativeObjCMarshalTests.GetExports(out beginEndCallback, out isReferencedCallback, out trackedObjectEnteredFinalization);
+
+ Assert.Throws<ArgumentNullException>(
+ () =>
+ {
+ ObjectiveCMarshal.Initialize(null, isReferencedCallback, trackedObjectEnteredFinalization, OnUnhandledExceptionPropagationHandler);
+ });
+ Assert.Throws<ArgumentNullException>(
+ () =>
+ {
+ ObjectiveCMarshal.Initialize(beginEndCallback, null, trackedObjectEnteredFinalization, OnUnhandledExceptionPropagationHandler);
+ });
+ Assert.Throws<ArgumentNullException>(
+ () =>
+ {
+ ObjectiveCMarshal.Initialize(beginEndCallback, isReferencedCallback, null, OnUnhandledExceptionPropagationHandler);
+ });
+ Assert.Throws<ArgumentNullException>(
+ () =>
+ {
+ ObjectiveCMarshal.Initialize(beginEndCallback, isReferencedCallback, trackedObjectEnteredFinalization, null);
+ });
+ Assert.Throws<ArgumentNullException>(
+ () =>
+ {
+ ObjectiveCMarshal.CreateReferenceTrackingHandle(null , out _);
+ });
+ }
+
+ // The expectation here is during reference tracking handle creation
+ // the RefCountDown will be set to some non-negative number and RefCountUp
+ // will remain zero. The values will be incremented and decremented respectively
+ // during the "is referenced" callback. When the object enters the finalizer queue
+ // the RefCountDown will then be set to nuint.MaxValue. In the object's finalizer
+ // the RefCountUp can be checked to ensure the count down value was respected.
+ struct Contract
+ {
+ public nuint RefCountDown;
+ public nuint RefCountUp;
+ };
+
+ [ObjectiveCTrackedTypeAttribute]
+ class Base
+ {
+ public static int AllocCount = 0;
+ public static int FinalizeCount = 0;
+
+ private nuint _expectedCount = 0;
+ private Contract* _contract;
+
+ public Base()
+ {
+ AllocCount++;
+ }
+
+ ~Base()
+ {
+ if (_contract != null)
+ {
+ Assert.AreEqual(nuint.MaxValue, _contract->RefCountDown); // Validate finalizer queue callback
+ Assert.AreEqual(_expectedCount, _contract->RefCountUp); // Validate "is referenced" callback
+ }
+
+ FinalizeCount++;
+ }
+
+ public IntPtr Contract { get => (IntPtr)_contract; }
+
+ public void SetContractMemory(IntPtr mem, uint count)
+ {
+ _contract = (Contract*)mem;
+
+ // Contract should be 0 initialized when supplied.
+ Assert.AreEqual((nuint)0, _contract->RefCountDown);
+ Assert.AreEqual((nuint)0, _contract->RefCountUp);
+
+ _expectedCount = (nuint)count;
+ _contract->RefCountDown = _expectedCount;
+ }
+ }
+
+ class Derived : Base { }
+
+ class DerivedWithFinalizer : Base
+ {
+ ~DerivedWithFinalizer() { }
+ }
+
+ [ObjectiveCTrackedTypeAttribute]
+ class AttributedNoFinalizer { }
+
+ static void InitializeObjectiveCMarshal()
+ {
+ delegate* unmanaged<void> beginEndCallback;
+ delegate* unmanaged<IntPtr, int> isReferencedCallback;
+ delegate* unmanaged<IntPtr, void> trackedObjectEnteredFinalization;
+ NativeObjCMarshalTests.GetExports(out beginEndCallback, out isReferencedCallback, out trackedObjectEnteredFinalization);
+
+ ObjectiveCMarshal.Initialize(beginEndCallback, isReferencedCallback, trackedObjectEnteredFinalization, OnUnhandledExceptionPropagationHandler);
+ }
+
+ static GCHandle AllocAndTrackObject<T>() where T : Base, new()
+ {
+ var obj = new T();
+ GCHandle h = ObjectiveCMarshal.CreateReferenceTrackingHandle(obj, out Span<IntPtr> s);
+
+ // Validate contract length for tagged memory.
+ Assert.AreEqual(2, s.Length);
+
+ // Make the "is referenced" callback run a few times.
+ fixed (void* p = s)
+ obj.SetContractMemory((IntPtr)p, count: 3);
+ return h;
+ }
+
+ static void Validate_AllocAndFreeAnotherHandle<T>(GCHandle handle) where T : Base, new()
+ {
+ var obj = (T)handle.Target;
+ GCHandle h = ObjectiveCMarshal.CreateReferenceTrackingHandle(obj, out Span<IntPtr> s);
+
+ // Validate the memory is the same but the GCHandles are distinct.
+ fixed (void* p = s)
+ Assert.AreEqual(obj.Contract, new IntPtr(p));
+
+ Assert.AreNotEqual(handle, h);
+ h.Free();
+ }
+
+ static unsafe void Validate_ReferenceTracking_Scenario()
+ {
+ Console.WriteLine($"Running {nameof(Validate_ReferenceTracking_Scenario)}...");
+
+ var handles = new List<GCHandle>();
+
+ // Attempting to create handle prior to initialization.
+ Assert.Throws<InvalidOperationException>(
+ () =>
+ {
+ ObjectiveCMarshal.CreateReferenceTrackingHandle(new Base(), out _);
+ });
+
+ InitializeObjectiveCMarshal();
+
+ // Type attributed but no finalizer.
+ Assert.Throws<InvalidOperationException>(
+ () =>
+ {
+ ObjectiveCMarshal.CreateReferenceTrackingHandle(new AttributedNoFinalizer(), out _);
+ });
+
+ {
+ GCHandle h = AllocAndTrackObject<Base>();
+ handles.Add(h);
+ Validate_AllocAndFreeAnotherHandle<Base>(h);
+ }
+ {
+ GCHandle h = AllocAndTrackObject<Derived>();
+ handles.Add(h);
+ Validate_AllocAndFreeAnotherHandle<Derived>(h);
+ }
+ {
+ GCHandle h = AllocAndTrackObject<DerivedWithFinalizer>();
+ handles.Add(h);
+ Validate_AllocAndFreeAnotherHandle<DerivedWithFinalizer>(h);
+ }
+
+ // Trigger the GC
+ for (int i = 0; i < 5; ++i)
+ {
+ GC.Collect();
+ GC.WaitForPendingFinalizers();
+ }
+
+ // Validate we finalized all the objects we allocated.
+ // It is important to validate the count prior to freeing
+ // the handles to verify they are not keeping objects alive.
+ Assert.AreEqual(Base.FinalizeCount, Base.AllocCount);
+
+ // Clean up all allocated handles that are no longer needed.
+ foreach (var h in handles)
+ {
+ h.Free();
+ }
+
+ // Validate the exception propagation logic.
+ _Validate_ExceptionPropagation();
+ }
+
+ private class IntException : Exception
+ {
+ public int Value { get; }
+ public IntException(int value) { this.Value = value; }
+ }
+
+ private class ExceptionException : Exception
+ {
+ public ExceptionException() {}
+ }
+
+ [UnmanagedCallersOnly]
+ static void UCO_ThrowIntException(int a) => throw new IntException(a);
+ [UnmanagedCallersOnly]
+ static void UCO_ThrowExceptionException(int _) => throw new ExceptionException();
+
+ delegate void ThrowExceptionDelegate(int a);
+ static void DEL_ThrowIntException(int a) => throw new IntException(a);
+ static void DEL_ThrowExceptionException(int _) => throw new ExceptionException();
+
+ static unsafe delegate* unmanaged<IntPtr, void> OnUnhandledExceptionPropagationHandler(
+ Exception e,
+ RuntimeMethodHandle lastMethodHandle,
+ out IntPtr context)
+ {
+ var lastMethod = (MethodInfo)MethodBase.GetMethodFromHandle(lastMethodHandle);
+ Assert.IsTrue(lastMethod != null);
+
+ context = IntPtr.Zero;
+ if (e is IntException ie)
+ {
+ context = new IntPtr(ie.Value);
+ return (delegate* unmanaged<IntPtr, void>)NativeObjCMarshalTests.GetThrowInt();
+ }
+ else if (e is ExceptionException)
+ {
+ return (delegate* unmanaged<IntPtr, void>)NativeObjCMarshalTests.GetThrowException();
+ }
+
+ Assert.Fail("Unknown exception type");
+ throw new Exception("Unreachable");
+ }
+
+ class Scenario
+ {
+ public Scenario(delegate* unmanaged<int, void> fptr, int expected) { Fptr = fptr; Expected = expected; }
+ public delegate* unmanaged<int, void> Fptr;
+ public int Expected;
+ }
+
+ // Do not call this method from Main as it depends on a previous test for set up.
+ static void _Validate_ExceptionPropagation()
+ {
+ var scenarios = new[]
+ {
+ new Scenario((delegate* unmanaged<int, void>)&UCO_ThrowIntException, 3423),
+ new Scenario((delegate* unmanaged<int, void>)&UCO_ThrowExceptionException, 5432),
+ new Scenario((delegate* unmanaged<int, void>)Marshal.GetFunctionPointerForDelegate<ThrowExceptionDelegate>(DEL_ThrowIntException), 6453),
+ new Scenario((delegate* unmanaged<int, void>)Marshal.GetFunctionPointerForDelegate<ThrowExceptionDelegate>(DEL_ThrowExceptionException), 5343)
+ };
+
+ foreach (var scen in scenarios)
+ {
+ delegate* unmanaged<int, void> testNativeMethod = scen.Fptr;
+ int ret = NativeObjCMarshalTests.CallAndCatch((IntPtr)testNativeMethod, scen.Expected);
+ Assert.AreEqual(scen.Expected, ret);
+ }
+ }
+
+ static void Validate_Initialize_FailsOnSecondAttempt()
+ {
+ Console.WriteLine($"Running {nameof(Validate_Initialize_FailsOnSecondAttempt)}...");
+
+ Assert.Throws<InvalidOperationException>(
+ () =>
+ {
+ InitializeObjectiveCMarshal();
+ });
+ }
+
+ static int Main(string[] doNotUse)
+ {
+ try
+ {
+ Validate_ReferenceTrackingAPIs_InvalidArgs();
+ Validate_ReferenceTracking_Scenario();
+ Validate_Initialize_FailsOnSecondAttempt();
+ }
+ catch (Exception e)
+ {
+ Console.WriteLine($"Test Failure: {e}");
+ return 101;
+ }
+
+ return 100;
+ }
+ }
+} \ No newline at end of file
diff --git a/src/tests/issues.targets b/src/tests/issues.targets
index 7fe259b9d01..90950afa3a8 100644
--- a/src/tests/issues.targets
+++ b/src/tests/issues.targets
@@ -1235,6 +1235,9 @@
<ExcludeList Include="$(XunitTestBinBase)Interop/NativeLibrary/AssemblyLoadContext/ResolveUnmanagedDllTests/** ">
<Issue>https://github.com/dotnet/runtime/issues/41180</Issue>
</ExcludeList>
+ <ExcludeList Include="$(XunitTestBinBase)/Interop/ObjectiveC/ObjectiveCMarshalAPI/**">
+ <Issue>needs triage</Issue>
+ </ExcludeList>
<ExcludeList Include="$(XunitTestBinBase)/Interop/PInvoke/Array/MarshalArrayAsField/AsByValArray/AsByValArrayTest/**">
<Issue>needs triage</Issue>
</ExcludeList>