Skip to content

Commit

Permalink
[CoreCLR] Handle ref/out enum values as parameters. Fixes #15744. (#1…
Browse files Browse the repository at this point in the history
…5870)

CoreCLR complains that enum values aren't blittable when passed to
Marshal.StructureToPtr, so unwrap enum values first.

Fixes #15744.
  • Loading branch information
rolfbjarne authored Sep 7, 2022
1 parent 62bc027 commit c5615ae
Show file tree
Hide file tree
Showing 3 changed files with 87 additions and 11 deletions.
7 changes: 7 additions & 0 deletions src/ObjCRuntime/Runtime.CoreCLR.cs
Original file line number Diff line number Diff line change
Expand Up @@ -419,6 +419,13 @@ static void StructureToPtr (object obj, IntPtr ptr)
if (obj == null)
return;

var structType = obj.GetType ();
// Unwrap enums, Marshal.StructureToPtr complains they're not blittable (https://github.com/xamarin/xamarin-macios/issues/15744)
if (structType.IsEnum) {
structType = Enum.GetUnderlyingType (structType);
obj = Convert.ChangeType (obj, structType);
}

if (obj is bool b) {
// Only write a single byte for bools
Marshal.WriteByte (ptr, b ? (byte) 1 : (byte) 0);
Expand Down
15 changes: 15 additions & 0 deletions tests/monotouch-test/ObjCRuntime/Messaging.cs
Original file line number Diff line number Diff line change
Expand Up @@ -263,8 +263,23 @@ public struct objc_super {

[DllImport (LIBOBJC_DYLIB, EntryPoint = "objc_msgSend")]
public extern static void void_objc_msgSend_NSRange_out_NSRange_ref_NSRange (IntPtr receiver, IntPtr selector, _LongNSRange p1, out _LongNSRange p2, ref _LongNSRange p3);

[DllImport (LIBOBJC_DYLIB, EntryPoint = "objc_msgSend")]
public extern static void void_objc_msgSend_ref_byte_ref_sbyte_ref_short_ref_ushort_ref_int_ref_uint_ref_long_ref_ulong (IntPtr receiver, IntPtr selector, ref EnumB b, ref EnumSB sb, ref EnumS s, ref EnumUS us, ref EnumI i, ref EnumUI ui, ref EnumL l, ref EnumUL ul);

[DllImport (LIBOBJC_DYLIB, EntryPoint = "objc_msgSend")]
public extern static void void_objc_msgSend_out_byte_out_sbyte_out_short_out_ushort_out_int_out_uint_out_long_out_ulong (IntPtr receiver, IntPtr selector, out EnumB b, out EnumSB sb, out EnumS s, out EnumUS us, out EnumI i, out EnumUI ui, out EnumL l, out EnumUL ul);
}

public enum EnumB : byte { a, b = 10 };
public enum EnumSB : sbyte { a, b = 11 };
public enum EnumS : short { a, b = 12 };
public enum EnumUS : ushort { a, b = 13 };
public enum EnumI : int { a, b = 14 };
public enum EnumUI : uint { a, b = 15 };
public enum EnumL : long { a, b = 16 };
public enum EnumUL : ulong { a, b = 17 };

public struct _LongNSRange {
public long Location;
public long Length;
Expand Down
76 changes: 65 additions & 11 deletions tests/monotouch-test/ObjCRuntime/RegistrarTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1694,20 +1694,37 @@ public class ConformsToProtocolTestClass<T> : NSObject where T: NSObject {
}

[Register ("UnderlyingEnumValues")]
class UnderlyingEnumValues : NSObject
internal class UnderlyingEnumValues : NSObject
{
enum B : byte { a };
enum SB : sbyte { a };
enum S : short { a };
enum US : ushort { a };
enum I : int { a };
enum UI : uint { a };
enum L : long { a };
enum UL : ulong { a };

[Export ("Foo:a:b:c:d:e:f:g:h")]
void Foo (B b, SB sb, S s, US us, I i, UI ui, L l, UL ul)
void Foo (EnumB b, EnumSB sb, EnumS s, EnumUS us, EnumI i, EnumUI ui, EnumL l, EnumUL ul)
{
}

[Export ("ByRef:a:b:c:d:e:f:g:")]
void ByRef (ref EnumB b, ref EnumSB sb, ref EnumS s, ref EnumUS us, ref EnumI i, ref EnumUI ui, ref EnumL l, ref EnumUL ul)
{
b = EnumB.b;
sb = EnumSB.b;
s = EnumS.b;
us = EnumUS.b;
i = EnumI.b;
ui = EnumUI.b;
l = EnumL.b;
ul = EnumUL.b;
}

[Export ("Out:a:b:c:d:e:f:g:")]
void Out (out EnumB b, out EnumSB sb, out EnumS s, out EnumUS us, out EnumI i, out EnumUI ui, out EnumL l, out EnumUL ul)
{
b = EnumB.b;
sb = EnumSB.b;
s = EnumS.b;
us = EnumUS.b;
i = EnumI.b;
ui = EnumUI.b;
l = EnumL.b;
ul = EnumUL.b;
}
}

Expand Down Expand Up @@ -5456,6 +5473,43 @@ public static unsafe void Invoke (IntPtr block, nint value)
}
}
#endif // !__WATCHOS__ && !__TVOS__

[Test]
public void RefEnumValues ()
{
EnumB b = 0;
EnumSB sb = 0;
EnumS s = 0;
EnumUS us = 0;
EnumI i = 0;
EnumUI ui = 0;
EnumL l = 0;
EnumUL ul = 0;

using (var obj = new UnderlyingEnumValues ()) {
b = 0; sb = 0; s = 0; us = 0; i = 0; ui = 0; l = 0; ul = 0;
Messaging.void_objc_msgSend_ref_byte_ref_sbyte_ref_short_ref_ushort_ref_int_ref_uint_ref_long_ref_ulong (obj.Handle, Selector.GetHandle ("ByRef:a:b:c:d:e:f:g:"), ref b, ref sb, ref s, ref us, ref i, ref ui, ref l, ref ul);
Assert.AreEqual (EnumB.b, b, "ref: B");
Assert.AreEqual (EnumSB.b, sb, "ref: SB");
Assert.AreEqual (EnumS.b, s, "ref: S");
Assert.AreEqual (EnumUS.b, us, "ref: US");
Assert.AreEqual (EnumI.b, i, "ref: I");
Assert.AreEqual (EnumUI.b, ui, "ref: UI");
Assert.AreEqual (EnumL.b, l, "ref: L");
Assert.AreEqual (EnumUL.b, ul, "ref: UL");

b = 0; sb = 0; s = 0; us = 0; i = 0; ui = 0; l = 0; ul = 0;
Messaging.void_objc_msgSend_out_byte_out_sbyte_out_short_out_ushort_out_int_out_uint_out_long_out_ulong (obj.Handle, Selector.GetHandle ("Out:a:b:c:d:e:f:g:"), out b, out sb, out s, out us, out i, out ui, out l, out ul);
Assert.AreEqual (EnumB.b, b, "out: B");
Assert.AreEqual (EnumSB.b, sb, "out: SB");
Assert.AreEqual (EnumS.b, s, "out: S");
Assert.AreEqual (EnumUS.b, us, "out: US");
Assert.AreEqual (EnumI.b, i, "out: I");
Assert.AreEqual (EnumUI.b, ui, "out: UI");
Assert.AreEqual (EnumL.b, l, "out: L");
Assert.AreEqual (EnumUL.b, ul, "out: UL");
}
}
}

#if !__WATCHOS__
Expand Down

5 comments on commit c5615ae

@vs-mobiletools-engineering-service2

This comment was marked as outdated.

@vs-mobiletools-engineering-service2

This comment was marked as outdated.

@vs-mobiletools-engineering-service2

This comment was marked as outdated.

@vs-mobiletools-engineering-service2

This comment was marked as outdated.

@vs-mobiletools-engineering-service2

This comment was marked as outdated.

Please sign in to comment.