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 index 101978434bd612..2fc4a1e2e254d7 100644 --- 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 @@ -2,6 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. using System.Runtime.CompilerServices; +using System.Runtime.ExceptionServices; using System.Runtime.Versioning; namespace System.Runtime.InteropServices.ObjectiveC @@ -39,6 +40,16 @@ private static partial IntPtr CreateReferenceTrackingHandleInternal( out int memInSizeT, out IntPtr mem); + [StackTraceHidden] + internal static void ThrowPendingExceptionObject() + { + Exception? ex = System.StubHelpers.StubHelpers.GetPendingExceptionObject(); + if (ex is not null) + { + ExceptionDispatchInfo.Throw(ex); + } + } + [UnmanagedCallersOnly] internal static unsafe void* InvokeUnhandledExceptionPropagation(Exception* pExceptionArg, IntPtr methodDesc, IntPtr* pContext, Exception* pException) { diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/ReadyToRunCodegenCompilation.cs b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/ReadyToRunCodegenCompilation.cs index 82c54c7b8d9a95..4345e856b029e1 100644 --- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/ReadyToRunCodegenCompilation.cs +++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/ReadyToRunCodegenCompilation.cs @@ -221,7 +221,16 @@ protected override MethodILData CreateValueFromKey(MethodDesc key) && key.IsPInvoke && _compilationModuleGroup.GeneratesPInvoke(key)) { - methodIL = PInvokeILEmitter.EmitIL(key); + try + { + methodIL = PInvokeILEmitter.EmitIL(key); + } + catch (RequiresRuntimeJitException) + { + // The P/Invoke IL emitter will throw for known unsupported scenarios. + // We don't propagate the exception and keep methodIL as null - this causes a fall back to JIT during runtime. + // We store the null methodIL in the IL cache so we don't keep trying to emit unsupported P/Invoke IL. + } } return new MethodILData() { Method = key, MethodIL = methodIL }; diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/ReadyToRunCompilationModuleGroupBase.cs b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/ReadyToRunCompilationModuleGroupBase.cs index 96f78ba0309247..facd3aaf1ca93d 100644 --- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/ReadyToRunCompilationModuleGroupBase.cs +++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/ReadyToRunCompilationModuleGroupBase.cs @@ -716,7 +716,13 @@ public sealed override bool GeneratesPInvoke(MethodDesc method) if (!VersionsWithMethodBody(method)) return false; - return !Marshaller.IsMarshallingRequired(method); + if (!Marshaller.IsMarshallingRequired(method)) + return true; + + if (MarshalHelpers.ShouldCheckForPendingException(method.Context.Target, method.GetPInvokeMethodMetadata())) + return true; + + return false; } public sealed override bool TryGetModuleTokenForExternalType(TypeDesc type, out ModuleToken token) diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/IL/Stubs/PInvokeILEmitter.cs b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/IL/Stubs/PInvokeILEmitter.cs index ed05deb5f23581..319e39dda7b3d5 100644 --- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/IL/Stubs/PInvokeILEmitter.cs +++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/IL/Stubs/PInvokeILEmitter.cs @@ -56,6 +56,14 @@ private void EmitPInvokeCall(PInvokeILCodeStreams ilCodeStreams) callsiteSetupCodeStream.Emit(ILOpcode.call, emitter.NewToken(rawTargetMethod)); + if (MarshalHelpers.ShouldCheckForPendingException(context.Target, _importMetadata)) + { + MetadataType objcMarshalType = context.SystemModule.GetKnownType( + "System.Runtime.InteropServices.ObjectiveC"u8, "ObjectiveCMarshal"u8); + callsiteSetupCodeStream.Emit(ILOpcode.call, emitter.NewToken( + objcMarshalType.GetKnownMethod("ThrowPendingExceptionObject"u8, null))); + } + static PInvokeTargetNativeMethod AllocateTargetNativeMethod(MethodDesc targetMethod, MethodSignature nativeSigArg) { var contextMethods = s_contexts.GetOrCreateValue(targetMethod.Context); @@ -72,9 +80,6 @@ private MethodIL EmitIL() if (!_importMetadata.Flags.PreserveSig) throw new NotSupportedException(); - if (MarshalHelpers.ShouldCheckForPendingException(_targetMethod.Context.Target, _importMetadata)) - throw new NotSupportedException(); - if (_targetMethod.IsUnmanagedCallersOnly) throw new NotSupportedException(); diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/JitInterface/CorInfoImpl.ReadyToRun.cs b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/JitInterface/CorInfoImpl.ReadyToRun.cs index 78827a157780e6..30df7c07b545d8 100644 --- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/JitInterface/CorInfoImpl.ReadyToRun.cs +++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/JitInterface/CorInfoImpl.ReadyToRun.cs @@ -3125,23 +3125,11 @@ private bool pInvokeMarshalingRequired(CORINFO_METHOD_STRUCT_* handle, CORINFO_S return true; } - MethodIL stubIL = null; - try + MethodIL stubIL = _compilation.GetMethodIL(method); + if (stubIL == null) { - stubIL = _compilation.GetMethodIL(method); - if (stubIL == null) - { - // This is the case of a PInvoke method that requires marshallers, which we can't use in this compilation - Debug.Assert(!_compilation.NodeFactory.CompilationModuleGroup.GeneratesPInvoke(method)); - return true; - } - } - catch (RequiresRuntimeJitException) - { - // The PInvoke IL emitter will throw for known unsupported scenario. We cannot propagate the exception here since - // this interface call might be used to check if a certain pinvoke can be inlined in the caller. Throwing means that the - // caller will not get compiled. Instead, we'll return true to let the JIT know that it cannot inline the pinvoke, and - // the actual pinvoke call will be handled by a stub that we create and compile in the runtime. + // This is the case of a P/Invoke method that requires an IL stub, which we can't use in this compilation. + // Return true so the JIT won't inline the P/Invoke — it will be handled by a runtime-generated stub. return true; } diff --git a/src/coreclr/vm/corelib.h b/src/coreclr/vm/corelib.h index df3c00eaac1db3..469f576fecbf88 100644 --- a/src/coreclr/vm/corelib.h +++ b/src/coreclr/vm/corelib.h @@ -450,6 +450,7 @@ DEFINE_FIELD_U(_buckets, GCHandleSetObject, _buckets) #ifdef FEATURE_OBJCMARSHAL DEFINE_CLASS(OBJCMARSHAL, ObjectiveC, ObjectiveCMarshal) DEFINE_METHOD(OBJCMARSHAL, INVOKEUNHANDLEDEXCEPTIONPROPAGATION, InvokeUnhandledExceptionPropagation, SM_PtrException_IntPtr_PtrIntPtr_PtrException_RetVoidPtr) +DEFINE_METHOD(OBJCMARSHAL, THROWPENDINGEXCEPTIONOBJECT, ThrowPendingExceptionObject, SM_RetVoid) #endif // FEATURE_OBJCMARSHAL DEFINE_CLASS(IENUMERATOR, Collections, IEnumerator)