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