diff options
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> |