diff --git a/eng/Version.Details.xml b/eng/Version.Details.xml index 7bf60f1f1948b..57aa8f6d5e362 100644 --- a/eng/Version.Details.xml +++ b/eng/Version.Details.xml @@ -117,9 +117,9 @@ - + https://github.com/dotnet/source-build-externals - 949db2fd23b687c0d545e954943feada8b361ed6 + 2c52f66055a098987321c8fe96472679661c4071 diff --git a/src/libraries/System.Net.Security/src/System/Net/Security/SslStreamCertificateContext.Linux.cs b/src/libraries/System.Net.Security/src/System/Net/Security/SslStreamCertificateContext.Linux.cs index 0a5b5c89004b7..82ba564cc0a66 100644 --- a/src/libraries/System.Net.Security/src/System/Net/Security/SslStreamCertificateContext.Linux.cs +++ b/src/libraries/System.Net.Security/src/System/Net/Security/SslStreamCertificateContext.Linux.cs @@ -154,6 +154,17 @@ partial void AddRootCertificate(X509Certificate2? rootCertificate, ref bool tran return ValueTask.FromResult(_ocspResponse); } + internal ValueTask WaitForPendingOcspFetchAsync() + { + Task? pending = _pendingDownload; + if (pending is not null && !pending.IsFaulted) + { + return new ValueTask(pending); + } + + return ValueTask.FromResult(DateTimeOffset.UtcNow <= _ocspExpiration ? _ocspResponse : null); + } + private ValueTask DownloadOcspAsync() { Task? pending = _pendingDownload; diff --git a/src/libraries/System.Net.Security/tests/UnitTests/SslStreamCertificateContextOcspLinuxTests.cs b/src/libraries/System.Net.Security/tests/UnitTests/SslStreamCertificateContextOcspLinuxTests.cs index a4d705fa454d3..b712f814a02bb 100644 --- a/src/libraries/System.Net.Security/tests/UnitTests/SslStreamCertificateContextOcspLinuxTests.cs +++ b/src/libraries/System.Net.Security/tests/UnitTests/SslStreamCertificateContextOcspLinuxTests.cs @@ -15,6 +15,7 @@ namespace System.Net.Security.Tests; +[PlatformSpecific(TestPlatforms.Linux)] public class SslStreamCertificateContextOcspLinuxTests { [Fact] @@ -101,7 +102,8 @@ await SimpleTest(PkiOptions.OcspEverywhere, async (root, intermediate, endEntity intermediate.RevocationExpiration = DateTimeOffset.UtcNow.Add(SslStreamCertificateContext.MinRefreshBeforeExpirationInterval); SslStreamCertificateContext ctx = ctxFactory(false); - byte[] ocsp = await ctx.GetOcspResponseAsync(); + byte[] ocsp = await ctx.WaitForPendingOcspFetchAsync(); + Assert.NotNull(ocsp); intermediate.RevocationExpiration = DateTimeOffset.UtcNow.AddDays(1); @@ -111,12 +113,10 @@ await SimpleTest(PkiOptions.OcspEverywhere, async (root, intermediate, endEntity byte[] ocsp2 = ctx.GetOcspResponseNoWaiting(); Assert.Equal(ocsp, ocsp2); - await RetryHelper.ExecuteAsync(async () => - { - byte[] ocsp3 = await ctx.GetOcspResponseAsync(); - Assert.NotNull(ocsp3); - Assert.NotEqual(ocsp, ocsp3); - }, maxAttempts: 5, backoffFunc: i => (i + 1) * 200 /* ms */); + // The download should succeed + byte[] ocsp3 = await ctx.WaitForPendingOcspFetchAsync(); + Assert.NotNull(ocsp3); + Assert.NotEqual(ocsp, ocsp3); }); } @@ -128,7 +128,10 @@ await SimpleTest(PkiOptions.OcspEverywhere, async (root, intermediate, endEntity intermediate.RevocationExpiration = DateTimeOffset.UtcNow.AddSeconds(1); SslStreamCertificateContext ctx = ctxFactory(false); + // Make sure the inner OCSP fetch finished + await ctx.WaitForPendingOcspFetchAsync(); + // wait until the cached OCSP response expires await Task.Delay(2000); intermediate.RevocationExpiration = DateTimeOffset.UtcNow.AddDays(1); @@ -137,8 +140,8 @@ await SimpleTest(PkiOptions.OcspEverywhere, async (root, intermediate, endEntity byte[] ocsp = ctx.GetOcspResponseNoWaiting(); Assert.Null(ocsp); - // subsequent call will return the new response - byte[] ocsp2 = await ctx.GetOcspResponseAsync(); + // The download should succeed + byte[] ocsp2 = await ctx.WaitForPendingOcspFetchAsync(); Assert.NotNull(ocsp2); }); } @@ -155,25 +158,34 @@ await SimpleTest(PkiOptions.OcspEverywhere, async (root, intermediate, endEntity intermediate.RevocationExpiration = DateTimeOffset.UtcNow.Add(SslStreamCertificateContext.MinRefreshBeforeExpirationInterval); SslStreamCertificateContext ctx = ctxFactory(false); - byte[] ocsp = await ctx.GetOcspResponseAsync(); + // Make sure the inner OCSP fetch finished + byte[] ocsp = await ctx.WaitForPendingOcspFetchAsync(); Assert.NotNull(ocsp); responder.RespondKind = RespondKind.Invalid; - for (int i = 0; i < 3; i++) + for (int i = 0; i < 2; i++) { - await Task.Delay(SslStreamCertificateContext.RefreshAfterFailureBackOffInterval); byte[] ocsp2 = await ctx.GetOcspResponseAsync(); + await ctx.WaitForPendingOcspFetchAsync(); Assert.Equal(ocsp, ocsp2); + await Task.Delay(SslStreamCertificateContext.RefreshAfterFailureBackOffInterval.Add(TimeSpan.FromSeconds(1))); } + // make sure we try again only after backoff expires + await ctx.WaitForPendingOcspFetchAsync(); + await Task.Delay(SslStreamCertificateContext.RefreshAfterFailureBackOffInterval.Add(TimeSpan.FromSeconds(1))); + // after responder comes back online, the staple is eventually refreshed + intermediate.RevocationExpiration = DateTimeOffset.UtcNow.AddDays(1); responder.RespondKind = RespondKind.Normal; - await RetryHelper.ExecuteAsync(async () => - { - byte[] ocsp3 = await ctx.GetOcspResponseAsync(); - Assert.NotNull(ocsp3); - Assert.NotEqual(ocsp, ocsp3); - }, maxAttempts: 5, backoffFunc: i => (i + 1) * 200 /* ms */); + + // dispatch background refresh (first call still returns the old cached value) + await ctx.GetOcspResponseAsync(); + + // after refresh we should have a new staple + byte[] ocsp3 = await ctx.WaitForPendingOcspFetchAsync(); + Assert.NotNull(ocsp3); + Assert.NotEqual(ocsp, ocsp3); }); } diff --git a/src/libraries/tests.proj b/src/libraries/tests.proj index f14356c960ddc..bae88e91ca34b 100644 --- a/src/libraries/tests.proj +++ b/src/libraries/tests.proj @@ -64,6 +64,8 @@ + + diff --git a/src/mono/mono/metadata/icall.c b/src/mono/mono/metadata/icall.c index a6b32288f2818..7bcddb230a0da 100644 --- a/src/mono/mono/metadata/icall.c +++ b/src/mono/mono/metadata/icall.c @@ -2813,7 +2813,7 @@ ves_icall_RuntimeType_GetCallingConventionFromFunctionPointerInternal (MonoQCall MonoType *type = type_handle.type; g_assert (type->type == MONO_TYPE_FNPTR); // FIXME: Once we address: https://github.com/dotnet/runtime/issues/90308 this should not be needed anymore - return GUINT_TO_INT8 (type->data.method->suppress_gc_transition ? MONO_CALL_UNMANAGED_MD : type->data.method->call_convention); + return GUINT_TO_INT8 (mono_method_signature_has_ext_callconv (type->data.method, MONO_EXT_CALLCONV_SUPPRESS_GC_TRANSITION) ? MONO_CALL_UNMANAGED_MD : type->data.method->call_convention); } MonoBoolean diff --git a/src/mono/mono/metadata/loader.c b/src/mono/mono/metadata/loader.c index 5f21d3371c3c1..67c613b9631ae 100644 --- a/src/mono/mono/metadata/loader.c +++ b/src/mono/mono/metadata/loader.c @@ -634,6 +634,7 @@ inflate_generic_signature_checked (MonoImage *image, MonoMethodSignature *sig, M res->explicit_this = sig->explicit_this; res->call_convention = sig->call_convention; res->pinvoke = sig->pinvoke; + res->ext_callconv = sig->ext_callconv; res->generic_param_count = sig->generic_param_count; res->sentinelpos = sig->sentinelpos; res->has_type_parameters = is_open; diff --git a/src/mono/mono/metadata/marshal.c b/src/mono/mono/metadata/marshal.c index 8d68b450556f2..473f06fc9cac5 100644 --- a/src/mono/mono/metadata/marshal.c +++ b/src/mono/mono/metadata/marshal.c @@ -127,6 +127,9 @@ static GENERATE_TRY_GET_CLASS_WITH_CACHE (suppress_gc_transition_attribute, "Sys static GENERATE_TRY_GET_CLASS_WITH_CACHE (unmanaged_callers_only_attribute, "System.Runtime.InteropServices", "UnmanagedCallersOnlyAttribute") static GENERATE_TRY_GET_CLASS_WITH_CACHE (unmanaged_callconv_attribute, "System.Runtime.InteropServices", "UnmanagedCallConvAttribute") +GENERATE_TRY_GET_CLASS_WITH_CACHE (swift_error, "System.Runtime.InteropServices.Swift", "SwiftError") +GENERATE_TRY_GET_CLASS_WITH_CACHE (swift_self, "System.Runtime.InteropServices.Swift", "SwiftSelf") + static gboolean type_is_blittable (MonoType *type); static IlgenCallbacksToMono ilgenCallbacksToMono = { @@ -3232,6 +3235,8 @@ mono_marshal_set_callconv_for_type(MonoType *type, MonoMethodSignature *csig, gb csig->call_convention = MONO_CALL_FASTCALL; else if (!strcmp (class_name, "CallConvThiscall")) csig->call_convention = MONO_CALL_THISCALL; + else if (!strcmp (class_name, "CallConvSwift")) + csig->ext_callconv |= MONO_EXT_CALLCONV_SWIFTCALL; else if (!strcmp (class_name, "CallConvSuppressGCTransition") && skip_gc_trans != NULL) *skip_gc_trans = TRUE; } @@ -3358,8 +3363,10 @@ mono_marshal_set_signature_callconv_from_attribute(MonoMethodSignature *sig, Mon sig->call_convention = MONO_CALL_THISCALL; else if (!strcmp (name, "Fastcall")) sig->call_convention = MONO_CALL_FASTCALL; + else if (!strcmp (name, "Swift")) + sig->ext_callconv |= MONO_EXT_CALLCONV_SWIFTCALL; else if (!strcmp (name, "SuppressGCTransition")) - sig->suppress_gc_transition = 1; + sig->ext_callconv |= MONO_EXT_CALLCONV_SUPPRESS_GC_TRANSITION; // TODO: Support CallConvMemberFunction? // TODO: Support multiple calling convetions? // - Switch MonoCallConvention enum values to powers of 2 @@ -3427,23 +3434,31 @@ mono_marshal_get_native_wrapper (MonoMethod *method, gboolean check_exceptions, MonoMethodSignature *sig, *csig; MonoMethodPInvoke *piinfo = (MonoMethodPInvoke *) method; MonoMethodBuilder *mb; - MonoMarshalSpec **mspecs; + MonoMarshalSpec **mspecs = NULL; MonoMethod *res; GHashTable *cache; gboolean pinvoke = FALSE; gboolean skip_gc_trans = FALSE; - gboolean pinvoke_not_found = FALSE; gpointer iter; - ERROR_DECL (emitted_error); WrapperInfo *info; + MonoType *string_type; + GHashTable **cache_ptr; + ERROR_DECL (emitted_error); g_assert (method != NULL); g_assertf (mono_method_signature_internal (method)->pinvoke, "%s flags:%X iflags:%X param_count:%X", method->name, method->flags, method->iflags, mono_method_signature_internal (method)->param_count); - GHashTable **cache_ptr; - - MonoType *string_type = m_class_get_byval_arg (mono_defaults.string_class); + if (MONO_CLASS_IS_IMPORT (method->klass)) { + /* The COM code is not AOT compatible, it calls mono_custom_attrs_get_attr_checked () */ + if (aot) + return method; +#ifndef DISABLE_COM + return mono_cominterop_get_native_wrapper (method); +#else + g_assert_not_reached (); +#endif + } if (aot) { if (check_exceptions) @@ -3470,17 +3485,6 @@ mono_marshal_get_native_wrapper (MonoMethod *method, gboolean check_exceptions, } } - if (MONO_CLASS_IS_IMPORT (method->klass)) { - /* The COM code is not AOT compatible, it calls mono_custom_attrs_get_attr_checked () */ - if (aot) - return method; -#ifndef DISABLE_COM - return mono_cominterop_get_native_wrapper (method); -#else - g_assert_not_reached (); -#endif - } - sig = mono_method_signature_internal (method); if (!(method->iflags & METHOD_IMPL_ATTRIBUTE_INTERNAL_CALL) && @@ -3493,12 +3497,30 @@ mono_marshal_get_native_wrapper (MonoMethod *method, gboolean check_exceptions, mono_error_set_generic_error (emitted_error, "System", "MissingMethodException", "Method contains unsupported native code"); else if (!aot) mono_lookup_pinvoke_call_internal (method, emitted_error); - } else { - if (!aot || (method->klass == mono_defaults.string_class)) - piinfo->addr = mono_lookup_internal_call (method); + } else if (!aot || (method->klass == mono_defaults.string_class)) { + piinfo->addr = mono_lookup_internal_call (method); } } + mb = mono_mb_new (method->klass, method->name, MONO_WRAPPER_MANAGED_TO_NATIVE); + mb->method->save_lmf = 1; + + if (G_UNLIKELY (pinvoke && mono_method_has_unmanaged_callers_only_attribute (method))) { + /* + * In AOT mode and embedding scenarios, it is possible that the icall is not registered in the runtime doing the AOT compilation. + * Emit a wrapper that throws a NotSupportedException. + */ + get_marshal_cb ()->mb_emit_exception (mb, "System", "NotSupportedException", "Method canot be marked with both DllImportAttribute and UnmanagedCallersOnlyAttribute"); + goto emit_exception_for_error; + } else if (!pinvoke && !piinfo->addr && !aot) { + /* if there's no code but the error isn't set, just use a fairly generic exception. */ + if (is_ok (emitted_error)) + mono_error_set_generic_error (emitted_error, "System", "MissingMethodException", ""); + get_marshal_cb ()->mb_emit_exception_for_error (mb, emitted_error); + goto emit_exception_for_error; + } + + string_type = m_class_get_byval_arg (mono_defaults.string_class); /* hack - redirect certain string constructors to CreateString */ if (piinfo->addr == ves_icall_System_String_ctor_RedirectToCreateString) { MonoMethod *m; @@ -3555,51 +3577,6 @@ mono_marshal_get_native_wrapper (MonoMethod *method, gboolean check_exceptions, return res; } - mb = mono_mb_new (method->klass, method->name, MONO_WRAPPER_MANAGED_TO_NATIVE); - - mb->method->save_lmf = 1; - - if (G_UNLIKELY (pinvoke && mono_method_has_unmanaged_callers_only_attribute (method))) { - /* emit a wrapper that throws a NotSupportedException */ - get_marshal_cb ()->mb_emit_exception (mb, "System", "NotSupportedException", "Method canot be marked with both DllImportAttribute and UnmanagedCallersOnlyAttribute"); - - info = mono_wrapper_info_create (mb, WRAPPER_SUBTYPE_NONE); - info->d.managed_to_native.method = method; - - csig = mono_metadata_signature_dup_full (get_method_image (method), sig); - csig->pinvoke = 0; - res = mono_mb_create_and_cache_full (cache, method, mb, csig, - csig->param_count + 16, info, NULL); - mono_mb_free (mb); - - return res; - } - - /* - * In AOT mode and embedding scenarios, it is possible that the icall is not - * registered in the runtime doing the AOT compilation. - */ - /* Handled at runtime */ - pinvoke_not_found = !pinvoke && !piinfo->addr && !aot; - if (pinvoke_not_found) { - /* if there's no code but the error isn't set, just use a fairly generic exception. */ - if (is_ok (emitted_error)) - mono_error_set_generic_error (emitted_error, "System", "MissingMethodException", ""); - get_marshal_cb ()->mb_emit_exception_for_error (mb, emitted_error); - mono_error_cleanup (emitted_error); - - info = mono_wrapper_info_create (mb, WRAPPER_SUBTYPE_NONE); - info->d.managed_to_native.method = method; - - csig = mono_metadata_signature_dup_full (get_method_image (method), sig); - csig->pinvoke = 0; - res = mono_mb_create_and_cache_full (cache, method, mb, csig, - csig->param_count + 16, info, NULL); - mono_mb_free (mb); - - return res; - } - /* internal calls: we simply push all arguments and call the method (no conversions) */ if (method->iflags & (METHOD_IMPL_ATTRIBUTE_INTERNAL_CALL | METHOD_IMPL_ATTRIBUTE_RUNTIME)) { if (sig->hasthis) @@ -3607,81 +3584,123 @@ mono_marshal_get_native_wrapper (MonoMethod *method, gboolean check_exceptions, else csig = mono_metadata_signature_dup_full (get_method_image (method), sig); - //printf ("%s\n", mono_method_full_name (method, 1)); - /* hack - string constructors returns a value */ if (method->string_ctor) csig->ret = string_type; get_marshal_cb ()->emit_native_icall_wrapper (mb, method, csig, check_exceptions, aot, piinfo); + } else { + g_assert(pinvoke); - info = mono_wrapper_info_create (mb, WRAPPER_SUBTYPE_NONE); - info->d.managed_to_native.method = method; + csig = mono_metadata_signature_dup_full (get_method_image (method), sig); + mono_marshal_set_callconv_from_modopt (method, csig, FALSE); - csig = mono_metadata_signature_dup_full (get_method_image (method), csig); - csig->pinvoke = 0; - res = mono_mb_create_and_cache_full (cache, method, mb, csig, csig->param_count + 16, - info, NULL); + mspecs = g_new0 (MonoMarshalSpec*, sig->param_count + 1); + mono_method_get_marshal_info (method, mspecs); - mono_mb_free (mb); - return res; - } + if (mono_class_try_get_suppress_gc_transition_attribute_class ()) { + MonoCustomAttrInfo *cinfo; + ERROR_DECL (error); - g_assert (pinvoke); + cinfo = mono_custom_attrs_from_method_checked (method, error); + mono_error_assert_ok (error); + gboolean found = FALSE; + if (cinfo) { + for (int i = 0; i < cinfo->num_attrs; ++i) { + MonoClass *ctor_class = cinfo->attrs [i].ctor->klass; + if (ctor_class == mono_class_try_get_suppress_gc_transition_attribute_class ()) { + found = TRUE; + break; + } + } + } + if (found) + skip_gc_trans = TRUE; + if (cinfo && !cinfo->cached) + mono_custom_attrs_free (cinfo); + } - csig = mono_metadata_signature_dup_full (get_method_image (method), sig); - mono_marshal_set_callconv_from_modopt (method, csig, FALSE); + if (csig->call_convention == MONO_CALL_DEFAULT) { + /* If the calling convention has not been set, check the UnmanagedCallConv attribute */ + mono_marshal_set_callconv_from_unmanaged_callconv_attribute (method, csig, &skip_gc_trans); + } - mspecs = g_new0 (MonoMarshalSpec*, sig->param_count + 1); - mono_method_get_marshal_info (method, mspecs); + if (mono_method_signature_has_ext_callconv (csig, MONO_EXT_CALLCONV_SWIFTCALL)) { + MonoClass *swift_self = mono_class_try_get_swift_self_class (); + MonoClass *swift_error = mono_class_try_get_swift_error_class (); + MonoClass *swift_error_ptr = mono_class_create_ptr (m_class_get_this_arg (swift_error)); + int swift_error_args = 0, swift_self_args = 0; + for (int i = 0; i < method->signature->param_count; ++i) { + MonoClass *param_klass = mono_class_from_mono_type_internal (method->signature->params [i]); + if (param_klass) { + if (param_klass == swift_error && !m_type_is_byref (method->signature->params [i])) { + swift_error_args = swift_self_args = 0; + mono_error_set_generic_error (emitted_error, "System", "InvalidProgramException", "SwiftError argument must be passed by reference."); + break; + } else if (param_klass == swift_error || param_klass == swift_error_ptr) { + swift_error_args++; + } else if (param_klass == swift_self) { + swift_self_args++; + } else if (!m_class_is_blittable (param_klass) && m_class_get_this_arg (param_klass)->type != MONO_TYPE_FNPTR) { + swift_error_args = swift_self_args = 0; + mono_error_set_generic_error (emitted_error, "System", "InvalidProgramException", "Passing non-primitive value types to a P/Invoke with the Swift calling convention is unsupported."); + break; + } + } + } - if (mono_class_try_get_suppress_gc_transition_attribute_class ()) { - MonoCustomAttrInfo *cinfo; - ERROR_DECL (error); + if (swift_self_args > 1 || swift_error_args > 1) { + mono_error_set_generic_error (emitted_error, "System", "InvalidProgramException", "Method signature contains multiple SwiftSelf or SwiftError arguments."); + } - cinfo = mono_custom_attrs_from_method_checked (method, error); - mono_error_assert_ok (error); - gboolean found = FALSE; - if (cinfo) { - for (int i = 0; i < cinfo->num_attrs; ++i) { - MonoClass *ctor_class = cinfo->attrs [i].ctor->klass; - if (ctor_class == mono_class_try_get_suppress_gc_transition_attribute_class ()) { - found = TRUE; - break; - } + if (!is_ok (emitted_error)) { + get_marshal_cb ()->mb_emit_exception_for_error (mb, emitted_error); + goto emit_exception_for_error; } } - if (found) - skip_gc_trans = TRUE; - if (cinfo && !cinfo->cached) - mono_custom_attrs_free (cinfo); - } - if (csig->call_convention == MONO_CALL_DEFAULT) { - /* If the calling convention has not been set, check the UnmanagedCallConv attribute */ - mono_marshal_set_callconv_from_unmanaged_callconv_attribute (method, csig, &skip_gc_trans); + MonoNativeWrapperFlags flags = aot ? EMIT_NATIVE_WRAPPER_AOT : (MonoNativeWrapperFlags)0; + flags |= check_exceptions ? EMIT_NATIVE_WRAPPER_CHECK_EXCEPTIONS : (MonoNativeWrapperFlags)0; + flags |= skip_gc_trans ? EMIT_NATIVE_WRAPPER_SKIP_GC_TRANS : (MonoNativeWrapperFlags)0; + flags |= runtime_marshalling_enabled (get_method_image (method)) ? EMIT_NATIVE_WRAPPER_RUNTIME_MARSHALLING_ENABLED : (MonoNativeWrapperFlags)0; + + mono_marshal_emit_native_wrapper (get_method_image (mb->method), mb, csig, piinfo, mspecs, piinfo->addr, flags); } - MonoNativeWrapperFlags flags = aot ? EMIT_NATIVE_WRAPPER_AOT : (MonoNativeWrapperFlags)0; - flags |= check_exceptions ? EMIT_NATIVE_WRAPPER_CHECK_EXCEPTIONS : (MonoNativeWrapperFlags)0; - flags |= skip_gc_trans ? EMIT_NATIVE_WRAPPER_SKIP_GC_TRANS : (MonoNativeWrapperFlags)0; - flags |= runtime_marshalling_enabled (get_method_image (method)) ? EMIT_NATIVE_WRAPPER_RUNTIME_MARSHALLING_ENABLED : (MonoNativeWrapperFlags)0; + info = mono_wrapper_info_create (mb, method->iflags & (METHOD_IMPL_ATTRIBUTE_INTERNAL_CALL | METHOD_IMPL_ATTRIBUTE_RUNTIME) + ? WRAPPER_SUBTYPE_NONE + : WRAPPER_SUBTYPE_PINVOKE); - mono_marshal_emit_native_wrapper (get_method_image (mb->method), mb, csig, piinfo, mspecs, piinfo->addr, flags); - info = mono_wrapper_info_create (mb, WRAPPER_SUBTYPE_PINVOKE); info->d.managed_to_native.method = method; + if (method->iflags & (METHOD_IMPL_ATTRIBUTE_INTERNAL_CALL | METHOD_IMPL_ATTRIBUTE_RUNTIME)) + csig = mono_metadata_signature_dup_full (get_method_image (method), sig); csig->pinvoke = 0; res = mono_mb_create_and_cache_full (cache, method, mb, csig, csig->param_count + 16, info, NULL); - mono_mb_free (mb); - for (int i = sig->param_count; i >= 0; i--) - if (mspecs [i]) - mono_metadata_free_marshal_spec (mspecs [i]); - g_free (mspecs); + if (mspecs) { + for (int i = sig->param_count; i >= 0; i--) + if (mspecs [i]) + mono_metadata_free_marshal_spec (mspecs [i]); + g_free (mspecs); + } - return res; + goto leave; + + emit_exception_for_error: + mono_error_cleanup (emitted_error); + info = mono_wrapper_info_create (mb, WRAPPER_SUBTYPE_NONE); + info->d.managed_to_native.method = method; + + csig = mono_metadata_signature_dup_full (get_method_image (method), sig); + csig->pinvoke = 0; + res = mono_mb_create_and_cache_full (cache, method, mb, csig, + csig->param_count + 16, info, NULL); + + leave: + mono_mb_free (mb); + return res; } /** diff --git a/src/mono/mono/metadata/marshal.h b/src/mono/mono/metadata/marshal.h index eaef14bf7bdcf..91758b6ecf431 100644 --- a/src/mono/mono/metadata/marshal.h +++ b/src/mono/mono/metadata/marshal.h @@ -771,4 +771,7 @@ mono_mb_create_and_cache_full (GHashTable *cache, gpointer key, IlgenCallbacksToMono* mono_marshal_get_mono_callbacks_for_ilgen (void); +GENERATE_TRY_GET_CLASS_WITH_CACHE_DECL (swift_self) +GENERATE_TRY_GET_CLASS_WITH_CACHE_DECL (swift_error) + #endif /* __MONO_MARSHAL_H__ */ diff --git a/src/mono/mono/metadata/metadata-internals.h b/src/mono/mono/metadata/metadata-internals.h index b0d1858f7148e..05934225cbd90 100644 --- a/src/mono/mono/metadata/metadata-internals.h +++ b/src/mono/mono/metadata/metadata-internals.h @@ -656,11 +656,17 @@ struct _MonoMethodSignature { unsigned int pinvoke : 1; unsigned int is_inflated : 1; unsigned int has_type_parameters : 1; - unsigned int suppress_gc_transition : 1; unsigned int marshalling_disabled : 1; + uint8_t ext_callconv; // see MonoExtCallConv MonoType *params [MONO_ZERO_LEN_ARRAY]; }; +typedef enum { + MONO_EXT_CALLCONV_SUPPRESS_GC_TRANSITION = 0x01, + MONO_EXT_CALLCONV_SWIFTCALL = 0x02, + /// see MonoMethodSignature:ext_callconv - only 8 bits +} MonoExtCallConv; + /* * AOT cache configuration loaded from config files. * Doesn't really belong here. @@ -1275,4 +1281,9 @@ mono_class_set_deferred_type_load_failure (MonoClass *klass, const char * fmt, . gboolean mono_class_set_type_load_failure (MonoClass *klass, const char * fmt, ...); +static inline gboolean +mono_method_signature_has_ext_callconv (MonoMethodSignature *sig, MonoExtCallConv flags) { + return (sig->ext_callconv & flags) != 0; +} + #endif /* __MONO_METADATA_INTERNALS_H__ */ diff --git a/src/mono/mono/metadata/metadata.c b/src/mono/mono/metadata/metadata.c index 9b64083fdfd79..05b6166d8bf94 100644 --- a/src/mono/mono/metadata/metadata.c +++ b/src/mono/mono/metadata/metadata.c @@ -2570,7 +2570,7 @@ metadata_signature_set_modopt_call_conv (MonoMethodSignature *sig, MonoType *cmo if (count == 0) return; int base_callconv = sig->call_convention; - gboolean suppress_gc_transition = sig->suppress_gc_transition; + gboolean suppress_gc_transition = mono_method_signature_has_ext_callconv (sig, MONO_EXT_CALLCONV_SUPPRESS_GC_TRANSITION); for (uint8_t i = 0; i < count; ++i) { gboolean req = FALSE; MonoType *cmod = mono_type_get_custom_modifier (cmod_type, i, &req, error); @@ -2613,7 +2613,8 @@ metadata_signature_set_modopt_call_conv (MonoMethodSignature *sig, MonoType *cmo } } sig->call_convention = base_callconv; - sig->suppress_gc_transition = suppress_gc_transition; + if (suppress_gc_transition) + sig->ext_callconv |= MONO_EXT_CALLCONV_SUPPRESS_GC_TRANSITION; } /** @@ -2681,6 +2682,10 @@ mono_metadata_parse_method_signature_full (MonoImage *m, MonoGenericContainer *c break; } + if (mono_method_signature_has_ext_callconv (method, MONO_EXT_CALLCONV_SWIFTCALL)) { + method->pinvoke = 1; + } + if (call_convention != 0xa) { method->ret = mono_metadata_parse_type_checked (m, container, pattrs ? pattrs [0] : 0, FALSE, ptr, &ptr, error); if (!method->ret) { diff --git a/src/mono/mono/metadata/sre.c b/src/mono/mono/metadata/sre.c index 3f47f9e84d008..47c086595af06 100644 --- a/src/mono/mono/metadata/sre.c +++ b/src/mono/mono/metadata/sre.c @@ -976,6 +976,7 @@ create_method_token (MonoDynamicImage *assembly, MonoMethod *method, MonoArrayHa sig->hasthis = old->hasthis; sig->explicit_this = old->explicit_this; sig->call_convention = old->call_convention; + sig->ext_callconv = old->ext_callconv; sig->generic_param_count = old->generic_param_count; sig->param_count = old->param_count + GINT_TO_UINT16 (nargs); sig->sentinelpos = old->param_count; diff --git a/src/mono/mono/mini/aot-compiler.c b/src/mono/mono/mini/aot-compiler.c index c646e5359c14f..1aab82ecc5271 100644 --- a/src/mono/mono/mini/aot-compiler.c +++ b/src/mono/mono/mini/aot-compiler.c @@ -3751,7 +3751,7 @@ static void encode_signature (MonoAotCompile *acfg, MonoMethodSignature *sig, guint8 *buf, guint8 **endbuf) { guint8 *p = buf; - guint8 flags = 0; + guint32 flags = 0; int i; /* Similar to the metadata encoding */ @@ -3759,14 +3759,18 @@ encode_signature (MonoAotCompile *acfg, MonoMethodSignature *sig, guint8 *buf, g flags |= 0x10; if (sig->hasthis) flags |= 0x20; - if (sig->explicit_this) - flags |= 0x40; if (sig->pinvoke) + flags |= 0x40; + if (sig->explicit_this) flags |= 0x80; + if (sig->ext_callconv) + flags |= 0x100; flags |= (sig->call_convention & 0x0F); - *p = flags; - ++p; + encode_value (flags, p, &p); + if (sig->ext_callconv) + encode_value (sig->ext_callconv, p, &p); + if (sig->generic_param_count) encode_value (sig->generic_param_count, p, &p); encode_value (sig->param_count, p, &p); diff --git a/src/mono/mono/mini/aot-runtime.c b/src/mono/mono/mini/aot-runtime.c index 96b8cb58dad48..e88b6965bb6bd 100644 --- a/src/mono/mono/mini/aot-runtime.c +++ b/src/mono/mono/mini/aot-runtime.c @@ -857,17 +857,21 @@ decode_signature_with_target (MonoAotModule *module, MonoMethodSignature *target guint16 param_count; unsigned int gen_param_count = 0; int call_conv; + uint8_t ext_callconv = 0; + guint8 *p = buf; gboolean hasthis, explicit_this, has_gen_params, pinvoke; - flags = *p; - p ++; + flags = decode_value (p, &p); has_gen_params = (flags & 0x10) != 0; hasthis = (flags & 0x20) != 0; - explicit_this = (flags & 0x40) != 0; - pinvoke = (flags & 0x80) != 0; + pinvoke = (flags & 0x40) != 0; + explicit_this = (flags & 0x80) != 0; call_conv = flags & 0x0F; + if ((flags & 0x100) != 0) + ext_callconv = GINT32_TO_UINT8 (decode_value (p, &p)); + if (has_gen_params) gen_param_count = decode_value (p, &p); param_count = GINT32_TO_UINT16 (decode_value (p, &p)); @@ -880,6 +884,7 @@ decode_signature_with_target (MonoAotModule *module, MonoMethodSignature *target sig->explicit_this = explicit_this; sig->pinvoke = pinvoke; sig->call_convention = call_conv; + sig->ext_callconv = ext_callconv; sig->generic_param_count = gen_param_count; sig->ret = decode_type (module, p, &p, error); if (!sig->ret) diff --git a/src/mono/mono/mini/decompose.c b/src/mono/mono/mini/decompose.c index 84221fc808afb..a0ecf8ff62553 100644 --- a/src/mono/mono/mini/decompose.c +++ b/src/mono/mono/mini/decompose.c @@ -216,9 +216,8 @@ decompose_long_opcode (MonoCompile *cfg, MonoInst *ins, MonoInst **repl_ins) #endif MONO_EMIT_NEW_LCOMPARE_IMM (cfg, ins->sreg1, 0x7fffffff); MONO_EMIT_NEW_COND_EXC (cfg, GT, "OverflowException"); - /* The int cast is needed for the VS compiler. See Compiler Warning (level 2) C4146. */ #if SIZEOF_REGISTER == 8 - MONO_EMIT_NEW_LCOMPARE_IMM (cfg, ins->sreg1, (-(int)2147483648)); + MONO_EMIT_NEW_LCOMPARE_IMM (cfg, ins->sreg1, INT_MIN); #else g_assert (COMPILE_LLVM (cfg)); MONO_EMIT_NEW_LCOMPARE_IMM (cfg, ins->sreg1, -2147483648LL); diff --git a/src/mono/mono/mini/interp/interp.c b/src/mono/mono/mini/interp/interp.c index 23bf79620a73e..afeb41929ec2c 100644 --- a/src/mono/mono/mini/interp/interp.c +++ b/src/mono/mono/mini/interp/interp.c @@ -1761,6 +1761,19 @@ ves_pinvoke_method ( interp_pop_lmf (&ext); #ifdef MONO_ARCH_HAVE_INTERP_PINVOKE_TRAMP +#ifdef MONO_ARCH_HAVE_SWIFTCALL + if (mono_method_signature_has_ext_callconv (sig, MONO_EXT_CALLCONV_SWIFTCALL)) { + int arg_index = -1; + gpointer data = mono_arch_get_swift_error (&ccontext, sig, &arg_index); + + // Perform an indirect store at arg_index stack location + if (arg_index >= 0) { + g_assert (data); + stackval *result = (stackval*) STACK_ADD_BYTES (frame.stack, get_arg_offset (frame.imethod, sig, arg_index)); + *(gpointer*)result->data.p = *(gpointer*)data; + } + } +#endif if (!context->has_resume_state) { mono_arch_get_native_call_context_ret (&ccontext, &frame, sig, call_info); } diff --git a/src/mono/mono/mini/interp/transform.c b/src/mono/mono/mini/interp/transform.c index 6f4a46e0b653b..dd195a29a4698 100644 --- a/src/mono/mono/mini/interp/transform.c +++ b/src/mono/mono/mini/interp/transform.c @@ -3243,7 +3243,7 @@ interp_transform_call (TransformData *td, MonoMethod *method, MonoMethod *target * the InterpMethod pointer. FIXME */ native = csignature->pinvoke || method->wrapper_type == MONO_WRAPPER_RUNTIME_INVOKE; - if (!method->dynamic && !method->wrapper_type && csignature->pinvoke && !csignature->suppress_gc_transition) { + if (!method->dynamic && !method->wrapper_type && csignature->pinvoke && !mono_method_signature_has_ext_callconv (csignature, MONO_EXT_CALLCONV_SUPPRESS_GC_TRANSITION)) { // native calli needs a wrapper target_method = mono_marshal_get_native_func_wrapper_indirect (method->klass, csignature, FALSE); calli = FALSE; @@ -3583,6 +3583,13 @@ interp_transform_call (TransformData *td, MonoMethod *method, MonoMethod *target int *call_args = create_call_args (td, num_args); +#ifndef MONO_ARCH_HAVE_SWIFTCALL + if (mono_method_signature_has_ext_callconv (csignature, MONO_EXT_CALLCONV_SWIFTCALL)) { + mono_error_set_not_supported (error, "CallConvSwift is not supported on this platform."); + return FALSE; + } +#endif + // We overwrite it with the return local, save it for future use if (csignature->param_count || csignature->hasthis) first_sreg = td->sp [0].var; @@ -3675,6 +3682,8 @@ interp_transform_call (TransformData *td, MonoMethod *method, MonoMethod *target default_cconv = csignature->call_convention == MONO_CALL_DEFAULT || csignature->call_convention == MONO_CALL_C; #endif #endif + // When using the Swift calling convention, emit MINT_CALLI_NAT opcode to manage context registers. + default_cconv = default_cconv && !mono_method_signature_has_ext_callconv (csignature, MONO_EXT_CALLCONV_SWIFTCALL); // FIXME calli receives both the args offset and sometimes another arg for the frame pointer, // therefore some args are in the param area, while the fp is not. We should differentiate for diff --git a/src/mono/mono/mini/method-to-ir.c b/src/mono/mono/mini/method-to-ir.c index 7944fc71bec4a..70f57b8a89ada 100644 --- a/src/mono/mono/mini/method-to-ir.c +++ b/src/mono/mono/mini/method-to-ir.c @@ -7544,7 +7544,7 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b method->dynamic case; for other wrapper types assume the code knows what its doing and added its own GC transitions */ - gboolean skip_gc_trans = fsig->suppress_gc_transition; + gboolean skip_gc_trans = mono_method_signature_has_ext_callconv (fsig, MONO_EXT_CALLCONV_SUPPRESS_GC_TRANSITION); if (!skip_gc_trans) { #if 0 fprintf (stderr, "generating wrapper for calli in method %s with wrapper type %s\n", method->name, mono_wrapper_type_to_str (method->wrapper_type)); @@ -11174,6 +11174,13 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b const MonoJitICallId jit_icall_id = (MonoJitICallId)token; MonoJitICallInfo * const jit_icall_info = mono_find_jit_icall_info (jit_icall_id); +#ifndef MONO_ARCH_HAVE_SWIFTCALL + if (mono_method_signature_has_ext_callconv (method->signature, MONO_EXT_CALLCONV_SWIFTCALL)) { + // Swift calling convention is not supported on this platform. + emit_not_supported_failure (cfg); + } +#endif + CHECK_STACK (jit_icall_info->sig->param_count); sp -= jit_icall_info->sig->param_count; diff --git a/src/mono/mono/mini/mini-amd64.c b/src/mono/mono/mini/mini-amd64.c index 4195828f7e32b..d424577821ba2 100644 --- a/src/mono/mono/mini/mini-amd64.c +++ b/src/mono/mono/mini/mini-amd64.c @@ -875,6 +875,7 @@ get_call_info (MonoMemPool *mp, MonoMethodSignature *sig) cinfo->nargs = n; cinfo->gsharedvt = mini_is_gsharedvt_variable_signature (sig); + cinfo->swift_error_index = -1; gr = 0; fr = 0; @@ -1008,6 +1009,36 @@ get_call_info (MonoMemPool *mp, MonoMethodSignature *sig) } ptype = mini_get_underlying_type (sig->params [i]); + +#ifdef MONO_ARCH_HAVE_SWIFTCALL + if (mono_method_signature_has_ext_callconv (sig, MONO_EXT_CALLCONV_SWIFTCALL)) { + MonoClass *swift_self = mono_class_try_get_swift_self_class (); + MonoClass *swift_error = mono_class_try_get_swift_error_class (); + MonoClass *swift_error_ptr = mono_class_create_ptr (m_class_get_this_arg (swift_error)); + MonoClass *klass = mono_class_from_mono_type_internal (sig->params [i]); + if (klass == swift_self && sig->pinvoke) { + guint32 size = mini_type_stack_size_full (m_class_get_byval_arg (klass), NULL, sig->pinvoke && !sig->marshalling_disabled); + g_assert (size == 8); + + ainfo->storage = ArgValuetypeInReg; + ainfo->pair_storage [0] = ArgInIReg; + ainfo->pair_storage [1] = ArgNone; + ainfo->nregs = 1; + ainfo->pair_regs [0] = GINT32_TO_UINT8 (AMD64_R13); + ainfo->pair_size [0] = size; + continue; + } else if (klass == swift_error || klass == swift_error_ptr) { + if (sig->pinvoke) + ainfo->reg = GINT32_TO_UINT8 (AMD64_R12); + else + add_general (&gr, &stack_size, ainfo); + ainfo->storage = ArgSwiftError; + cinfo->swift_error_index = i; + continue; + } + } +#endif + switch (ptype->type) { case MONO_TYPE_I1: ainfo->is_signed = 1; @@ -1133,6 +1164,8 @@ arg_get_storage (CallContext *ccontext, ArgInfo *ainfo) case ArgValuetypeAddrInIReg: g_assert (ainfo->pair_storage [0] == ArgInIReg && ainfo->pair_storage [1] == ArgNone); return &ccontext->gregs [ainfo->pair_regs [0]]; + case ArgSwiftError: + return &ccontext->gregs [AMD64_R12]; default: g_error ("Arg storage type not yet supported"); } @@ -1249,6 +1282,11 @@ mono_arch_set_native_call_context_args (CallContext *ccontext, gpointer frame, M else storage = arg_get_storage (ccontext, ainfo); + if (ainfo->storage == ArgSwiftError) { + *(gpointer*)storage = 0; + continue; + } + interp_cb->frame_arg_to_data ((MonoInterpFrameHandle)frame, sig, i, storage); if (temp_size) arg_set_val (ccontext, ainfo, storage); @@ -1360,6 +1398,30 @@ mono_arch_get_native_call_context_ret (CallContext *ccontext, gpointer frame, Mo } } +#ifdef MONO_ARCH_HAVE_SWIFTCALL +/** + * Gets error context from `ccontext` registers by indirectly storing the value onto the stack. + * + * The function searches for an argument with SwiftError type. + * If found, it retrieves the value from `ccontext`. + */ +gpointer +mono_arch_get_swift_error (CallContext *ccontext, MonoMethodSignature *sig, int *arg_index) +{ + MonoClass *swift_error = mono_class_try_get_swift_error_class (); + MonoClass *swift_error_ptr = mono_class_create_ptr (m_class_get_this_arg (swift_error)); + for (guint i = 0; i < sig->param_count + sig->hasthis; i++) { + MonoClass *klass = mono_class_from_mono_type_internal (sig->params [i]); + if (klass && (klass == swift_error || klass == swift_error_ptr)) { + *arg_index = i; + return &ccontext->gregs [AMD64_R12]; + } + } + + return NULL; +} +#endif + /* * mono_arch_get_argument_info: * @csig: a method signature @@ -1623,7 +1685,8 @@ mono_arch_get_global_int_regs (MonoCompile *cfg) /* We use the callee saved registers for global allocation */ regs = g_list_prepend (regs, (gpointer)AMD64_RBX); - regs = g_list_prepend (regs, (gpointer)AMD64_R12); + if (!mono_method_signature_has_ext_callconv (cfg->method->signature, MONO_EXT_CALLCONV_SWIFTCALL)) + regs = g_list_prepend (regs, (gpointer)AMD64_R12); regs = g_list_prepend (regs, (gpointer)AMD64_R13); regs = g_list_prepend (regs, (gpointer)AMD64_R14); regs = g_list_prepend (regs, (gpointer)AMD64_R15); @@ -1925,11 +1988,22 @@ mono_arch_allocate_vars (MonoCompile *cfg) break; } + case ArgSwiftError: { + ins->opcode = OP_REGOFFSET; + ins->inst_basereg = cfg->frame_reg; + ins->inst_offset = ainfo->offset + ARGS_OFFSET; + offset = ALIGN_TO (offset, sizeof (target_mgreg_t)); + offset += sizeof (target_mgreg_t); + + cfg->arch.swift_error_var = ins; + cfg->used_int_regs |= (size_t)(1 << AMD64_R12); + } + break; default: NOT_IMPLEMENTED; } - if (!inreg && (ainfo->storage != ArgOnStack) && (ainfo->storage != ArgValuetypeAddrInIReg) && (ainfo->storage != ArgValuetypeAddrOnStack) && (ainfo->storage != ArgGSharedVtOnStack)) { + if (!inreg && (ainfo->storage != ArgOnStack) && (ainfo->storage != ArgValuetypeAddrInIReg) && (ainfo->storage != ArgValuetypeAddrOnStack) && (ainfo->storage != ArgGSharedVtOnStack) && (ainfo->storage != ArgSwiftError)) { ins->opcode = OP_REGOFFSET; ins->inst_basereg = cfg->frame_reg; /* These arguments are saved to the stack in the prolog */ @@ -1997,6 +2071,9 @@ mono_arch_create_vars (MonoCompile *cfg) if (cfg->method->save_lmf) { cfg->lmf_ir = TRUE; } + + if (cinfo->swift_error_index >= 0) + cfg->args [cinfo->swift_error_index]->flags |= MONO_INST_VOLATILE; } static void @@ -2364,6 +2441,10 @@ mono_arch_emit_call (MonoCompile *cfg, MonoCallInst *call) } break; } + case ArgSwiftError: { + MONO_EMIT_NEW_I8CONST (cfg, ainfo->reg, 0); + break; + } default: g_assert_not_reached (); } @@ -4198,6 +4279,11 @@ emit_move_return_value (MonoCompile *cfg, MonoInst *ins, guint8 *code) CallInfo *cinfo; guint32 quad; + if (cfg->arch.swift_error_var) { + amd64_mov_reg_membase (code, AMD64_R11, cfg->arch.swift_error_var->inst_basereg, cfg->arch.swift_error_var->inst_offset, sizeof (target_mgreg_t)); + amd64_mov_membase_reg (code, AMD64_R11, 0, AMD64_R12, sizeof (target_mgreg_t)); + } + /* Move return value to the target register */ /* FIXME: do this in the local reg allocator */ switch (ins->opcode) { @@ -8161,6 +8247,14 @@ MONO_RESTORE_WARNING case ArgGSharedVtInReg: amd64_mov_membase_reg (code, ins->inst_basereg, ins->inst_offset, ainfo->reg, 8); break; + case ArgSwiftError: + if (ainfo->offset) { + amd64_mov_reg_membase (code, AMD64_R11, AMD64_RBP, ARGS_OFFSET + ainfo->offset, 8); + amd64_mov_membase_reg (code, cfg->arch.swift_error_var->inst_basereg, cfg->arch.swift_error_var->inst_offset, AMD64_R11, sizeof (target_mgreg_t)); + } else { + amd64_mov_membase_reg (code, cfg->arch.swift_error_var->inst_basereg, cfg->arch.swift_error_var->inst_offset, ainfo->reg, sizeof (target_mgreg_t)); + } + break; default: break; } diff --git a/src/mono/mono/mini/mini-amd64.h b/src/mono/mono/mini/mini-amd64.h index 33d4152ad7d24..0dda406f95b8b 100644 --- a/src/mono/mono/mini/mini-amd64.h +++ b/src/mono/mono/mini/mini-amd64.h @@ -211,6 +211,7 @@ typedef struct MonoCompileArch { MonoInst *ss_tramp_var; MonoInst *bp_tramp_var; MonoInst *lmf_var; + MonoInst *swift_error_var; #ifdef HOST_WIN32 struct _UNWIND_INFO* unwindinfo; #endif @@ -247,6 +248,9 @@ static const AMD64_XMM_Reg_No float_param_regs[] = {AMD64_XMM0, AMD64_XMM1, AMD6 static const AMD64_Reg_No return_regs [] = {AMD64_RAX, AMD64_RDX}; #endif +#define CTX_REGS 2 +#define CTX_REGS_OFFSET AMD64_R12 + typedef struct { /* Method address to call */ gpointer addr; @@ -297,6 +301,7 @@ typedef enum { ArgGSharedVtOnStack, /* Variable sized gsharedvt argument passed/returned by addr */ ArgGsharedvtVariableInReg, + ArgSwiftError, ArgNone /* only in pair_storage */ } ArgStorage; @@ -324,6 +329,7 @@ struct CallInfo { guint32 stack_usage; guint32 reg_usage; guint32 freg_usage; + gint32 swift_error_index; gboolean need_stack_align; gboolean gsharedvt; /* The index of the vret arg in the argument list */ @@ -487,6 +493,10 @@ typedef struct { // can pass context to generics or interfaces? #define MONO_ARCH_HAVE_VOLATILE_NON_PARAM_REGISTER 1 +#if defined(TARGET_OSX) || defined(TARGET_APPLE_MOBILE) +#define MONO_ARCH_HAVE_SWIFTCALL 1 +#endif + void mono_amd64_patch (unsigned char* code, gpointer target); diff --git a/src/mono/mono/mini/mini-arm64.c b/src/mono/mono/mini/mini-arm64.c index 0e3da92fd41c0..657aac2c79d8a 100644 --- a/src/mono/mono/mini/mini-arm64.c +++ b/src/mono/mono/mini/mini-arm64.c @@ -1858,6 +1858,34 @@ get_call_info (MonoMemPool *mp, MonoMethodSignature *sig) add_param (cinfo, &cinfo->sig_cookie, mono_get_int_type (), FALSE); } +#ifdef MONO_ARCH_HAVE_SWIFTCALL + if (mono_method_signature_has_ext_callconv (sig, MONO_EXT_CALLCONV_SWIFTCALL)) { + MonoClass *swift_self = mono_class_try_get_swift_self_class (); + MonoClass *swift_error = mono_class_try_get_swift_error_class (); + MonoClass *swift_error_ptr = mono_class_create_ptr (m_class_get_this_arg (swift_error)); + MonoClass *klass = mono_class_from_mono_type_internal (sig->params [pindex]); + if (klass == swift_self && sig->pinvoke) { + guint32 align; + MonoType *ptype = mini_get_underlying_type (sig->params [pindex]); + int size = mini_type_stack_size_full (ptype, &align, cinfo->pinvoke); + g_assert (size == 8); + + ainfo->storage = ArgVtypeInIRegs; + ainfo->reg = ARMREG_R20; + ainfo->nregs = 1; + ainfo->size = size; + continue; + } else if (klass == swift_error || klass == swift_error_ptr) { + if (sig->pinvoke) + ainfo->reg = ARMREG_R21; + else + add_param (cinfo, ainfo, sig->params [pindex], FALSE); + ainfo->storage = ArgSwiftError; + continue; + } + } +#endif + add_param (cinfo, ainfo, sig->params [pindex], FALSE); if (ainfo->storage == ArgVtypeByRef) { /* Pass the argument address in the next register */ @@ -1902,7 +1930,10 @@ arg_get_storage (CallContext *ccontext, ArgInfo *ainfo) switch (ainfo->storage) { case ArgVtypeInIRegs: case ArgInIReg: - return &ccontext->gregs [ainfo->reg]; + if (ainfo->reg == ARMREG_R20) + return &ccontext->gregs [PARAM_REGS + 1]; + else + return &ccontext->gregs [ainfo->reg]; case ArgInFReg: case ArgInFRegR4: case ArgHFA: @@ -1916,6 +1947,8 @@ arg_get_storage (CallContext *ccontext, ArgInfo *ainfo) return *(gpointer*)(ccontext->stack + ainfo->offset); case ArgVtypeByRef: return (gpointer) ccontext->gregs [ainfo->reg]; + case ArgSwiftError: + return &ccontext->gregs [PARAM_REGS + 2]; default: g_error ("Arg storage type not yet supported"); } @@ -2011,6 +2044,11 @@ mono_arch_set_native_call_context_args (CallContext *ccontext, gpointer frame, M else storage = arg_get_storage (ccontext, ainfo); + if (ainfo->storage == ArgSwiftError) { + *(gpointer*)storage = 0; + continue; + } + interp_cb->frame_arg_to_data ((MonoInterpFrameHandle)frame, sig, i, storage); if (temp_size) arg_set_val (ccontext, ainfo, storage); @@ -2109,6 +2147,30 @@ mono_arch_get_native_call_context_ret (CallContext *ccontext, gpointer frame, Mo } } +#ifdef MONO_ARCH_HAVE_SWIFTCALL +/** + * Gets error context from `ccontext` registers by indirectly storing the value onto the stack. + * + * The function searches for an argument with SwiftError type + * If found, it retrieves the value from `ccontext`. + */ +gpointer +mono_arch_get_swift_error (CallContext *ccontext, MonoMethodSignature *sig, int *arg_index) +{ + MonoClass *swift_error = mono_class_try_get_swift_error_class (); + MonoClass *swift_error_ptr = mono_class_create_ptr (m_class_get_this_arg (swift_error)); + for (guint i = 0; i < sig->param_count + sig->hasthis; i++) { + MonoClass *klass = mono_class_from_mono_type_internal (sig->params [i]); + if (klass && (klass == swift_error || klass == swift_error_ptr)) { + *arg_index = i; + return &ccontext->gregs [PARAM_REGS + 2]; + } + } + + return NULL; +} +#endif + typedef struct { MonoMethodSignature *sig; CallInfo *cinfo; @@ -2560,8 +2622,10 @@ mono_arch_get_global_int_regs (MonoCompile *cfg) /* r28 is reserved for cfg->arch.args_reg */ /* r27 is reserved for the imt argument */ - for (i = ARMREG_R19; i <= ARMREG_R26; ++i) - regs = g_list_prepend (regs, GUINT_TO_POINTER (i)); + for (i = ARMREG_R19; i <= ARMREG_R26; ++i) { + if (!(mono_method_signature_has_ext_callconv (cfg->method->signature, MONO_EXT_CALLCONV_SWIFTCALL) && i == ARMREG_R21)) + regs = g_list_prepend (regs, GUINT_TO_POINTER (i)); + } return regs; } @@ -2811,6 +2875,21 @@ mono_arch_allocate_vars (MonoCompile *cfg) ins->inst_left = vtaddr; break; } + case ArgSwiftError: { + ins->flags |= MONO_INST_VOLATILE; + size = 8; + align = 8; + offset += align - 1; + offset &= ~(align - 1); + ins->opcode = OP_REGOFFSET; + ins->inst_basereg = cfg->frame_reg; + ins->inst_offset = offset; + offset += size; + + cfg->arch.swift_error_var = ins; + cfg->used_int_regs |= 1 << ARMREG_R21; + break; + } default: g_assert_not_reached (); break; @@ -3160,6 +3239,10 @@ mono_arch_emit_call (MonoCompile *cfg, MonoCallInst *call) MONO_ADD_INS (cfg->cbb, ins); break; } + case ArgSwiftError: { + MONO_EMIT_NEW_I8CONST (cfg, ainfo->reg, 0); + break; + } default: g_assert_not_reached (); break; @@ -3303,10 +3386,9 @@ mono_arch_emit_outarg_vt (MonoCompile *cfg, MonoInst *ins, MonoInst *src) void mono_arch_emit_setret (MonoCompile *cfg, MonoMethod *method, MonoInst *val) { - MonoMethodSignature *sig; + MonoMethodSignature *sig = mono_method_signature_internal (cfg->method); CallInfo *cinfo; - sig = mono_method_signature_internal (cfg->method); if (!cfg->arch.cinfo) cfg->arch.cinfo = get_call_info (cfg->mempool, sig); cinfo = cfg->arch.cinfo; @@ -3648,6 +3730,11 @@ emit_move_return_value (MonoCompile *cfg, guint8 * code, MonoInst *ins) CallInfo *cinfo; MonoCallInst *call; + if (cfg->arch.swift_error_var) { + code = emit_ldrx (code, ARMREG_IP0, cfg->arch.swift_error_var->inst_basereg, GTMREG_TO_INT (cfg->arch.swift_error_var->inst_offset)); + code = emit_strx (code, ARMREG_R21, ARMREG_IP0, 0); + } + call = (MonoCallInst*)ins; cinfo = call->call_info; g_assert (cinfo); @@ -5849,6 +5936,14 @@ emit_move_args (MonoCompile *cfg, guint8 *code) case ArgInSIMDReg: code = emit_strfpq (code, ainfo->reg, ins->inst_basereg, GTMREG_TO_INT (ins->inst_offset)); break; + case ArgSwiftError: + if (ainfo->offset) { + code = emit_ldrx (code, ARMREG_IP0, cfg->arch.args_reg, ainfo->offset); + code = emit_strx (code, ARMREG_IP0, cfg->arch.swift_error_var->inst_basereg, GTMREG_TO_INT (cfg->arch.swift_error_var->inst_offset)); + } else { + code = emit_strx (code, ainfo->reg, cfg->arch.swift_error_var->inst_basereg, GTMREG_TO_INT (cfg->arch.swift_error_var->inst_offset)); + } + break; default: g_assert_not_reached (); break; diff --git a/src/mono/mono/mini/mini-arm64.h b/src/mono/mono/mini/mini-arm64.h index 3c7f0b8a6eaa5..99fbc7d2235e5 100644 --- a/src/mono/mono/mini/mini-arm64.h +++ b/src/mono/mono/mini/mini-arm64.h @@ -98,6 +98,8 @@ struct SeqPointInfo { #define PARAM_REGS 8 #define FP_PARAM_REGS 8 +#define CTX_REGS 2 +#define CTX_REGS_OFFSET ARMREG_R20 typedef struct { host_mgreg_t res, res2; @@ -119,6 +121,7 @@ typedef struct { MonoInst *seq_point_info_var; MonoInst *ss_tramp_var; MonoInst *bp_tramp_var; + MonoInst *swift_error_var; guint8 *thunks; int thunks_size; } MonoCompileArch; @@ -205,6 +208,10 @@ typedef struct { #define MONO_ARCH_EXPLICIT_NULL_CHECKS 1 #endif +#if defined(TARGET_OSX) || defined(TARGET_APPLE_MOBILE) +#define MONO_ARCH_HAVE_SWIFTCALL 1 +#endif + /* Relocations */ #define MONO_R_ARM64_B 1 #define MONO_R_ARM64_BCC 2 @@ -234,6 +241,7 @@ typedef enum { ArgVtypeByRefOnStack, ArgVtypeOnStack, ArgHFA, + ArgSwiftError, ArgNone } ArgStorage; @@ -268,8 +276,8 @@ struct CallInfo { }; typedef struct { - /* General registers + ARMREG_R8 for indirect returns */ - host_mgreg_t gregs [PARAM_REGS + 1]; + /* General registers + ARMREG_R8 for indirect returns + context registers */ + host_mgreg_t gregs [PARAM_REGS + CTX_REGS + 1]; /* Floating registers */ double fregs [FP_PARAM_REGS]; /* Stack usage, used for passing params on stack */ diff --git a/src/mono/mono/mini/mini-codegen.c b/src/mono/mono/mini/mini-codegen.c index efcf9c2564ac6..21b03a98ee7e0 100644 --- a/src/mono/mono/mini/mini-codegen.c +++ b/src/mono/mono/mini/mini-codegen.c @@ -1095,6 +1095,9 @@ assign_reg (MonoCompile *cfg, MonoRegState *rs, int reg, int hreg, int bank) #if !defined(TARGET_ARM) && !defined(TARGET_ARM64) /* this seems to trigger a gcc compilation bug sometime (hreg is 0) */ /* On arm64, rgctx_reg is a global hreg, and it is used to pass an argument */ +#ifdef MONO_ARCH_HAVE_SWIFTCALL + if (!(mono_method_signature_has_ext_callconv (cfg->method->signature, MONO_EXT_CALLCONV_SWIFTCALL) && (hreg == AMD64_R12 || hreg == AMD64_R13))) +#endif g_assert (! is_global_ireg (hreg)); #endif diff --git a/src/mono/mono/mini/mini.h b/src/mono/mono/mini/mini.h index 0e77e536b68f9..bc9a0bd371217 100644 --- a/src/mono/mono/mini/mini.h +++ b/src/mono/mono/mini/mini.h @@ -2554,6 +2554,10 @@ void mono_arch_set_native_call_context_ret (CallContext *ccontext, gpointer gpointer mono_arch_get_native_call_context_args (CallContext *ccontext, gpointer frame, MonoMethodSignature *sig, gpointer call_info); // After the pinvoke call is done, this moves return value from the ccontext to the InterpFrame. void mono_arch_get_native_call_context_ret (CallContext *ccontext, gpointer frame, MonoMethodSignature *sig, gpointer call_info); +#ifdef MONO_ARCH_HAVE_SWIFTCALL +// After the pinvoke call is done, this return an error context value from the ccontext. +gpointer mono_arch_get_swift_error (CallContext *ccontext, MonoMethodSignature *sig, int *arg_index); +#endif /* Free the structure returned by mono_arch_get_interp_native_call_info (NULL, sig) */ void mono_arch_free_interp_native_call_info (gpointer call_info); #endif diff --git a/src/mono/mono/mini/tramp-amd64.c b/src/mono/mono/mini/tramp-amd64.c index 086edc2941ea5..4f0e48c61918a 100644 --- a/src/mono/mono/mini/tramp-amd64.c +++ b/src/mono/mono/mini/tramp-amd64.c @@ -1085,7 +1085,7 @@ mono_arch_get_interp_to_native_trampoline (MonoTrampInfo **info) guint8 *label_start_copy, *label_exit_copy; MonoJumpInfo *ji = NULL; GSList *unwind_ops = NULL; - int buf_len, i, cfa_offset, off_methodargs, off_targetaddr; + int buf_len, i, cfa_offset, framesize = 8, off_methodargs, off_targetaddr; buf_len = 512; start = code = (guint8 *) mono_global_codeman_reserve (buf_len + MONO_MAX_TRAMPOLINE_UNWINDINFO_SIZE); @@ -1105,15 +1105,24 @@ mono_arch_get_interp_to_native_trampoline (MonoTrampInfo **info) mono_add_unwind_op_def_cfa_reg (unwind_ops, code, start, AMD64_RBP); mono_add_unwind_op_fp_alloc (unwind_ops, code, start, AMD64_RBP, 0); - /* allocate space for saving the target addr and the call context */ - amd64_alu_reg_imm (code, X86_SUB, AMD64_RSP, 2 * sizeof (target_mgreg_t)); + /* allocate space for saving the target addr, call context, and context registers */ +#ifdef MONO_ARCH_HAVE_SWIFTCALL + int off_ctxregs = -framesize; + framesize += CTX_REGS * sizeof (host_mgreg_t); +#endif + + off_methodargs = -framesize; + framesize += sizeof (host_mgreg_t); + + off_targetaddr = -framesize; + framesize += sizeof (host_mgreg_t); + + amd64_alu_reg_imm (code, X86_SUB, AMD64_RSP, (framesize - 8) * sizeof (target_mgreg_t)); /* save CallContext* onto stack */ - off_methodargs = - 8; amd64_mov_membase_reg (code, AMD64_RBP, off_methodargs, AMD64_ARG_REG2, sizeof (target_mgreg_t)); /* save target address on stack */ - off_targetaddr = - 2 * 8; amd64_mov_membase_reg (code, AMD64_RBP, off_targetaddr, AMD64_ARG_REG1, sizeof (target_mgreg_t)); /* load pointer to CallContext* into R11 */ @@ -1147,6 +1156,14 @@ mono_arch_get_interp_to_native_trampoline (MonoTrampInfo **info) for (i = 0; i < FLOAT_PARAM_REGS; ++i) amd64_sse_movsd_reg_membase (code, i, AMD64_R11, MONO_STRUCT_OFFSET (CallContext, fregs) + i * sizeof (double)); +#ifdef MONO_ARCH_HAVE_SWIFTCALL + /* store the values of context registers onto the stack and set the context registers from CallContext */ + for (i = 0; i < CTX_REGS; i++) { + amd64_mov_membase_reg (code, AMD64_RBP, off_ctxregs - i * sizeof (target_mgreg_t), i + CTX_REGS_OFFSET, sizeof (target_mgreg_t)); + amd64_mov_reg_membase (code, i + CTX_REGS_OFFSET, AMD64_R11, MONO_STRUCT_OFFSET (CallContext, gregs) + (i + CTX_REGS_OFFSET) * sizeof (target_mgreg_t), sizeof (target_mgreg_t)); + } +#endif + /* load target addr */ amd64_mov_reg_membase (code, AMD64_R11, AMD64_RBP, off_targetaddr, sizeof (target_mgreg_t)); @@ -1162,6 +1179,14 @@ mono_arch_get_interp_to_native_trampoline (MonoTrampInfo **info) for (i = 0; i < FLOAT_RETURN_REGS; i++) amd64_sse_movsd_membase_reg (code, AMD64_R11, MONO_STRUCT_OFFSET (CallContext, fregs) + i * sizeof (double), i); +#ifdef MONO_ARCH_HAVE_SWIFTCALL + /* set context registers to CallContext and load context registers from the stack */ + for (i = 0; i < CTX_REGS; i++) { + amd64_mov_membase_reg (code, AMD64_R11, MONO_STRUCT_OFFSET (CallContext, gregs) + (i + CTX_REGS_OFFSET) * sizeof (target_mgreg_t), i + CTX_REGS_OFFSET, sizeof (target_mgreg_t)); + amd64_mov_reg_membase (code, i + CTX_REGS_OFFSET, AMD64_RBP, off_ctxregs - i * sizeof (target_mgreg_t), sizeof (target_mgreg_t)); + } +#endif + #if TARGET_WIN32 amd64_lea_membase (code, AMD64_RSP, AMD64_RBP, 0); #else diff --git a/src/mono/mono/mini/tramp-arm64.c b/src/mono/mono/mini/tramp-arm64.c index dd55c6cd25457..7e8483409610c 100644 --- a/src/mono/mono/mini/tramp-arm64.c +++ b/src/mono/mono/mini/tramp-arm64.c @@ -694,6 +694,11 @@ mono_arch_get_interp_to_native_trampoline (MonoTrampInfo **info) /* allocate frame */ framesize += 2 * sizeof (host_mgreg_t); +#ifdef MONO_ARCH_HAVE_SWIFTCALL + int off_ctxregs = framesize; + framesize += CTX_REGS * sizeof (host_mgreg_t); +#endif + off_methodargs = framesize; framesize += sizeof (host_mgreg_t); @@ -748,6 +753,14 @@ mono_arch_get_interp_to_native_trampoline (MonoTrampInfo **info) for (i = 0; i < FP_PARAM_REGS; i++) arm_ldrfpx (code, i, ARMREG_IP0, MONO_STRUCT_OFFSET (CallContext, fregs) + i * sizeof (double)); +#ifdef MONO_ARCH_HAVE_SWIFTCALL + /* store the values of context registers onto the stack and set the context registers from CallContext */ + for (i = 0; i < CTX_REGS; i++) { + arm_strx (code, i + CTX_REGS_OFFSET, ARMREG_FP, off_ctxregs + i * sizeof (host_mgreg_t)); + arm_ldrx (code, i + CTX_REGS_OFFSET, ARMREG_IP0, MONO_STRUCT_OFFSET (CallContext, gregs) + (i + PARAM_REGS + 1) * sizeof (host_mgreg_t)); + } +#endif + /* load target addr */ arm_ldrx (code, ARMREG_IP0, ARMREG_FP, off_targetaddr); @@ -765,6 +778,14 @@ mono_arch_get_interp_to_native_trampoline (MonoTrampInfo **info) for (i = 0; i < FP_PARAM_REGS; i++) arm_strfpx (code, i, ARMREG_IP0, MONO_STRUCT_OFFSET (CallContext, fregs) + i * sizeof (double)); +#ifdef MONO_ARCH_HAVE_SWIFTCALL + /* set context registers to CallContext and load context registers from the stack */ + for (i = 0; i < CTX_REGS; i++) { + arm_strx (code, i + CTX_REGS_OFFSET, ARMREG_IP0, MONO_STRUCT_OFFSET (CallContext, gregs) + (i + PARAM_REGS + 1) * sizeof (host_mgreg_t)); + arm_ldrx (code, i + CTX_REGS_OFFSET, ARMREG_FP, off_ctxregs + i * sizeof (host_mgreg_t)); + } +#endif + arm_movspx (code, ARMREG_SP, ARMREG_FP); arm_ldpx (code, ARMREG_FP, ARMREG_LR, ARMREG_SP, 0); arm_addx_imm (code, ARMREG_SP, ARMREG_SP, framesize); diff --git a/src/tests/Interop/CMakeLists.txt b/src/tests/Interop/CMakeLists.txt index f77b8b469d773..80958de80bea5 100644 --- a/src/tests/Interop/CMakeLists.txt +++ b/src/tests/Interop/CMakeLists.txt @@ -102,4 +102,7 @@ endif(CLR_CMAKE_TARGET_UNIX) if(CLR_CMAKE_TARGET_APPLE) add_subdirectory(ObjectiveC/AutoReleaseTest) add_subdirectory(ObjectiveC/ObjectiveCMarshalAPI) + add_subdirectory(Swift/SwiftErrorHandling) + add_subdirectory(Swift/SwiftSelfContext) + add_subdirectory(Swift/SwiftInvalidCallConv) endif() diff --git a/src/tests/Interop/Swift/SwiftErrorHandling/CMakeLists.txt b/src/tests/Interop/Swift/SwiftErrorHandling/CMakeLists.txt new file mode 100644 index 0000000000000..2d07bd3bf745c --- /dev/null +++ b/src/tests/Interop/Swift/SwiftErrorHandling/CMakeLists.txt @@ -0,0 +1,21 @@ +project(SwiftErrorHandling) +include ("${CLR_INTEROP_TEST_ROOT}/Interop.cmake") + +set(SOURCE SwiftErrorHandling) + +if (NOT SWIFT_COMPILER_TARGET AND CLR_CMAKE_TARGET_OSX) + set(SWIFT_PLATFORM "macosx") + set(SWIFT_PLATFORM_SUFFIX "") + set(SWIFT_DEPLOYMENT_TARGET ${CMAKE_OSX_DEPLOYMENT_TARGET}) + set(SWIFT_COMPILER_TARGET "${CMAKE_OSX_ARCHITECTURES}-apple-${SWIFT_PLATFORM}${SWIFT_DEPLOYMENT_TARGET}${SWIFT_PLATFORM_SUFFIX}") +endif() + +add_custom_target(${SOURCE} ALL + COMMAND xcrun swiftc -target ${SWIFT_COMPILER_TARGET} -emit-library ${CMAKE_CURRENT_SOURCE_DIR}/${SOURCE}.swift -o ${CMAKE_CURRENT_BINARY_DIR}/lib${SOURCE}.dylib + DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/${SOURCE}.swift + COMMENT "Generating ${SOURCE} library" +) + +install(FILES ${CMAKE_CURRENT_BINARY_DIR}/lib${SOURCE}.dylib + DESTINATION bin +) diff --git a/src/tests/Interop/Swift/SwiftErrorHandling/SwiftErrorHandling.cs b/src/tests/Interop/Swift/SwiftErrorHandling/SwiftErrorHandling.cs new file mode 100644 index 0000000000000..67a398d357e11 --- /dev/null +++ b/src/tests/Interop/Swift/SwiftErrorHandling/SwiftErrorHandling.cs @@ -0,0 +1,105 @@ +// 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.CompilerServices; +using System.Runtime.InteropServices; +using System.Runtime.InteropServices.Swift; +using System.Text; +using Xunit; + +public class ErrorHandlingTests +{ + private const string SwiftLib = "libSwiftErrorHandling.dylib"; + + [DllImport(SwiftLib, EntryPoint = "$s18SwiftErrorHandling05setMyB7Message7message6lengthySPys6UInt16VG_s5Int32VtF", CharSet = CharSet.Unicode)] + public static extern void SetErrorMessage(string message, int length); + + [UnmanagedCallConv(CallConvs = new Type[] { typeof(CallConvSwift) })] + [DllImport(SwiftLib, EntryPoint = "$s18SwiftErrorHandling018conditionallyThrowB004willE0s5Int32VAE_tKF")] + public static extern nint conditionallyThrowError(int willThrow, ref SwiftError error); + + [UnmanagedCallConv(CallConvs = new Type[] { typeof(CallConvSwift) })] + [DllImport(SwiftLib, EntryPoint = "$s18SwiftErrorHandling018conditionallyThrowB004willE0s5Int32VAE_tKF")] + public static extern nint conditionallyThrowErrorOnStack(int willThrow, int dummy1, int dummy2, int dummy3, int dummy4, int dummy5, int dummy6, int dummy7, int dummy8, int dummy9, ref SwiftError error); + + [DllImport(SwiftLib, EntryPoint = "$s18SwiftErrorHandling05getMyB7Message4from13messageLengthSPys6UInt16VGSgs0B0_p_s5Int32VztF")] + public unsafe static extern void* GetErrorMessage(void* handle, out int length); + + [Fact] + public unsafe static void TestSwiftErrorThrown() + { + const string expectedErrorMessage = "Catch me if you can!"; + SetErrorMessageForSwift(expectedErrorMessage); + + SwiftError error = new SwiftError(); + + // This will throw an error + conditionallyThrowError(1, ref error); + Assert.True(error.Value != null, "A Swift error was expected to be thrown."); + + string errorMessage = GetErrorMessageFromSwift(error); + Assert.True(errorMessage == expectedErrorMessage, string.Format("The error message retrieved from Swift does not match the expected message. Expected: {0}, Actual: {1}", expectedErrorMessage, errorMessage)); + } + + [Fact] + public unsafe static void TestSwiftErrorNotThrown() + { + const string expectedErrorMessage = "Catch me if you can!"; + SetErrorMessageForSwift(expectedErrorMessage); + + SwiftError error = new SwiftError(); + + // This will not throw an error + int result = (int)conditionallyThrowError(0, ref error); + + Assert.True(error.Value == null, "No Swift error was expected to be thrown."); + Assert.True(result == 42, "The result from Swift does not match the expected value."); + } + + [Fact] + public unsafe static void TestSwiftErrorOnStackThrown() + { + const string expectedErrorMessage = "Catch me if you can!"; + SetErrorMessageForSwift(expectedErrorMessage); + + SwiftError error = new SwiftError(); + + int i = 0; + // This will throw an error + conditionallyThrowErrorOnStack(1, i + 1, i + 2, i + 3, i + 4, i + 5, i + 6, i + 7, i + 8, i + 9, ref error); + Assert.True(error.Value != null, "A Swift error was expected to be thrown."); + + string errorMessage = GetErrorMessageFromSwift(error); + Assert.True(errorMessage == expectedErrorMessage, string.Format("The error message retrieved from Swift does not match the expected message. Expected: {0}, Actual: {1}", expectedErrorMessage, errorMessage)); + } + + [Fact] + public unsafe static void TestSwiftErrorOnStackNotThrown() + { + const string expectedErrorMessage = "Catch me if you can!"; + SetErrorMessageForSwift(expectedErrorMessage); + + SwiftError error = new SwiftError(); + + int i = 0; + // This will not throw an error + int result = (int)conditionallyThrowErrorOnStack(0, i + 1, i + 2, i + 3, i + 4, i + 5, i + 6, i + 7, i + 8, i + 9, ref error); + + Assert.True(error.Value == null, "No Swift error was expected to be thrown."); + Assert.True(result == 42, "The result from Swift does not match the expected value."); + } + + private static void SetErrorMessageForSwift(string message) + { + SetErrorMessage(message, message.Length); + } + + private unsafe static string GetErrorMessageFromSwift(SwiftError error) + { + void* pointer = GetErrorMessage(error.Value, out int messageLength); + string errorMessage = Marshal.PtrToStringUni((IntPtr)pointer, messageLength); + NativeMemory.Free((void*)pointer); + return errorMessage; + } +} diff --git a/src/tests/Interop/Swift/SwiftErrorHandling/SwiftErrorHandling.csproj b/src/tests/Interop/Swift/SwiftErrorHandling/SwiftErrorHandling.csproj new file mode 100644 index 0000000000000..a57cd84cf8842 --- /dev/null +++ b/src/tests/Interop/Swift/SwiftErrorHandling/SwiftErrorHandling.csproj @@ -0,0 +1,16 @@ + + + + true + true + + true + + + + + + + + + diff --git a/src/tests/Interop/Swift/SwiftErrorHandling/SwiftErrorHandling.swift b/src/tests/Interop/Swift/SwiftErrorHandling/SwiftErrorHandling.swift new file mode 100644 index 0000000000000..20022c0dba3e2 --- /dev/null +++ b/src/tests/Interop/Swift/SwiftErrorHandling/SwiftErrorHandling.swift @@ -0,0 +1,35 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +import Foundation + +public enum MyError: Error { + case runtimeError(message: NSString) +} + +var errorMessage: NSString = "" + +public func setMyErrorMessage(message: UnsafePointer, length: Int32) { + errorMessage = NSString(characters: message, length: Int(length)) +} + +public func conditionallyThrowError(willThrow: Int32) throws -> Int32 { + if willThrow != 0 { + throw MyError.runtimeError(message: errorMessage) + } else { + return 42 + } +} + +public func getMyErrorMessage(from error: Error, messageLength: inout Int32) -> UnsafePointer? { + if let myError = error as? MyError { + switch myError { + case .runtimeError(let message): + let buffer = UnsafeMutableBufferPointer.allocate(capacity: message.length) + message.getCharacters(buffer.baseAddress!, range: NSRange(location: 0, length: message.length)) + messageLength = Int32(message.length) + return UnsafePointer(buffer.baseAddress!) + } + } + return nil +} diff --git a/src/tests/Interop/Swift/SwiftInvalidCallConv/CMakeLists.txt b/src/tests/Interop/Swift/SwiftInvalidCallConv/CMakeLists.txt new file mode 100644 index 0000000000000..dea341f587c15 --- /dev/null +++ b/src/tests/Interop/Swift/SwiftInvalidCallConv/CMakeLists.txt @@ -0,0 +1,21 @@ +project(SwiftInvalidCallConv) +include ("${CLR_INTEROP_TEST_ROOT}/Interop.cmake") + +set(SOURCE SwiftInvalidCallConv) + +if (NOT SWIFT_COMPILER_TARGET AND CLR_CMAKE_TARGET_OSX) + set(SWIFT_PLATFORM "macosx") + set(SWIFT_PLATFORM_SUFFIX "") + set(SWIFT_DEPLOYMENT_TARGET ${CMAKE_OSX_DEPLOYMENT_TARGET}) + set(SWIFT_COMPILER_TARGET "${CMAKE_OSX_ARCHITECTURES}-apple-${SWIFT_PLATFORM}${SWIFT_DEPLOYMENT_TARGET}${SWIFT_PLATFORM_SUFFIX}") +endif() + +add_custom_target(${SOURCE} ALL + COMMAND xcrun swiftc -target ${SWIFT_COMPILER_TARGET} -emit-library ${CMAKE_CURRENT_SOURCE_DIR}/${SOURCE}.swift -o ${CMAKE_CURRENT_BINARY_DIR}/lib${SOURCE}.dylib + DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/${SOURCE}.swift + COMMENT "Generating ${SOURCE} library" +) + +install(FILES ${CMAKE_CURRENT_BINARY_DIR}/lib${SOURCE}.dylib + DESTINATION bin +) diff --git a/src/tests/Interop/Swift/SwiftInvalidCallConv/SwiftInvalidCallConv.cs b/src/tests/Interop/Swift/SwiftInvalidCallConv/SwiftInvalidCallConv.cs new file mode 100644 index 0000000000000..41c98e3791f91 --- /dev/null +++ b/src/tests/Interop/Swift/SwiftInvalidCallConv/SwiftInvalidCallConv.cs @@ -0,0 +1,80 @@ +// 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.CompilerServices; +using System.Runtime.InteropServices; +using System.Runtime.InteropServices.Swift; +using Xunit; + +public class InvalidCallingConvTests +{ + // Dummy class with a dummy attribute + public class StringClass + { + public string value { get; set; } + } + private const string SwiftLib = "libSwiftInvalidCallConv.dylib"; + + [UnmanagedCallConv(CallConvs = new Type[] { typeof(CallConvSwift) })] + [DllImport(SwiftLib, EntryPoint = "$s20SwiftInvalidCallConv10simpleFuncyyF")] + public static extern void FuncWithTwoSelfParameters(SwiftSelf self1, SwiftSelf self2); + + [UnmanagedCallConv(CallConvs = new Type[] { typeof(CallConvSwift) })] + [DllImport(SwiftLib, EntryPoint = "$s20SwiftInvalidCallConv10simpleFuncyyF")] + public static extern void FuncWithTwoErrorParameters(ref SwiftError error1, ref SwiftError error2); + + [UnmanagedCallConv(CallConvs = new Type[] { typeof(CallConvSwift) })] + [DllImport(SwiftLib, EntryPoint = "$s20SwiftInvalidCallConv10simpleFuncyyF")] + public static extern void FuncWithMixedParameters(SwiftSelf self1, SwiftSelf self2, ref SwiftError error1, ref SwiftError error2); + + [UnmanagedCallConv(CallConvs = new Type[] { typeof(CallConvSwift) })] + [DllImport(SwiftLib, EntryPoint = "$s20SwiftInvalidCallConv10simpleFuncyyF")] + public static extern void FuncWithSwiftErrorAsArg(SwiftError error1); + + [UnmanagedCallConv(CallConvs = new Type[] { typeof(CallConvSwift) })] + [DllImport(SwiftLib, EntryPoint = "$s20SwiftInvalidCallConv10simpleFuncyyF")] + public static extern void FuncWithNonPrimitiveArg(StringClass arg1); + + [Fact] + public static void TestFuncWithTwoSelfParameters() + { + // Invalid due to multiple SwiftSelf arguments. + SwiftSelf self = new SwiftSelf(); + Assert.Throws(() => FuncWithTwoSelfParameters(self, self)); + } + + [Fact] + public static void TestFuncWithTwoErrorParameters() + { + // Invalid due to multiple SwiftError arguments. + SwiftError error = new SwiftError(); + Assert.Throws(() => FuncWithTwoErrorParameters(ref error, ref error)); + } + + [Fact] + public static void TestFuncWithMixedParameters() + { + // Invalid due to multiple SwiftSelf/SwiftError arguments. + SwiftSelf self = new SwiftSelf(); + SwiftError error = new SwiftError(); + Assert.Throws(() => FuncWithMixedParameters(self, self, ref error, ref error)); + } + + [Fact] + public static void TestFuncWithSwiftErrorAsArg() + { + // Invalid due to SwiftError not passed as a pointer. + SwiftError error = new SwiftError(); + Assert.Throws(() => FuncWithSwiftErrorAsArg(error)); + } + + [Fact] + public static void TestFuncWithNonPrimitiveArg() + { + // Invalid due to a non-primitive argument. + StringClass arg1 = new StringClass(); + arg1.value = "fail"; + Assert.Throws(() => FuncWithNonPrimitiveArg(arg1)); + } +} diff --git a/src/tests/Interop/Swift/SwiftInvalidCallConv/SwiftInvalidCallConv.csproj b/src/tests/Interop/Swift/SwiftInvalidCallConv/SwiftInvalidCallConv.csproj new file mode 100644 index 0000000000000..a57cd84cf8842 --- /dev/null +++ b/src/tests/Interop/Swift/SwiftInvalidCallConv/SwiftInvalidCallConv.csproj @@ -0,0 +1,16 @@ + + + + true + true + + true + + + + + + + + + diff --git a/src/tests/Interop/Swift/SwiftInvalidCallConv/SwiftInvalidCallConv.swift b/src/tests/Interop/Swift/SwiftInvalidCallConv/SwiftInvalidCallConv.swift new file mode 100644 index 0000000000000..f75d1f069a659 --- /dev/null +++ b/src/tests/Interop/Swift/SwiftInvalidCallConv/SwiftInvalidCallConv.swift @@ -0,0 +1,4 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +public func simpleFunc() { } diff --git a/src/tests/Interop/Swift/SwiftSelfContext/CMakeLists.txt b/src/tests/Interop/Swift/SwiftSelfContext/CMakeLists.txt new file mode 100644 index 0000000000000..8d7efa1eea675 --- /dev/null +++ b/src/tests/Interop/Swift/SwiftSelfContext/CMakeLists.txt @@ -0,0 +1,21 @@ +project(SwiftSelfContext) +include ("${CLR_INTEROP_TEST_ROOT}/Interop.cmake") + +set(SOURCE SwiftSelfContext) + +if (NOT SWIFT_COMPILER_TARGET AND CLR_CMAKE_TARGET_OSX) + set(SWIFT_PLATFORM "macosx") + set(SWIFT_PLATFORM_SUFFIX "") + set(SWIFT_DEPLOYMENT_TARGET ${CMAKE_OSX_DEPLOYMENT_TARGET}) + set(SWIFT_COMPILER_TARGET "${CMAKE_OSX_ARCHITECTURES}-apple-${SWIFT_PLATFORM}${SWIFT_DEPLOYMENT_TARGET}${SWIFT_PLATFORM_SUFFIX}") +endif() + +add_custom_target(${SOURCE} ALL + COMMAND xcrun swiftc -target ${SWIFT_COMPILER_TARGET} -emit-library ${CMAKE_CURRENT_SOURCE_DIR}/${SOURCE}.swift -o ${CMAKE_CURRENT_BINARY_DIR}/lib${SOURCE}.dylib + DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/${SOURCE}.swift + COMMENT "Generating ${SOURCE} library" +) + +install(FILES ${CMAKE_CURRENT_BINARY_DIR}/lib${SOURCE}.dylib + DESTINATION bin +) diff --git a/src/tests/Interop/Swift/SwiftSelfContext/SwiftSelfContext.cs b/src/tests/Interop/Swift/SwiftSelfContext/SwiftSelfContext.cs new file mode 100644 index 0000000000000..bbd234639cc9e --- /dev/null +++ b/src/tests/Interop/Swift/SwiftSelfContext/SwiftSelfContext.cs @@ -0,0 +1,47 @@ +// 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.CompilerServices; +using System.Runtime.InteropServices; +using System.Runtime.InteropServices.Swift; +using Xunit; + +public class SelfContextTests +{ + private const string SwiftLib = "libSwiftSelfContext.dylib"; + + [DllImport(SwiftLib, EntryPoint = "$s16SwiftSelfContext0B7LibraryC11getInstanceSvyFZ")] + public unsafe static extern void* getInstance(); + + [UnmanagedCallConv(CallConvs = new Type[] { typeof(CallConvSwift) })] + [DllImport(SwiftLib, EntryPoint = "$s16SwiftSelfContext0B7LibraryC14getMagicNumberSiyF")] + public static extern nint getMagicNumber(SwiftSelf self); + + [UnmanagedCallConv(CallConvs = new Type[] { typeof(CallConvSwift) })] + [DllImport(SwiftLib, EntryPoint = "$s16SwiftSelfContext0B7LibraryC14getMagicNumberSiyF")] + public static extern nint getMagicNumberOnStack(int dummy0, int dummy1, int dummy2, int dummy3, int dummy4, int dummy5, int dummy6, int dummy7, int dummy8, int dummy9, SwiftSelf self); + + [Fact] + public unsafe static void TestSwiftSelfContext() + { + void* pointer = getInstance(); + SwiftSelf self = new SwiftSelf(pointer); + Assert.True(self.Value != null, "Failed to obtain an instance of SwiftSelf from the Swift library."); + + int result = (int)getMagicNumber(self); + Assert.True(result == 42, "The result from Swift does not match the expected value."); + } + + [Fact] + public unsafe static void TestSwiftSelfContextOnStack() + { + void* pointer = getInstance(); + SwiftSelf self = new SwiftSelf(pointer); + Assert.True(self.Value != null, "Failed to obtain an instance of SwiftSelf from the Swift library."); + + int i = 0; + int result = (int)getMagicNumberOnStack(i, i + 1, i + 2, i + 3, i + 4, i + 5, i + 6, i + 7, i + 8, i + 9, self); + Assert.True(result == 42, "The result from Swift does not match the expected value."); + } +} diff --git a/src/tests/Interop/Swift/SwiftSelfContext/SwiftSelfContext.csproj b/src/tests/Interop/Swift/SwiftSelfContext/SwiftSelfContext.csproj new file mode 100644 index 0000000000000..a57cd84cf8842 --- /dev/null +++ b/src/tests/Interop/Swift/SwiftSelfContext/SwiftSelfContext.csproj @@ -0,0 +1,16 @@ + + + + true + true + + true + + + + + + + + + diff --git a/src/tests/Interop/Swift/SwiftSelfContext/SwiftSelfContext.swift b/src/tests/Interop/Swift/SwiftSelfContext/SwiftSelfContext.swift new file mode 100644 index 0000000000000..047f3bd942801 --- /dev/null +++ b/src/tests/Interop/Swift/SwiftSelfContext/SwiftSelfContext.swift @@ -0,0 +1,21 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +public class SelfLibrary { + public var number: Int + public static let shared = SelfLibrary(number: 42) + + private init(number: Int) { + self.number = number + } + + public func getMagicNumber() -> Int { + return self.number + } + + public static func getInstance() -> UnsafeMutableRawPointer { + let unmanagedInstance = Unmanaged.passUnretained(shared) + let pointer = unmanagedInstance.toOpaque() + return pointer + } +} diff --git a/src/tests/issues.targets b/src/tests/issues.targets index f78799d8c7e27..c1468004a8ec6 100644 --- a/src/tests/issues.targets +++ b/src/tests/issues.targets @@ -75,6 +75,9 @@ https://github.com/dotnet/runtime/issues/88586 + + https://github.com/dotnet/runtime/issues/93631 + @@ -1952,6 +1955,9 @@ https://github.com/dotnet/runtime/issues/67047 + + Not supported + @@ -3194,6 +3200,9 @@ needs native libraries + + Not supported + @@ -3640,6 +3649,12 @@ + + + Not supported + + + https://github.com/dotnet/runtime/issues/54906