From 444f51c068e32c9e58a3338d56de7dc3baeb0dce Mon Sep 17 00:00:00 2001 From: Alex Soto Date: Fri, 16 Jun 2017 09:08:56 -0500 Subject: [PATCH 01/46] [generator] Persist BindAs attribute information in generated code. Persisting BindAs attributes is required for the registrars to be able to handle methods with BindAs attributes correctly. Generator diff: https://gist.github.com/rolfbjarne/0fafbce9373ff1f407839a20ea5e72e9 --- src/Makefile.generator | 1 + src/ObjCRuntime/BindAsAttribute.cs | 60 ++++++++++++++++++++++ src/frameworks.sources | 1 + src/generator-attributes.cs | 53 ------------------- src/generator.cs | 38 ++++++++++++-- tests/generator/bindas1048error.cs | 1 + tests/generator/bindas1049error.cs | 1 + tests/generator/bindas1050modelerror.cs | 1 + tests/generator/bindas1050protocolerror.cs | 1 + tests/generator/bindastests.cs | 22 ++++---- 10 files changed, 112 insertions(+), 67 deletions(-) create mode 100644 src/ObjCRuntime/BindAsAttribute.cs diff --git a/src/Makefile.generator b/src/Makefile.generator index 6ae3f48d1203..41ffb3f93591 100644 --- a/src/Makefile.generator +++ b/src/Makefile.generator @@ -41,6 +41,7 @@ GENERATOR_ATTRIBUTE_SOURCES = \ $(TOP)/src/Foundation/RegisterAttribute.cs \ $(TOP)/src/ObjCRuntime/AlphaAttribute.cs \ $(TOP)/src/ObjCRuntime/ArgumentSemantic.cs \ + $(TOP)/src/ObjCRuntime/BindAsAttribute.cs \ $(TOP)/src/ObjCRuntime/LinkWithAttribute.cs \ $(TOP)/src/ObjCRuntime/NativeAttribute.cs \ $(TOP)/src/ObjCRuntime/PlatformAvailability2.cs \ diff --git a/src/ObjCRuntime/BindAsAttribute.cs b/src/ObjCRuntime/BindAsAttribute.cs new file mode 100644 index 000000000000..24efd1fda63a --- /dev/null +++ b/src/ObjCRuntime/BindAsAttribute.cs @@ -0,0 +1,60 @@ +using System; +#if IKVM +using IKVM.Reflection; +using Type = IKVM.Reflection.Type; +#endif +namespace XamCore.ObjCRuntime { + // + // BindAsAttribute + // + // The BindAsAttribute allows binding NSNumber, NSValue and NSString(enums) into more accurate C# types. + // It can be used in methods, parameters and properties. The only restriction is that your member must not + // be inside a [Protocol] or [Model] interface. + // + // For example: + // + // [return: BindAs (typeof (bool?))] + // [Export ("shouldDrawAt:")] + // NSNumber ShouldDraw ([BindAs (typeof (CGRect))] NSValue rect); + // + // Would output: + // + // [Export ("shouldDrawAt:")] + // bool? ShouldDraw (CGRect rect) { ... } + // + // Internally we will do the bool? <-> NSNumber and CGRect <-> NSValue conversions. + // + // BindAs also supports arrays of NSNumber|NSValue|NSString(enums) + // + // For example: + // + // [BindAs (typeof (CAScroll []))] + // [Export ("supportedScrollModes")] + // NSString [] SupportedScrollModes { get; set; } + // + // Would output: + // + // [Export ("supportedScrollModes")] + // CAScroll [] SupportedScrollModes { get; set; } + // + // CAScroll is a NSString backed enum, we will fetch the right NSString value and handle the type conversion. + // + [AttributeUsage (AttributeTargets.ReturnValue | AttributeTargets.Property | AttributeTargets.Parameter, AllowMultiple = false)] + public class BindAsAttribute : Attribute { + public BindAsAttribute (Type type) + { + Type = type; +#if BGENERATOR + var nullable = type.IsArray ? TypeManager.GetUnderlyingNullableType (type.GetElementType ()) : TypeManager.GetUnderlyingNullableType (type); + IsNullable = nullable != null; + IsValueType = IsNullable ? nullable.IsValueType : type.IsValueType; +#endif + } + public Type Type; + public Type OriginalType; +#if BGENERATOR + internal readonly bool IsNullable; + internal readonly bool IsValueType; +#endif + } +} diff --git a/src/frameworks.sources b/src/frameworks.sources index 197eb22ae657..67d0241783f7 100644 --- a/src/frameworks.sources +++ b/src/frameworks.sources @@ -1519,6 +1519,7 @@ SHARED_CORE_SOURCES = \ build/common/NativeTypes/Primitives.cs \ ObjCRuntime/AlphaAttribute.cs \ ObjCRuntime/ArgumentSemantic.cs \ + ObjCRuntime/BindAsAttribute.cs \ ObjCRuntime/Blocks.cs \ ObjCRuntime/Class.cs \ ObjCRuntime/INativeObject.cs \ diff --git a/src/generator-attributes.cs b/src/generator-attributes.cs index 9da23127df69..d590774d8529 100644 --- a/src/generator-attributes.cs +++ b/src/generator-attributes.cs @@ -54,59 +54,6 @@ public ForcedTypeAttribute (bool owns = false) public bool Owns; } -// -// BindAsAttribute -// -// The BindAsAttribute allows binding NSNumber, NSValue and NSString(enums) into more accurate C# types. -// It can be used in methods, parameters and properties. The only restriction is that your member must not -// be inside a [Protocol] or [Model] interface. -// -// For example: -// -// [return: BindAs (typeof (bool?))] -// [Export ("shouldDrawAt:")] -// NSNumber ShouldDraw ([BindAs (typeof (CGRect))] NSValue rect); -// -// Would output: -// -// [Export ("shouldDrawAt:")] -// bool? ShouldDraw (CGRect rect) { ... } -// -// Internally we will do the bool? <-> NSNumber and CGRect <-> NSValue conversions. -// -// BindAs also supports arrays of NSNumber|NSValue|NSString(enums) -// -// For example: -// -// [BindAs (typeof (CAScroll []))] -// [Export ("supportedScrollModes")] -// NSString [] SupportedScrollModes { get; set; } -// -// Would output: -// -// [Export ("supportedScrollModes")] -// CAScroll [] SupportedScrollModes { get; set; } -// -// CAScroll is a NSString backed enum, we will fetch the right NSString value and handle the type conversion. -// -[AttributeUsage (AttributeTargets.ReturnValue | AttributeTargets.Property | AttributeTargets.Parameter, AllowMultiple = false)] -public class BindAsAttribute : Attribute { - public BindAsAttribute (Type type) - { - Type = type; -#if BGENERATOR - var nullable = type.IsArray ? TypeManager.GetUnderlyingNullableType (type.GetElementType ()) : TypeManager.GetUnderlyingNullableType (type); - IsNullable = nullable != null; - IsValueType = IsNullable ? nullable.IsValueType : type.IsValueType; -#endif - } - public Type Type; -#if BGENERATOR - internal readonly bool IsNullable; - internal readonly bool IsValueType; -#endif -} - // Used to flag a type as needing to be turned into a protocol on output for Unified // For example: // [Protocolize, Wrap ("WeakDelegate")] diff --git a/src/generator.cs b/src/generator.cs index a1c982d9e135..df8cfe25fb70 100644 --- a/src/generator.cs +++ b/src/generator.cs @@ -3269,7 +3269,7 @@ public string MakeSignature (MemberInformation minfo, bool is_async, ParameterIn string name = minfo.is_ctor ? GetGeneratedTypeName (mi.DeclaringType) : is_async ? GetAsyncName (mi) : mi.Name; // Some codepaths already write preservation info - PrintAttributes (minfo.mi, preserve:!alreadyPreserved, advice:true); + PrintAttributes (minfo.mi, preserve:!alreadyPreserved, advice:true, bindAs:true); if (!minfo.is_ctor && !is_async){ var prefix = ""; @@ -3375,8 +3375,10 @@ public void MakeSignatureFromParameterInfo (bool comma, StringBuilder sb, Member } var bindAsAtt = GetBindAsAttribute (pi); - if (bindAsAtt != null) + if (bindAsAtt != null) { + PrintBindAsAttribute (pi, sb); sb.Append (FormatType (bindAsAtt.Type.DeclaringType, bindAsAtt.Type, protocolized)); + } else sb.Append (FormatType (declaringType, parType, protocolized)); @@ -4512,7 +4514,7 @@ void GenerateProperty (Type type, PropertyInfo pi, List instance_fields_ // we must look if the type has an [Availability] attribute PrintPlatformAttributesIfInlined (minfo); - PrintAttributes (pi, preserve:true, advice:true); + PrintAttributes (pi, preserve:true, advice:true, bindAs:true); string propertyTypeName; if (minfo.protocolize) { @@ -5533,16 +5535,42 @@ public void PrintNotImplementedAttribute (ICustomAttributeProvider mi) print ($"[NotImplemented ({Quote (p.Message)})]"); } - public void PrintAttributes (MemberInfo mi, bool platform = false, bool preserve = false, bool advice = false, bool notImplemented = false) + public void PrintBindAsAttribute (ICustomAttributeProvider mi, StringBuilder sb = null) + { + var p = GetBindAsAttribute (mi); + if (p == null) + return; + + var property = mi as PropertyInfo; + var method = mi as MethodInfo; + var param = mi as ParameterInfo; + var originalType = method?.ReturnType ?? property?.PropertyType; + originalType = originalType ?? param?.ParameterType; + + var type = TypeManager.GetUnderlyingNullableType (p.Type) ?? p.Type; + var pReturn = method != null ? "return: " : string.Empty; + var pBoolean = p.IsNullable ? "?" : string.Empty; + + var attribstr = $"[{pReturn}BindAs (typeof ({type.FullName}{pBoolean}), OriginalType = typeof ({originalType.FullName}))]"; + + if (sb != null) + sb.Append ($"{attribstr} "); + else + print (attribstr); + } + + public void PrintAttributes (ICustomAttributeProvider mi, bool platform = false, bool preserve = false, bool advice = false, bool notImplemented = false, bool bindAs = false) { if (platform) - PrintPlatformAttributes (mi); + PrintPlatformAttributes (mi as MemberInfo); if (preserve) PrintPreserveAttribute (mi); if (advice) PrintAdviceAttribute (mi); if (notImplemented) PrintNotImplementedAttribute (mi); + if (bindAs) + PrintBindAsAttribute (mi); } public void ComputeLibraryName (FieldAttribute fieldAttr, Type type, string propertyName, out string library_name, out string library_path) diff --git a/tests/generator/bindas1048error.cs b/tests/generator/bindas1048error.cs index 0de14d979242..5462f428017d 100644 --- a/tests/generator/bindas1048error.cs +++ b/tests/generator/bindas1048error.cs @@ -2,6 +2,7 @@ using System.Drawing; using Foundation; using CoreMedia; +using ObjCRuntime; namespace BindAs1048ErrorTests { diff --git a/tests/generator/bindas1049error.cs b/tests/generator/bindas1049error.cs index d39169f86cfb..fd531a3b75dd 100644 --- a/tests/generator/bindas1049error.cs +++ b/tests/generator/bindas1049error.cs @@ -2,6 +2,7 @@ using System.Drawing; using Foundation; using CoreMedia; +using ObjCRuntime; namespace BindAs1049ErrorTests { diff --git a/tests/generator/bindas1050modelerror.cs b/tests/generator/bindas1050modelerror.cs index c55af9777414..5836983f2e6d 100644 --- a/tests/generator/bindas1050modelerror.cs +++ b/tests/generator/bindas1050modelerror.cs @@ -2,6 +2,7 @@ using System.Drawing; using Foundation; using CoreAnimation; +using ObjCRuntime; namespace BindAs1050ModelErrorTests { diff --git a/tests/generator/bindas1050protocolerror.cs b/tests/generator/bindas1050protocolerror.cs index fde0a9568645..8524c20fbead 100644 --- a/tests/generator/bindas1050protocolerror.cs +++ b/tests/generator/bindas1050protocolerror.cs @@ -2,6 +2,7 @@ using System.Drawing; using Foundation; using CoreAnimation; +using ObjCRuntime; namespace BindAs1050ProtocolErrorTests { diff --git a/tests/generator/bindastests.cs b/tests/generator/bindastests.cs index 8b93cd2fa605..80f03ce3e996 100644 --- a/tests/generator/bindastests.cs +++ b/tests/generator/bindastests.cs @@ -3,6 +3,7 @@ using Foundation; using CoreMedia; using CoreAnimation; +using ObjCRuntime; namespace BindAsTests { @@ -80,9 +81,10 @@ interface MyFooClass { [Export ("getScrollArrayEnum:")] NSString [] GetScrollArrayEnum ([BindAs (typeof (CAScroll []))] NSString [] arg1); - [return: BindAs (typeof (CAScroll? []))] - [Export ("getScrollArrayNullableEnum:")] - NSString [] GetScrollArrayEnumNullable ([BindAs (typeof (CAScroll? []))] NSString [] arg1); + // Bug #57797 + // [return: BindAs (typeof (CAScroll? []))] + // [Export ("getScrollArrayNullableEnum:")] + // NSString [] GetScrollArrayEnumNullable ([BindAs (typeof (CAScroll? []))] NSString [] arg1); [BindAs (typeof (CAScroll []))] [Export ("scrollEnumArray2")] @@ -92,9 +94,10 @@ interface MyFooClass { [Export ("getScrollArrayEnum2:")] NSNumber [] GetScrollArrayEnum2 ([BindAs (typeof (CAScroll []))] NSNumber [] arg1); - [return: BindAs (typeof (CAScroll? []))] - [Export ("getScrollArrayNullableEnum2:")] - NSNumber [] GetScrollArrayNullableEnum2 ([BindAs (typeof (CAScroll? []))] NSNumber [] arg1); + // Bug #57797 + // [return: BindAs (typeof (CAScroll? []))] + // [Export ("getScrollArrayNullableEnum2:")] + // NSNumber [] GetScrollArrayNullableEnum2 ([BindAs (typeof (CAScroll? []))] NSNumber [] arg1); [BindAs (typeof (CMTime []))] [Export ("timeEnumArray")] @@ -104,9 +107,10 @@ interface MyFooClass { [Export ("getTimeEnumArray:")] NSValue [] GetTimeEnumArray ([BindAs (typeof (CMTime []))] NSValue [] arg1); - [return: BindAs (typeof (CMTime? []))] - [Export ("getTimeEnumNullableArray:")] - NSValue [] GetTimeEnumNullableArray ([BindAs (typeof (CMTime? []))] NSValue [] arg1); + // Bug #57797 + // [return: BindAs (typeof (CMTime? []))] + // [Export ("getTimeEnumNullableArray:")] + // NSValue [] GetTimeEnumNullableArray ([BindAs (typeof (CMTime? []))] NSValue [] arg1); [BindAs (typeof (CAScroll))] [Export ("scrollFooEnum")] From 73a886701920083def343bd8713f3ef11e1fbd18 Mon Sep 17 00:00:00 2001 From: Alex Soto Date: Tue, 20 Jun 2017 23:29:49 -0500 Subject: [PATCH 02/46] [generator] Add nullcheck (?.) when using nullables with BindAs Generator diff: https://gist.github.com/rolfbjarne/8dcc78f9355b4fc621e98e3e7544baa9 --- src/generator.cs | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/src/generator.cs b/src/generator.cs index df8cfe25fb70..7d1e76632698 100644 --- a/src/generator.cs +++ b/src/generator.cs @@ -1388,7 +1388,9 @@ string GetFromBindAsWrapper (MemberInformation minfo) throw new BindingException (1050, true, "[BindAs] cannot be used inside Protocol or Model types. Type: {0}", declaringType.Name); var attrib = GetBindAsAttribute (minfo.mi); - var retType = TypeManager.GetUnderlyingNullableType (attrib.Type) ?? attrib.Type; + var nullableRetType = TypeManager.GetUnderlyingNullableType (attrib.Type); + var isNullable = nullableRetType != null; + var retType = isNullable ? nullableRetType : attrib.Type; var isValueType = retType.IsValueType; var append = string.Empty; var property = minfo.mi as PropertyInfo; @@ -1405,6 +1407,8 @@ string GetFromBindAsWrapper (MemberInformation minfo) else throw new BindingException (1049, true, GetBindAsExceptionString ("unbox", retType.Name, originalReturnType.Name, "container", minfo.mi.Name)); } + if (isNullable) + append = $"?{append}"; } else if (originalReturnType == TypeManager.NSValue) { if (!NSValueReturnMap.TryGetValue (retType, out append)) { // HACK: These are problematic for X.M due to we do not ship System.Drawing for Full profile @@ -1413,22 +1417,28 @@ string GetFromBindAsWrapper (MemberInformation minfo) else throw new BindingException (1049, true, GetBindAsExceptionString ("unbox", retType.Name, originalReturnType.Name, "container", minfo.mi.Name)); } + if (isNullable) + append = $"?{append}"; } else if (originalReturnType == TypeManager.NSString && IsSmartEnum (retType)) { append = $"{FormatType (retType.DeclaringType, retType)}Extensions.GetValue ("; } else if (originalReturnType.IsArray) { var arrType = originalReturnType.GetElementType (); - var arrRetType = TypeManager.GetUnderlyingNullableType (retType.GetElementType ()) ?? retType.GetElementType (); + var nullableElementType = TypeManager.GetUnderlyingNullableType (retType.GetElementType ()); + var arrIsNullable = nullableElementType != null; + var arrRetType = arrIsNullable ? nullableElementType : retType.GetElementType (); var valueFetcher = string.Empty; if (arrType == TypeManager.NSString) append = $"ptr => {{\n\tusing (var str = Runtime.GetNSObject (ptr)) {{\n\t\treturn {FormatType (arrRetType.DeclaringType, arrRetType)}Extensions.GetValue (str);\n\t}}\n}}"; else if (arrType == TypeManager.NSNumber) { - if (NSNumberReturnMap.TryGetValue (arrRetType, out valueFetcher) || arrRetType.IsEnum) - append = string.Format ("ptr => {{\n\tusing (var num = Runtime.GetNSObject (ptr)) {{\n\t\treturn ({1}) num{0};\n\t}}\n}}", arrRetType.IsEnum ? ".Int32Value" : valueFetcher, FormatType (arrRetType.DeclaringType, arrRetType)); + if (NSNumberReturnMap.TryGetValue (arrRetType, out valueFetcher) || arrRetType.IsEnum) { + var getterStr = string.Format ("{0}{1}", arrIsNullable ? "?" : string.Empty, arrRetType.IsEnum ? ".Int32Value" : valueFetcher); + append = string.Format ("ptr => {{\n\tusing (var num = Runtime.GetNSObject (ptr)) {{\n\t\treturn ({1}) num{0};\n\t}}\n}}", getterStr, FormatType (arrRetType.DeclaringType, arrRetType)); + } else throw new BindingException (1049, true, GetBindAsExceptionString ("unbox", retType.Name, arrType.Name, "array", minfo.mi.Name)); } else if (arrType == TypeManager.NSValue) { if (arrRetType.Name == "RectangleF" || arrRetType.Name == "SizeF" || arrRetType.Name == "PointF") - valueFetcher = $".{arrRetType.Name}Value"; + valueFetcher = $"{(arrIsNullable ? "?" : string.Empty)}.{arrRetType.Name}Value"; else if (!NSValueReturnMap.TryGetValue (arrRetType, out valueFetcher)) throw new BindingException (1049, true, GetBindAsExceptionString ("unbox", retType.Name, arrType.Name, "array", minfo.mi.Name)); From 50f682fab4a4eeca88f8486147f5f97367e907ed Mon Sep 17 00:00:00 2001 From: Rolf Bjarne Kvinge Date: Fri, 23 Jun 2017 12:58:30 +0200 Subject: [PATCH 03/46] [generator] Don't try creating NSNumber/NSValue for null. Generator diff (empty): https://gist.github.com/rolfbjarne/eaf290c19bdd1845a2bdd40d5fe0d88f --- src/generator.cs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/generator.cs b/src/generator.cs index 7d1e76632698..9a1799bf07c0 100644 --- a/src/generator.cs +++ b/src/generator.cs @@ -1264,13 +1264,14 @@ string GetToBindAsWrapper (MemberInformation minfo = null, ParameterInfo pi = nu var isEnum = retType.IsEnum; var parameterName = pi != null ? pi.Name.GetSafeParamName () : "value"; var denullify = isNullable ? ".Value" : string.Empty; + var nullCheck = isNullable ? $"{parameterName} == null ? null : " : string.Empty; if (isNullable || !isValueType) temp = string.Format ("{0} == null ? null : ", parameterName); if (originalType == TypeManager.NSNumber) { var enumCast = isEnum ? $"(int)" : string.Empty; - temp = string.Format ("new NSNumber ({2}{1}{0});", denullify, parameterName, enumCast); + temp = string.Format ("{3}new NSNumber ({2}{1}{0});", denullify, parameterName, enumCast, nullCheck); } else if (originalType == TypeManager.NSValue) { var typeStr = string.Empty; @@ -1281,7 +1282,7 @@ string GetToBindAsWrapper (MemberInformation minfo = null, ParameterInfo pi = nu else throw new BindingException (1049, true, GetBindAsExceptionString ("box", retType.Name, originalType.Name, "container", minfo?.mi?.Name ?? pi?.Name)); } - temp = string.Format ("NSValue.From{0} ({2}{1});", typeStr, denullify, parameterName); + temp = string.Format ("{3}NSValue.From{0} ({2}{1});", typeStr, denullify, parameterName, nullCheck); } else if (originalType == TypeManager.NSString && IsSmartEnum (retType)) { temp = string.Format ("{1}{0}.GetConstant ();", denullify, parameterName); } else if (originalType.IsArray) { From eb3d329bb44612c7042c873a5d5e9fc99272f337 Mon Sep 17 00:00:00 2001 From: Rolf Bjarne Kvinge Date: Mon, 3 Jul 2017 14:40:50 +0200 Subject: [PATCH 04/46] [generator] Add helpful make target to only rebuild the generator. --- src/Makefile.generator | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/Makefile.generator b/src/Makefile.generator index 41ffb3f93591..709072ca2871 100644 --- a/src/Makefile.generator +++ b/src/Makefile.generator @@ -322,3 +322,11 @@ $(MAC_BUILD_DIR)/Xamarin.Mac-%.BindingAttributes.dll: generator-attributes.cs Ma $(Q) mkdir -p $(dir $@) $(Q_GEN) $(SYSTEM_MONO) --debug $(PMCS_EXE) -profile:"$(TOP)/src/pmcs-profiles/$*" -compiler:$(SYSTEM_MCS) -global-replace:"^XamCore.=" -out:$@ -debug generator-attributes.cs -target:library endif + +bgen: \ + $(IOS_DESTDIR)$(MONOTOUCH_PREFIX)/lib/bgen/monotouch.BindingAttributes.dll \ + $(IOS_DESTDIR)$(MONOTOUCH_PREFIX)/lib/bgen/Xamarin.iOS.BindingAttributes.dll \ + $(IOS_DESTDIR)$(MONOTOUCH_PREFIX)/lib/bgen/bgen.exe \ + $(IOS_DESTDIR)$(MONOTOUCH_PREFIX)/bin/bgen \ + + From 1c779fc1769017fa811698234cc51998847b8497 Mon Sep 17 00:00:00 2001 From: Rolf Bjarne Kvinge Date: Mon, 3 Jul 2017 15:29:20 +0200 Subject: [PATCH 05/46] [src] Change Stret.Need* to take the return type itself, instead of the MethodInfo. This makes it possible to check if a type that isn't the return type of a MethodInfo needs stret. --- src/ObjCRuntime/Registrar.cs | 10 +++++----- src/ObjCRuntime/Stret.cs | 20 ++++++++++---------- src/generator.cs | 16 ++++++++-------- 3 files changed, 23 insertions(+), 23 deletions(-) diff --git a/src/ObjCRuntime/Registrar.cs b/src/ObjCRuntime/Registrar.cs index 6c02135f643e..fff21ff7440d 100644 --- a/src/ObjCRuntime/Registrar.cs +++ b/src/ObjCRuntime/Registrar.cs @@ -621,17 +621,17 @@ public Trampoline Trampoline { var mi = (System.Reflection.MethodInfo) Method; bool is_stret; #if __WATCHOS__ - is_stret = Runtime.Arch == Arch.DEVICE ? Stret.ArmNeedStret (mi) : Stret.X86NeedStret (mi); + is_stret = Runtime.Arch == Arch.DEVICE ? Stret.ArmNeedStret (mi.ReturnType) : Stret.X86NeedStret (mi.ReturnType); #elif MONOMAC - is_stret = IntPtr.Size == 8 ? Stret.X86_64NeedStret (mi) : Stret.X86NeedStret (mi); + is_stret = IntPtr.Size == 8 ? Stret.X86_64NeedStret (mi.ReturnType) : Stret.X86NeedStret (mi.ReturnType); #elif __IOS__ if (Runtime.Arch == Arch.DEVICE) { - is_stret = IntPtr.Size == 4 && Stret.ArmNeedStret (mi); + is_stret = IntPtr.Size == 4 && Stret.ArmNeedStret (mi.ReturnType); } else { - is_stret = IntPtr.Size == 4 ? Stret.X86NeedStret (mi) : Stret.X86_64NeedStret (mi); + is_stret = IntPtr.Size == 4 ? Stret.X86NeedStret (mi.ReturnType) : Stret.X86_64NeedStret (mi.ReturnType); } #elif __TVOS__ - is_stret = Runtime.Arch == Arch.SIMULATOR && Stret.X86_64NeedStret (mi); + is_stret = Runtime.Arch == Arch.SIMULATOR && Stret.X86_64NeedStret (mi.ReturnType); #else #error unknown architecture #endif diff --git a/src/ObjCRuntime/Stret.cs b/src/ObjCRuntime/Stret.cs index 4666d69af725..47528c1bfa84 100644 --- a/src/ObjCRuntime/Stret.cs +++ b/src/ObjCRuntime/Stret.cs @@ -111,7 +111,7 @@ static bool IsMagicTypeOrCorlibType (Type t) } } - public static bool ArmNeedStret (MethodInfo mi) + public static bool ArmNeedStret (Type returnType) { bool has32bitArm; #if BGENERATOR @@ -124,7 +124,7 @@ public static bool ArmNeedStret (MethodInfo mi) if (!has32bitArm) return false; - Type t = mi.ReturnType; + Type t = returnType; if (!t.IsValueType || t.IsEnum || IsMagicTypeOrCorlibType (t)) return false; @@ -184,9 +184,9 @@ public static bool ArmNeedStret (MethodInfo mi) return true; } - public static bool X86NeedStret (MethodInfo mi) + public static bool X86NeedStret (Type returnType) { - Type t = mi.ReturnType; + Type t = returnType; if (!t.IsValueType || t.IsEnum || IsMagicTypeOrCorlibType (t)) return false; @@ -203,12 +203,12 @@ public static bool X86NeedStret (MethodInfo mi) return false; } - public static bool X86_64NeedStret (MethodInfo mi) + public static bool X86_64NeedStret (Type returnType) { if (!isUnified) return false; - Type t = mi.ReturnType; + Type t = returnType; if (!t.IsValueType || t.IsEnum || IsMagicTypeOrCorlibType (t)) return false; @@ -341,15 +341,15 @@ static void GetValueTypeSize (Type original_type, Type type, List field_ty } } - public static bool NeedStret (MethodInfo mi) + public static bool NeedStret (Type returnType) { - if (X86NeedStret (mi)) + if (X86NeedStret (returnType)) return true; - if (X86_64NeedStret (mi)) + if (X86_64NeedStret (returnType)) return true; - if (ArmNeedStret (mi)) + if (ArmNeedStret (returnType)) return true; return false; diff --git a/src/generator.cs b/src/generator.cs index 9a1799bf07c0..46c91d102ec4 100644 --- a/src/generator.cs +++ b/src/generator.cs @@ -1972,12 +1972,12 @@ void DeclareInvoker (MethodInfo mi) try { if (Compat) { - bool arm_stret = Stret.ArmNeedStret (mi); + bool arm_stret = Stret.ArmNeedStret (mi.ReturnType); bool is_aligned = AttributeManager.HasAttribute (mi); RegisterMethod (arm_stret, mi, MakeSig (mi, arm_stret, arm_stret && is_aligned), arm_stret && is_aligned); RegisterMethod (arm_stret, mi, MakeSuperSig (mi, arm_stret, arm_stret && is_aligned), arm_stret && is_aligned); - bool x86_stret = Stret.X86NeedStret (mi); + bool x86_stret = Stret.X86NeedStret (mi.ReturnType); if (x86_stret != arm_stret){ RegisterMethod (x86_stret, mi, MakeSig (mi, x86_stret, x86_stret && is_aligned), x86_stret && is_aligned); RegisterMethod (x86_stret, mi, MakeSuperSig (mi, x86_stret, x86_stret && is_aligned), x86_stret && is_aligned); @@ -3602,8 +3602,8 @@ void GenerateInvoke (bool supercall, MethodInfo mi, MemberInformation minfo, str return; } - bool arm_stret = Stret.ArmNeedStret (mi); - bool x86_stret = Stret.X86NeedStret (mi); + bool arm_stret = Stret.ArmNeedStret (mi.ReturnType); + bool x86_stret = Stret.X86NeedStret (mi.ReturnType); bool aligned = AttributeManager.HasAttribute (mi); if (CurrentPlatform == PlatformName.MacOSX) { @@ -3629,9 +3629,9 @@ void GenerateInvoke (bool supercall, MethodInfo mi, MemberInformation minfo, str void GenerateNewStyleInvoke (bool supercall, MethodInfo mi, MemberInformation minfo, string selector, string[] args, bool assign_to_temp, Type category_type) { - bool arm_stret = Stret.ArmNeedStret (mi); - bool x86_stret = Stret.X86NeedStret (mi); - bool x64_stret = Stret.X86_64NeedStret (mi); + bool arm_stret = Stret.ArmNeedStret (mi.ReturnType); + bool x86_stret = Stret.X86NeedStret (mi.ReturnType); + bool x64_stret = Stret.X86_64NeedStret (mi.ReturnType); bool dual_enum = HasNativeEnumInSignature (mi); bool is_stret_multi = arm_stret || x86_stret || x64_stret; bool need_multi_path = is_stret_multi || dual_enum; @@ -3984,7 +3984,7 @@ void GenerateThreadCheck () bool CheckNeedStret (MethodInfo mi) { try { - return Stret.NeedStret (mi); + return Stret.NeedStret (mi.ReturnType); } catch (TypeLoadException ex) { throw new BindingException (0001, true, $"The .NET runtime could not load the {mi.ReturnType.Name} type. Message: {ex.Message}"); From cc66404f6413407a172870a0d07a7bca478c0bc8 Mon Sep 17 00:00:00 2001 From: Rolf Bjarne Kvinge Date: Mon, 3 Jul 2017 15:39:12 +0200 Subject: [PATCH 06/46] [generator] Generate a 'GetConstant (T?)' and 'GetNullableValue' for smart enums. This makes it possible to also generate code that works properly with nullable smart enums (both the managed enum and the native string). Generator diff: https://gist.github.com/rolfbjarne/90324a90de2d812eec078756d3ad2b66 --- src/generator-enums.cs | 24 ++++++++++++++++++++++++ src/generator.cs | 8 ++++---- 2 files changed, 28 insertions(+), 4 deletions(-) diff --git a/src/generator-enums.cs b/src/generator-enums.cs index 8eba548a1cbe..22cadb74b38b 100644 --- a/src/generator-enums.cs +++ b/src/generator-enums.cs @@ -165,6 +165,18 @@ void GenerateEnum (Type type) print (""); } + print ("public static NSString GetConstant (this {0}? self)", type.Name); + print ("{"); + indent++; + print ("if (self == null)"); + indent++; + print ("return null;"); + indent--; + print ("return GetConstant (self.Value);"); + indent--; + print ("}"); + print (""); + print ("public static NSString GetConstant (this {0} self)", type.Name); print ("{"); indent++; @@ -189,6 +201,18 @@ void GenerateEnum (Type type) print (""); + print ("public static {0}? GetNullableValue (NSString constant)", type.Name); + print ("{"); + indent++; + print ("if (constant == null)"); + indent++; + print ("return null;"); + indent--; + print ("return GetValue (constant);"); + indent--; + print ("}"); + print (""); + print ("public static {0} GetValue (NSString constant)", type.Name); print ("{"); indent++; diff --git a/src/generator.cs b/src/generator.cs index 46c91d102ec4..63272def0727 100644 --- a/src/generator.cs +++ b/src/generator.cs @@ -1284,14 +1284,14 @@ string GetToBindAsWrapper (MemberInformation minfo = null, ParameterInfo pi = nu } temp = string.Format ("{3}NSValue.From{0} ({2}{1});", typeStr, denullify, parameterName, nullCheck); } else if (originalType == TypeManager.NSString && IsSmartEnum (retType)) { - temp = string.Format ("{1}{0}.GetConstant ();", denullify, parameterName); + temp = $"{parameterName}.GetConstant ();"; } else if (originalType.IsArray) { var arrType = originalType.GetElementType (); var arrRetType = TypeManager.GetUnderlyingNullableType (retType.GetElementType ()) ?? retType.GetElementType (); var valueConverter = string.Empty; if (arrType == TypeManager.NSString) - valueConverter = $"o{denullify}.GetConstant (), {parameterName});"; + valueConverter = $"o.GetConstant (), {parameterName});"; else if (arrType == TypeManager.NSNumber) { var cast = arrRetType.IsEnum ? "(int)" : string.Empty; valueConverter = $"new NSNumber ({cast}o{denullify}), {parameterName});"; @@ -1421,7 +1421,7 @@ string GetFromBindAsWrapper (MemberInformation minfo) if (isNullable) append = $"?{append}"; } else if (originalReturnType == TypeManager.NSString && IsSmartEnum (retType)) { - append = $"{FormatType (retType.DeclaringType, retType)}Extensions.GetValue ("; + append = $"{FormatType (retType.DeclaringType, retType)}Extensions.{(isNullable ? "GetNullableValue" : "GetValue")} ("; } else if (originalReturnType.IsArray) { var arrType = originalReturnType.GetElementType (); var nullableElementType = TypeManager.GetUnderlyingNullableType (retType.GetElementType ()); @@ -1429,7 +1429,7 @@ string GetFromBindAsWrapper (MemberInformation minfo) var arrRetType = arrIsNullable ? nullableElementType : retType.GetElementType (); var valueFetcher = string.Empty; if (arrType == TypeManager.NSString) - append = $"ptr => {{\n\tusing (var str = Runtime.GetNSObject (ptr)) {{\n\t\treturn {FormatType (arrRetType.DeclaringType, arrRetType)}Extensions.GetValue (str);\n\t}}\n}}"; + append = $"ptr => {{\n\tusing (var str = Runtime.GetNSObject (ptr)) {{\n\t\treturn {FormatType (arrRetType.DeclaringType, arrRetType)}Extensions.{(isNullable ? "GetNullableValue" : "GetValue")} (str);\n\t}}\n}}"; else if (arrType == TypeManager.NSNumber) { if (NSNumberReturnMap.TryGetValue (arrRetType, out valueFetcher) || arrRetType.IsEnum) { var getterStr = string.Format ("{0}{1}", arrIsNullable ? "?" : string.Empty, arrRetType.IsEnum ? ".Int32Value" : valueFetcher); From e82c59815fdaf4636d995ddabd4c9285ffe41ed2 Mon Sep 17 00:00:00 2001 From: Rolf Bjarne Kvinge Date: Mon, 3 Jul 2017 19:06:06 +0200 Subject: [PATCH 07/46] [Foundation] Make NSArray.FromNSObjects return null on null input. This makes it easier to generate code that uses this function and which can get null input from Objective-C. --- src/Foundation/NSArray.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Foundation/NSArray.cs b/src/Foundation/NSArray.cs index 3405a9a7883b..37ba1ab52124 100644 --- a/src/Foundation/NSArray.cs +++ b/src/Foundation/NSArray.cs @@ -64,7 +64,7 @@ public static NSArray FromNSObjects (Func nsobjectificator, para if (nsobjectificator == null) throw new ArgumentNullException (nameof (nsobjectificator)); if (items == null) - throw new ArgumentNullException (nameof (items)); + return null; var arr = new NSObject [items.Length]; for (int i = 0; i < items.Length; i++) { From fecf315fab1a5e5fb38d1e308e617d44935b8437 Mon Sep 17 00:00:00 2001 From: Rolf Bjarne Kvinge Date: Mon, 3 Jul 2017 19:16:33 +0200 Subject: [PATCH 08/46] [tests] Add generated runtime tests for the BindAs attribute. --- tests/test-libraries/.gitignore | 2 + tests/test-libraries/libtest.h | 3 + tests/test-libraries/libtest.m | 2 + tests/test-libraries/testgenerator.cs | 1128 ++++++++++++++++++++++++- tests/tests.sln | 28 + 5 files changed, 1160 insertions(+), 3 deletions(-) diff --git a/tests/test-libraries/.gitignore b/tests/test-libraries/.gitignore index dcb5821d82a5..444f92e71a8f 100644 --- a/tests/test-libraries/.gitignore +++ b/tests/test-libraries/.gitignore @@ -6,4 +6,6 @@ libtest-object.m libtest.structs.h libtest.properties.h libtest.decompile.m +libtest.methods.h +libtest.methods.m diff --git a/tests/test-libraries/libtest.h b/tests/test-libraries/libtest.h index 5f1918d86b20..c2ed31508e2c 100644 --- a/tests/test-libraries/libtest.h +++ b/tests/test-libraries/libtest.h @@ -101,6 +101,9 @@ typedef unsigned int (^RegistrarTestBlock) (unsigned int magic); -(bool) testBlocks; -(void) idAsIntPtr: (id)p1; + +#include "libtest.methods.h" + -(void) outNSErrorOnStack:(int)i1 i:(int)i2 i:(int)i3 i:(int)i4 i:(int)i5 i:(int)i6 err:(NSError **)err; // 6 in regs, 7th (out) in mem (on all architectures) -(void) outNSErrorOnStack:(id)obj1 obj:(id)obj2 obj:(id)obj3 int64:(long long)l4 i:(int)i5 err:(NSError **)err; // 5 in regs, 6th (out) in mem (on at least x86-64) @end diff --git a/tests/test-libraries/libtest.m b/tests/test-libraries/libtest.m index cc6eab828c30..b24420e9ae6b 100644 --- a/tests/test-libraries/libtest.m +++ b/tests/test-libraries/libtest.m @@ -429,6 +429,8 @@ -(void) idAsIntPtr: (id)p1 // Nothing to do here. } +#include "libtest.methods.m" + -(void) outNSErrorOnStack:(int)i1 i:(int)i2 i:(int)i3 i:(int)i4 i:(int)i5 i:(int)i6 err:(NSError **)err { // Nothing to do here diff --git a/tests/test-libraries/testgenerator.cs b/tests/test-libraries/testgenerator.cs index a0fed5a3787b..506f6bfafd7b 100644 --- a/tests/test-libraries/testgenerator.cs +++ b/tests/test-libraries/testgenerator.cs @@ -49,6 +49,56 @@ enum Architecture static string [] structs = structs_and_stret.Select ((v) => v.IndexOf (':') >= 0 ? v.Substring (0, v.IndexOf (':')) : v).ToArray (); static Architecture [] strets = structs_and_stret.Select ((v) => v.IndexOf (':') >= 0 ? (Architecture) int.Parse (v.Substring (v.IndexOf (':') + 1)) : Architecture.None).ToArray (); + class BindAsData + { + public string Managed; + public string Native; + public string ManagedCondition; + public string ManagedNewExpression; + public string Map; + public string MapFrom; + } + + static BindAsData [] bindas_nsnumber = new [] { + new BindAsData { Managed = "Byte", Native = "uint8_t", ManagedNewExpression = "((byte) 1)", Map = ".ByteValue" }, + new BindAsData { Managed = "SByte", Native = "int8_t", ManagedNewExpression = "((sbyte) 1)", Map = ".SByteValue" }, + new BindAsData { Managed = "Int16", Native = "int16_t", ManagedNewExpression = "((short) 1)", Map = ".Int16Value" }, + new BindAsData { Managed = "UInt16", Native = "uint16_t", ManagedNewExpression = "((ushort) 1)", Map = ".UInt16Value" }, + new BindAsData { Managed = "Int32", Native = "int32_t", ManagedNewExpression = "((int) 1)", Map = ".Int32Value" }, + new BindAsData { Managed = "UInt32", Native = "uint32_t", ManagedNewExpression = "((uint) 1)", Map = ".UInt32Value" }, + new BindAsData { Managed = "Int64", Native = "int64_t", ManagedNewExpression = "((long) 1)", Map = ".Int64Value" }, + new BindAsData { Managed = "UInt64", Native = "uint64_t", ManagedNewExpression = "((ulong) 1)", Map = ".UInt64Value" }, + new BindAsData { Managed = "Single", Native = "float", ManagedNewExpression = "((float) 1.1)", Map = ".FloatValue" }, + new BindAsData { Managed = "Double", Native = "double", ManagedNewExpression = "((double) 1.2)", Map = ".DoubleValue" }, + new BindAsData { Managed = "nint", Native = "NSInteger", ManagedNewExpression = "((nint) 1)", Map = ".NIntValue" }, + new BindAsData { Managed = "nuint", Native = "NSUInteger", ManagedNewExpression = "((nuint) 1)", Map = ".NUIntValue" }, + new BindAsData { Managed = "nfloat", Native = "NSFloat", ManagedNewExpression = "((nfloat) 1)", Map = ".NFloatValue" }, + new BindAsData { Managed = "Boolean", Native = "BOOL", ManagedNewExpression = "true", Map = ".BoolValue" }, + }; + static BindAsData[] bindas_nsvalue = new [] { + new BindAsData { Managed = "CGAffineTransform", Native = "CGAffineTransform", ManagedNewExpression = "new CGAffineTransform (1, 2, 3, 4, 5, 6)", Map = ".CGAffineTransformValue", MapFrom = "FromCGAffineTransform" }, + new BindAsData { Managed = "NSRange", Native = "NSRange", ManagedNewExpression = "new NSRange (7, 8)", Map = ".RangeValue", MapFrom = "FromRange" }, + new BindAsData { Managed = "CGVector", Native = "CGVector", ManagedNewExpression = "new CGVector (9, 10)", Map = ".CGVectorValue", MapFrom = "FromCGVector" }, + new BindAsData { Managed = "SCNMatrix4", Native = "SCNMatrix4", ManagedNewExpression = "SCNMatrix4.Identity", Map = ".SCNMatrix4Value", MapFrom = "FromSCNMatrix4" }, + new BindAsData { Managed = "CLLocationCoordinate2D", Native = "CLLocationCoordinate2D", ManagedNewExpression = "new CLLocationCoordinate2D (11, 12)", Map = ".CoordinateValue", MapFrom = "FromMKCoordinate" }, + new BindAsData { Managed = "SCNVector3", Native = "SCNVector3", ManagedNewExpression = "new SCNVector3 (13, 14, 15)", Map = ".Vector3Value", MapFrom = "FromVector" }, + new BindAsData { Managed = "SCNVector4", Native = "SCNVector4", ManagedNewExpression = "new SCNVector4 (16, 17, 18, 19)", Map = ".Vector4Value", MapFrom = "FromVector" }, + new BindAsData { Managed = "CGPoint", Native = "CGPoint", ManagedCondition = "XAMCORE_2_0", ManagedNewExpression = "new CGPoint (19, 20)", Map = ".CGPointValue", MapFrom = "FromCGPoint" }, + new BindAsData { Managed = "CGSize", Native = "CGSize", ManagedCondition = "XAMCORE_2_0", ManagedNewExpression = "new CGSize (21, 22)", Map = ".CGSizeValue", MapFrom = "FromCGSize" }, + new BindAsData { Managed = "CGRect", Native = "CGRect", ManagedCondition = "XAMCORE_2_0", ManagedNewExpression = "new CGRect (23, 24, 25, 26)", Map = ".CGRectValue", MapFrom = "FromCGRect" }, + new BindAsData { Managed = "UIEdgeInsets", Native = "UIEdgeInsets", ManagedCondition = "HAVE_UIKIT", ManagedNewExpression = "new UIEdgeInsets (27, 28, 29, 30)", Map = ".UIEdgeInsetsValue", MapFrom = "FromUIEdgeInsets" }, + new BindAsData { Managed = "UIOffset", Native = "UIOffset", ManagedCondition = "HAVE_UIKIT", ManagedNewExpression = "new UIOffset (31, 32)", Map = ".UIOffsetValue", MapFrom = "FromUIOffset" }, + new BindAsData { Managed = "MKCoordinateSpan", Native = "MKCoordinateSpan", ManagedCondition = "HAVE_MAPKIT", ManagedNewExpression = "new MKCoordinateSpan (33, 34)", Map = ".CoordinateSpanValue", MapFrom = "FromMKCoordinateSpan" }, + new BindAsData { Managed = "CMTimeRange", Native = "CMTimeRange", ManagedCondition = "HAVE_COREMEDIA", ManagedNewExpression = "new CMTimeRange { Duration = new CMTime (37, 38), Start = new CMTime (39, 40) }", Map = ".CMTimeRangeValue", MapFrom = "FromCMTimeRange" }, + new BindAsData { Managed = "CMTime", Native = "CMTime", ManagedCondition = "HAVE_COREMEDIA", ManagedNewExpression = "new CMTime (35, 36)", Map = ".CMTimeValue", MapFrom = "FromCMTime" }, + new BindAsData { Managed = "CMTimeMapping", Native = "CMTimeMapping", ManagedCondition = "HAVE_COREMEDIA", ManagedNewExpression = "new CMTimeMapping { Source = new CMTimeRange { Duration = new CMTime (42, 43), Start = new CMTime (44, 45) } }", Map = ".CMTimeMappingValue", MapFrom = "FromCMTimeMapping" }, + new BindAsData { Managed = "CATransform3D", Native = "CATransform3D", ManagedCondition = "HAVE_COREANIMATION", ManagedNewExpression = "new CATransform3D { m11 = 41 }", Map = ".CATransform3DValue", MapFrom = "FromCATransform3D" }, + }; + + static BindAsData [] bindas_nsstring = new [] { + new BindAsData { Managed = "AVMediaTypes", ManagedNewExpression = "AVMediaTypes.Audio" }, + }; + static string GetNativeName (char t) { switch (t) { @@ -144,22 +194,178 @@ static void WriteLibTestPropertiesH () foreach (var s in structs) w.AppendLine ($"\t@property struct S{s} PS{s};"); + w.AppendLine (); + foreach (var v in bindas_nsnumber) { + w.AppendLine ($"\t@property (retain) NSNumber* P{v.Managed}Number;"); + w.AppendLine ($"\t@property (retain) NSArray * P{v.Managed}Array;"); + } + + w.AppendLine (); + foreach (var v in bindas_nsvalue) { + w.AppendLine ($"\t@property (retain) NSValue* P{v.Managed}Value;"); + w.AppendLine ($"\t@property (retain) NSArray * P{v.Managed}Array;"); + } + + w.AppendLine (); + foreach (var v in bindas_nsstring) { + w.AppendLine ($"\t@property (retain) NSString *PSmart{v.Managed}Property;"); + w.AppendLine ($"\t@property (retain) NSArray * PSmart{v.Managed}Properties;"); + } File.WriteAllText ("libtest.properties.h", w.ToString ()); } - static void WriteApiDefinition () + static void WriteLibTestMethodsH () { var w = new StringBuilder (); - w.AppendLine (@"using System; + w.AppendLine (); + foreach (var v in bindas_nsnumber) { + w.AppendLine ($"\t-(NSNumber *) get{v.Managed}NumberNonNullable;"); + w.AppendLine ($"\t-(void) set{v.Managed}NumberNonNullable: (NSNumber *) value;"); + w.AppendLine (); + w.AppendLine ($"\t-(NSNumber *) get{v.Managed}NumberNullable;"); + w.AppendLine ($"\t-(void) set{v.Managed}NumberNullable: (NSNumber *) value;"); + w.AppendLine (); + w.AppendLine ($"\t-(NSArray *) get{v.Managed}Array;"); + w.AppendLine ($"\t-(void) set{v.Managed}Array: (NSArray *) value;"); + w.AppendLine (); + } + + w.AppendLine (); + foreach (var v in bindas_nsvalue) { + w.AppendLine ($"\t-(NSValue *) get{v.Managed}ValueNonNullable;"); + w.AppendLine ($"\t-(void) set{v.Managed}ValueNonNullable: (NSValue *) value;"); + w.AppendLine (); + w.AppendLine ($"\t-(NSValue *) get{v.Managed}ValueNullable;"); + w.AppendLine ($"\t-(void) set{v.Managed}ValueNullable: (NSValue *) value;"); + w.AppendLine (); + w.AppendLine ($"\t-(NSArray *) get{v.Managed}Array;"); + w.AppendLine ($"\t-(void) set{v.Managed}Array: (NSArray *) value;"); + w.AppendLine (); + } + + w.AppendLine (); + foreach (var v in bindas_nsstring) { + // plain value + w.AppendLine ($"\t-(NSString *) getSmart{v.Managed}Value;"); + w.AppendLine ($"\t-(void) setSmart{v.Managed}Value: (NSString *) value;"); + w.AppendLine (); + + // nullable + w.AppendLine ($"\t-(NSString *) getSmartNullable{v.Managed}Value;"); + w.AppendLine ($"\t-(void) setSmartNullable{v.Managed}Value: (NSString *) value;"); + w.AppendLine (); + + // array of plain value + w.AppendLine ($"\t-(NSArray *) getSmart{v.Managed}Values;"); + w.AppendLine ($"\t-(void) setSmart{v.Managed}Values: (NSArray *) value;"); + w.AppendLine (); + } + + File.WriteAllText ("libtest.methods.h", w.ToString ()); + } + + static void WriteLibTestMethodsM () + { + var w = new StringBuilder (); + + w.AppendLine (); + foreach (var v in bindas_nsnumber) { + w.AppendLine ($"\t-(NSNumber *) get{v.Managed}NumberNonNullable {{ return self.P{v.Managed}Number; }}"); + w.AppendLine ($"\t-(void) set{v.Managed}NumberNonNullable: (NSNumber *) value {{ self.P{v.Managed}Number = value; }}"); + w.AppendLine (); + w.AppendLine ($"\t-(NSNumber *) get{v.Managed}NumberNullable {{ return self.P{v.Managed}Number; }}"); + w.AppendLine ($"\t-(void) set{v.Managed}NumberNullable: (NSNumber *) value {{ self.P{v.Managed}Number = value; }}"); + w.AppendLine (); + w.AppendLine ($"\t-(NSArray *) get{v.Managed}Array {{ return self.P{v.Managed}Array; }}"); + w.AppendLine ($"\t-(void) set{v.Managed}Array: (NSArray *) value {{ self.P{v.Managed}Array = value; }}"); + w.AppendLine (); + } + + w.AppendLine (); + foreach (var v in bindas_nsvalue) { + w.AppendLine ($"\t-(NSValue *) get{v.Managed}ValueNonNullable {{ return self.P{v.Managed}Value; }}"); + w.AppendLine ($"\t-(void) set{v.Managed}ValueNonNullable: (NSValue *) value {{ self.P{v.Managed}Value = value; }}"); + w.AppendLine (); + w.AppendLine ($"\t-(NSValue *) get{v.Managed}ValueNullable {{ return self.P{v.Managed}Value; }}"); + w.AppendLine ($"\t-(void) set{v.Managed}ValueNullable: (NSValue *) value {{ self.P{v.Managed}Value = value; }}"); + w.AppendLine (); + w.AppendLine ($"\t-(NSArray *) get{v.Managed}Array {{ return self.P{v.Managed}Array; }}"); + w.AppendLine ($"\t-(void) set{v.Managed}Array: (NSArray *) value {{ self.P{v.Managed}Array = value; }}"); + w.AppendLine (); + } + + w.AppendLine (); + foreach (var v in bindas_nsstring) { + // plain value + w.AppendLine ($"\t-(NSString *) getSmart{v.Managed}Value {{ return self.PSmart{v.Managed}Property; }}"); + w.AppendLine ($"\t-(void) setSmart{v.Managed}Value: (NSString *) value {{ self.PSmart{v.Managed}Property = value; }}"); + w.AppendLine (); + + // nullable + w.AppendLine ($"\t-(NSString *) getSmartNullable{v.Managed}Value {{ return self.PSmart{v.Managed}Property; }}"); + w.AppendLine ($"\t-(void) setSmartNullable{v.Managed}Value: (NSString *) value {{ self.PSmart{v.Managed}Property = value; }}"); + w.AppendLine (); + + // array of plain value + w.AppendLine ($"\t-(NSArray *) getSmart{v.Managed}Values {{ return self.PSmart{v.Managed}Properties; }}"); + w.AppendLine ($"\t-(void) setSmart{v.Managed}Values: (NSArray *) value {{ self.PSmart{v.Managed}Properties = value; }}"); + w.AppendLine (); + } + + File.WriteAllText ("libtest.methods.m", w.ToString ()); + } + + static void WriteFrameworkDefines (StringBuilder w) + { + w.AppendLine (@" +#if __IOS__ || __MACOS__ || __TVOS__ +#define HAVE_COREANIMATION +#endif + +#if __IOS__ || __MACOS__ || __TVOS__ +#define HAVE_COREMEDIA +#endif + +#if __IOS__ || __WATCHOS__ || __TVOS__ +#define HAVE_UIKIT +#endif + +#if XAMCORE_2_0 +#define HAVE_MAPKIT +#endif"); + + } + + static void WriteApiDefinition () + { + var w = new StringBuilder (); + WriteFrameworkDefines (w); + w.AppendLine (@" +using System; #if !__WATCHOS__ using System.Drawing; #endif #if __UNIFIED__ -using ObjCRuntime; +using AVFoundation; +#if HAVE_COREANIMATION +using CoreAnimation; +#endif +using CoreGraphics; +using CoreLocation; +#if HAVE_COREMEDIA +using CoreMedia; +#endif using Foundation; +#if HAVE_MAPKIT +using MapKit; +#endif +using ObjCRuntime; +using SceneKit; +#if HAVE_UIKIT using UIKit; +#endif #else using MonoTouch.ObjCRuntime; using MonoTouch.Foundation; @@ -177,6 +383,336 @@ partial interface ObjCRegistrarTest { w.AppendLine (); } + w.AppendLine ("\t\t// BindAs: NSNumber"); + foreach (var v in bindas_nsnumber) { + if (v.ManagedCondition != null) + w.AppendLine ($"#if {v.ManagedCondition}"); + + // no BindAs + + w.AppendLine ($"\t\t[Export (\"P{v.Managed}Number\")]"); + w.AppendLine ($"\t\tNSNumber P{v.Managed}Number {{ get; set; }}"); + + // plain value + w.AppendLine (); + w.AppendLine ("\t\t[Sealed]"); + w.AppendLine ($"\t\t[Export (\"P{v.Managed}Number\")]"); + w.AppendLine ($"\t\t[BindAs (typeof ({v.Managed}))]"); + w.AppendLine ($"\t\tNSNumber P{v.Managed}NumberNonNullable {{ get; set; }}"); + + w.AppendLine (); + w.AppendLine ($"\t\t[Export (\"get{v.Managed}NumberNonNullable\")]"); + w.AppendLine ($"\t\t[return: BindAs (typeof ({v.Managed}))]"); + w.AppendLine ($"\t\t[return: NullAllowed] // This should be the default"); + w.AppendLine ($"\t\tNSNumber Get{v.Managed}NumberNonNullable ();"); + + w.AppendLine (); + w.AppendLine ($"\t\t[Export (\"set{v.Managed}NumberNonNullable:\")]"); + w.AppendLine ($"\t\tvoid Set{v.Managed}NumberNonNullable ([BindAs (typeof ({v.Managed}))] NSNumber value);"); + + // nullable value + w.AppendLine (); + w.AppendLine ("\t\t[Sealed]"); + w.AppendLine ($"\t\t[Export (\"P{v.Managed}Number\")]"); + w.AppendLine ($"\t\t[BindAs (typeof ({v.Managed}?))]"); + w.AppendLine ($"\t\t[NullAllowed] // This should be the default"); + w.AppendLine ($"\t\tNSNumber P{v.Managed}NumberNullable {{ get; set; }}"); + + w.AppendLine (); + w.AppendLine ($"\t\t[Export (\"get{v.Managed}NumberNullable\")]"); + w.AppendLine ($"\t\t[return: BindAs (typeof ({v.Managed}?))]"); + w.AppendLine ($"\t\tNSNumber Get{v.Managed}NumberNullable ();"); + + w.AppendLine (); + w.AppendLine ($"\t\t[Export (\"set{v.Managed}NumberNullable:\")]"); + w.AppendLine ($"\t\tvoid Set{v.Managed}NumberNullable ([BindAs (typeof ({v.Managed}?))] [NullAllowed /* this should be the default */] NSNumber value);"); + + // ref/out plain value is not supported (error BI1048: bgen: Unsupported type Boolean decorated with [BindAs]) + // ref/out nullable value is not supported (error BI1048: bgen: Unsupported type Boolean decorated with [BindAs]) + + // array of plain value + w.AppendLine (); + w.AppendLine ("\t\t[Sealed]"); + w.AppendLine ($"\t\t[Export (\"P{v.Managed}Array\")]"); + w.AppendLine ($"\t\t[BindAs (typeof ({v.Managed}[]))]"); + w.AppendLine ($"\t\t[NullAllowed]"); + w.AppendLine ($"\t\tNSNumber[] P{v.Managed}Array {{ get; set; }}"); + + w.AppendLine (); + w.AppendLine ($"\t\t[Export (\"get{v.Managed}Array\")]"); + w.AppendLine ($"\t\t[return: BindAs (typeof ({v.Managed}[]))]"); + w.AppendLine ($"\t\t[return: NullAllowed]"); + w.AppendLine ($"\t\tNSNumber[] Get{v.Managed}Array ();"); + + w.AppendLine (); + w.AppendLine ($"\t\t[Export (\"set{v.Managed}Array:\")]"); + w.AppendLine ($"\t\tvoid Set{v.Managed}Array ([NullAllowed] [BindAs (typeof ({v.Managed}[]))] NSNumber[] value);"); + + // multidimensional array of plain value + // https://bugzilla.xamarin.com/show_bug.cgi?id=57795 + //w.AppendLine (); + //w.AppendLine ("\t\t[Sealed]"); + //w.AppendLine ($"\t\t[Export (\"P{v.Managed}Multi1Array\")]"); + //w.AppendLine ($"\t\t[BindAs (typeof ({v.Managed}[,]))]"); + //w.AppendLine ($"\t\tNSNumber[,] P{v.Managed}Multi1ArrayValue {{ get; set; }}"); + + // BI1048: bgen: Unsupported type Boolean[][] decorated with [BindAs] + //w.AppendLine (); + //w.AppendLine ("\t\t[Sealed]"); + //w.AppendLine ($"\t\t[Export (\"P{v.Managed}Multi2Array\")]"); + //w.AppendLine ($"\t\t[BindAs (typeof ({v.Managed}[][]))]"); + //w.AppendLine ($"\t\tNSNumber[][] P{v.Managed}Multi2ArrayValue {{ get; set; }}"); + + // array of nullable value + // https://bugzilla.xamarin.com/show_bug.cgi?id=57797 + //w.AppendLine (); + //w.AppendLine ("\t\t[Sealed]"); + //w.AppendLine ($"\t\t[Export (\"P{v.Managed}NullableArray\")]"); + //w.AppendLine ($"\t\t[BindAs (typeof ({v.Managed}?[]))]"); + //w.AppendLine ($"\t\tNSNumber[] P{v.Managed}NullableArrayValue {{ get; set; }}"); + + //w.AppendLine (); + //w.AppendLine ($"\t\t[Export (\"get{v.Managed}NullableArray\")]"); + //w.AppendLine ($"\t\t[return: BindAs (typeof ({v.Managed}?[]))]"); + //w.AppendLine ($"\t\tNSNumber[] Get{v.Managed}NullableArrayValue ();"); + + //w.AppendLine (); + //w.AppendLine ($"\t\t[Export (\"set{v.Managed}NullableArray:\")]"); + //w.AppendLine ($"\t\tvoid Set{v.Managed}NullableArray ([BindAs (typeof ({v.Managed}?[]))] NSNumber[] value);"); + + // multidimensional array of nullable value + // https://bugzilla.xamarin.com/show_bug.cgi?id=57797 + //w.AppendLine (); + //w.AppendLine ("\t\t[Sealed]"); + //w.AppendLine ($"\t\t[Export (\"P{v.Managed}NullableMulti1Array\")]"); + //w.AppendLine ($"\t\t[BindAs (typeof ({v.Managed}?[,]))]"); + //w.AppendLine ($"\t\tNSNumber[,] P{v.Managed}NullableMulti1ArrayValue {{ get; set; }}"); + + // BI1048: bgen: Unsupported type Nullable`1[][] decorated with [BindAs] + //w.AppendLine (); + //w.AppendLine ("\t\t[Sealed]"); + //w.AppendLine ($"\t\t[Export (\"P{v.Managed}NullableMulti2Array\")]"); + //w.AppendLine ($"\t\t[BindAs (typeof ({v.Managed}?[][]))]"); + //w.AppendLine ($"\t\tNSNumber[][] P{v.Managed}NullableMulti2ArrayValue {{ get; set; }}"); + + if (v.ManagedCondition != null) + w.AppendLine ("#endif"); + w.AppendLine (); + } + + w.AppendLine ("\t\t// BindAs: NSValue"); + foreach (var v in bindas_nsvalue) { + if (v.ManagedCondition != null) + w.AppendLine ($"#if {v.ManagedCondition}"); + + // no BindAs + + w.AppendLine ($"\t\t[Export (\"P{v.Managed}Value\")]"); + w.AppendLine ($"\t\tNSValue P{v.Managed}Value {{ get; set; }}"); + + // plain value + w.AppendLine (); + w.AppendLine ("\t\t[Sealed]"); + w.AppendLine ($"\t\t[Export (\"P{v.Managed}Value\")]"); + w.AppendLine ($"\t\t[BindAs (typeof ({v.Managed}))]"); + w.AppendLine ($"\t\tNSValue P{v.Managed}ValueNonNullable {{ get; set; }}"); + + w.AppendLine (); + w.AppendLine ($"\t\t[Export (\"get{v.Managed}ValueNonNullable\")]"); + w.AppendLine ($"\t\t[return: BindAs (typeof ({v.Managed}))]"); + w.AppendLine ($"\t\t[return: NullAllowed] // This should be the default"); + w.AppendLine ($"\t\tNSValue Get{v.Managed}ValueNonNullable ();"); + + w.AppendLine (); + w.AppendLine ($"\t\t[Export (\"set{v.Managed}ValueNonNullable:\")]"); + w.AppendLine ($"\t\tvoid Set{v.Managed}ValueNonNullable ([BindAs (typeof ({v.Managed}))] NSValue value);"); + + // nullable + + w.AppendLine (); + w.AppendLine ($"\t\t[Sealed]"); + w.AppendLine ($"\t\t[Export (\"P{v.Managed}Value\")]"); + w.AppendLine ($"\t\t[BindAs (typeof ({v.Managed}?))]"); + w.AppendLine ($"\t\t[NullAllowed] // This should be the default"); + w.AppendLine ($"\t\tNSValue P{v.Managed}ValueNullable {{ get; set; }}"); + + w.AppendLine (); + w.AppendLine ($"\t\t[Export (\"get{v.Managed}ValueNullable\")]"); + w.AppendLine ($"\t\t[return: BindAs (typeof ({v.Managed}?))]"); + w.AppendLine ($"\t\tNSValue Get{v.Managed}ValueNullable ();"); + + w.AppendLine (); + w.AppendLine ($"\t\t[Export (\"set{v.Managed}ValueNullable:\")]"); + w.AppendLine ($"\t\tvoid Set{v.Managed}ValueNullable ([BindAs (typeof ({v.Managed}?))] [NullAllowed /* this should be the default */] NSValue value);"); + + // ref/out plain value is not supported (error BI1048: bgen: Unsupported type CATransform3D decorated with [BindAs]) + // ref/out nullable value is not supported (error BI1048: bgen: Unsupported type CATransform3D decorated with [BindAs]) + + // array of plain value + w.AppendLine (); + w.AppendLine ("\t\t[Sealed]"); + w.AppendLine ($"\t\t[Export (\"P{v.Managed}Array\")]"); + w.AppendLine ($"\t\t[BindAs (typeof ({v.Managed}[]))]"); + w.AppendLine ($"\t\t[NullAllowed]"); + w.AppendLine ($"\t\tNSValue[] P{v.Managed}Array {{ get; set; }}"); + + w.AppendLine (); + w.AppendLine ($"\t\t[Export (\"get{v.Managed}Array\")]"); + w.AppendLine ($"\t\t[return: BindAs (typeof ({v.Managed}[]))]"); + w.AppendLine ($"\t\t[return: NullAllowed]"); + w.AppendLine ($"\t\tNSValue[] Get{v.Managed}Array ();"); + + w.AppendLine (); + w.AppendLine ($"\t\t[Export (\"set{v.Managed}Array:\")]"); + w.AppendLine ($"\t\tvoid Set{v.Managed}Array ([NullAllowed] [BindAs (typeof ({v.Managed}[]))] NSValue[] value);"); + + // multidimensional array of plain value + // https://bugzilla.xamarin.com/show_bug.cgi?id=57795 + //w.AppendLine (); + //w.AppendLine ("\t\t[Sealed]"); + //w.AppendLine ($"\t\t[Export (\"P{v.Managed}Multi1Array\")]"); + //w.AppendLine ($"\t\t[BindAs (typeof ({v.Managed}[,]))]"); + //w.AppendLine ($"\t\tNSValue[,] P{v.Managed}Multi1ArrayValue {{ get; set; }}"); + + // BI1048: bgen: Unsupported type CATransform3D[][] decorated with [BindAs] + //w.AppendLine (); + //w.AppendLine ("\t\t[Sealed]"); + //w.AppendLine ($"\t\t[Export (\"P{v.Managed}Multi2Array\")]"); + //w.AppendLine ($"\t\t[BindAs (typeof ({v.Managed}[][]))]"); + //w.AppendLine ($"\t\tNSValue[][] P{v.Managed}Multi2ArrayValue {{ get; set; }}"); + + // array of nullable value + // https://bugzilla.xamarin.com/show_bug.cgi?id=57797 + //w.AppendLine (); + //w.AppendLine ("\t\t[Sealed]"); + //w.AppendLine ($"\t\t[Export (\"P{v.Managed}NullableArray\")]"); + //w.AppendLine ($"\t\t[BindAs (typeof ({v.Managed}?[]))]"); + //w.AppendLine ($"\t\tNSValue[] P{v.Managed}NullableArrayValue {{ get; set; }}"); + + //w.AppendLine (); + //w.AppendLine ($"\t\t[Export (\"get{v.Managed}NullableArray\")]"); + //w.AppendLine ($"\t\t[return: BindAs (typeof ({v.Managed}?[]))]"); + //w.AppendLine ($"\t\tNSValue[] Get{v.Managed}NullableArrayValue ();"); + + //w.AppendLine (); + //w.AppendLine ($"\t\t[Export (\"set{v.Managed}NullableArray:\")]"); + //w.AppendLine ($"\t\tvoid Set{v.Managed}NullableArray ([BindAs (typeof ({v.Managed}?[]))] NSValue[] value);"); + + // multidimensional array of nullable value + // https://bugzilla.xamarin.com/show_bug.cgi?id=57797 + //w.AppendLine (); + //w.AppendLine ("\t\t[Sealed]"); + //w.AppendLine ($"\t\t[Export (\"P{v.Managed}NullableMulti1Array\")]"); + //w.AppendLine ($"\t\t[BindAs (typeof ({v.Managed}?[,]))]"); + //w.AppendLine ($"\t\tNSValue[,] P{v.Managed}NullableMulti1 {{ get; set; }}"); + + // BI1048: bgen: Unsupported type Nullable`1[][] decorated with [BindAs] + //w.AppendLine (); + //w.AppendLine ("\t\t[Sealed]"); + //w.AppendLine ($"\t\t[Export (\"P{v.Managed}NullableMulti2Array\")]"); + //w.AppendLine ($"\t\t[BindAs (typeof ({v.Managed}?[][]))]"); + //w.AppendLine ($"\t\tNSValue[][] P{v.Managed}NullableMulti2ArrayValue {{ get; set; }}"); + + + if (v.ManagedCondition != null) + w.AppendLine ("#endif"); + w.AppendLine (); + } + + w.AppendLine ("\t\t// BindAs: NSString"); + foreach (var v in bindas_nsstring) { + if (v.ManagedCondition != null) + w.AppendLine ($"#if {v.ManagedCondition}"); + // plain value + w.AppendLine ($"\t\t[return: BindAs (typeof ({v.Managed}))]"); + w.AppendLine ($"\t\t[Export (\"getSmart{v.Managed}Value\")]"); + w.AppendLine ($"\t\tNSString GetSmart{v.Managed}Value ();"); + + w.AppendLine (); + w.AppendLine ($"\t\t[Export (\"setSmart{v.Managed}Value:\")]"); + w.AppendLine ($"\t\tvoid SetSmart{v.Managed}Value ([BindAs (typeof ({v.Managed}))] NSString value);"); + + w.AppendLine (); + w.AppendLine ($"\t\t[BindAs (typeof ({v.Managed}))]"); + w.AppendLine ($"\t\t[Export (\"PSmart{v.Managed}Property\")]"); + w.AppendLine ($"\t\tNSString PSmart{v.Managed}Property {{ get; set; }}"); + + // nullable + w.AppendLine (); + w.AppendLine ($"\t\t[return: NullAllowed]"); + w.AppendLine ($"\t\t[return: BindAs (typeof ({v.Managed}?))]"); + w.AppendLine ($"\t\t[Export (\"getSmartNullable{v.Managed}Value\")]"); + w.AppendLine ($"\t\tNSString GetSmartNullable{v.Managed}Value ();"); + + w.AppendLine (); + w.AppendLine ($"\t\t[Export (\"setSmartNullable{v.Managed}Value:\")]"); + w.AppendLine ($"\t\tvoid SetSmartNullable{v.Managed}Value ([NullAllowed] [BindAs (typeof ({v.Managed}?))] NSString value);"); + + w.AppendLine (); + w.AppendLine ($"\t\t[NullAllowed]"); + w.AppendLine ($"\t\t[BindAs (typeof ({v.Managed}?))]"); + w.AppendLine ($"\t\t[Export (\"smartNullable{v.Managed}Property\")]"); + w.AppendLine ($"\t\tNSString SmartNullable{v.Managed}Property {{ get; set; }}"); + + // array of plain value + w.AppendLine (); + w.AppendLine ($"\t\t[return: NullAllowed]"); + w.AppendLine ($"\t\t[return: BindAs (typeof ({v.Managed}[]))]"); + w.AppendLine ($"\t\t[Export (\"getSmart{v.Managed}Values\")]"); + w.AppendLine ($"\t\tNSString[] GetSmart{v.Managed}Values ();"); + + w.AppendLine (); + w.AppendLine ($"\t\t[Export (\"setSmart{v.Managed}Values:\")]"); + w.AppendLine ($"\t\tvoid SetSmart{v.Managed}Values ([NullAllowed] [BindAs (typeof ({v.Managed}[]))] NSString[] value);"); + + w.AppendLine (); + w.AppendLine ($"\t\t[NullAllowed]"); + w.AppendLine ($"\t\t[BindAs (typeof ({v.Managed}[]))]"); + w.AppendLine ($"\t\t[Export (\"PSmart{v.Managed}Properties\")]"); + w.AppendLine ($"\t\tNSString[] PSmart{v.Managed}Properties {{ get; set; }}"); + + // array of nullable values + // https://bugzilla.xamarin.com/show_bug.cgi?id=57797 + //w.AppendLine (); + //w.AppendLine ($"\t\t[return: NullAllowed]"); + //w.AppendLine ($"\t\t[return: BindAs (typeof ({v.Managed}?[]))]"); + //w.AppendLine ($"\t\t[Export (\"getSmartNullable{v.Managed}Values\")]"); + //w.AppendLine ($"\t\tNSString[] GetSmartNullable{v.Managed}Values ();"); + + //w.AppendLine (); + //w.AppendLine ($"\t\t[Export (\"setSmartNullable{v.Managed}Values\")]"); + //w.AppendLine ($"\t\tvoid SetSmartNullable{v.Managed}Values ([NullAllowed] [BindAs (typeof ({v.Managed}?[]))] NSString[] value);"); + + //w.AppendLine (); + //w.AppendLine ($"\t\t[NullAllowed]"); + //w.AppendLine ($"\t\t[BindAs (typeof ({v.Managed}?[]))]"); + //w.AppendLine ($"\t\t[Export (\"smartNullable{v.Managed}Properties:\")]"); + //w.AppendLine ($"\t\tNSString[] SmartNullable{v.Managed}Properties {{ get; set; }}"); + + // multidimensional array of plain value + // https://bugzilla.xamarin.com/show_bug.cgi?id=57795 + //w.AppendLine (); + //w.AppendLine ($"\t\t[return: BindAs (typeof ({v.Managed}[,]))]"); + //w.AppendLine ($"\t\t[Export (\"getSmart{v.Managed}ValuesMulti\")]"); + //w.AppendLine ($"\t\tNSString[,] GetSmart{v.Managed}ValuesMulti ();"); + + //w.AppendLine (); + //w.AppendLine ($"\t\t[Export (\"setSmart{v.Managed}ValuesMulti:\")]"); + //w.AppendLine ($"\t\tvoid SetSmart{v.Managed}ValuesMulti ([BindAs (typeof ({v.Managed}[]))] NSString[,] value);"); + + //w.AppendLine (); + //w.AppendLine ($"\t\t[BindAs (typeof ({v.Managed}[,]))]"); + //w.AppendLine ($"\t\t[Export (\"PSmart{v.Managed}PropertiesMulti:\")]"); + //w.AppendLine ($"\t\tNSString[,] PSmart{v.Managed}PropertiesMulti {{ get; set; }}"); + + if (v.ManagedCondition != null) + w.AppendLine ("#endif"); + w.AppendLine (); + } + + + // multi-dimensional array of plain value + w.AppendLine (@" } }"); @@ -224,10 +760,28 @@ static void WriteRegistrarTests () { var w = new StringBuilder (); + WriteFrameworkDefines (w); w.AppendLine (@" +using System; #if XAMCORE_2_0 +using AVFoundation; +#if HAVE_COREANIMATION +using CoreAnimation; +#endif +using CoreGraphics; +using CoreLocation; +#if HAVE_COREMEDIA +using CoreMedia; +#endif using Foundation; +#if HAVE_MAPKIT +using MapKit; +#endif using ObjCRuntime; +using SceneKit; +#if HAVE_UIKIT +using UIKit; +#endif using MonoTouchException=ObjCRuntime.RuntimeException; using NativeException=Foundation.MonoTouchException; #else @@ -270,6 +824,572 @@ public class RegistrarTestGenerated {"); w.AppendLine (); } + w.AppendLine ("\t\tinternal class BindAsTestClassGenerated : ObjCRegistrarTest {"); + + w.AppendLine ("\t\t\t// BindAs: NSNumber"); + foreach (var v in bindas_nsnumber) { + if (v.ManagedCondition != null) + w.AppendLine ($"#if {v.ManagedCondition}"); + + w.AppendLine ($"\t\t\t{v.Managed}? _{v.Managed};"); + w.AppendLine ($"\t\t\tpublic {v.Managed}? {v.Managed}Number {{ get {{ return _{v.Managed}; }} set {{ _{v.Managed} = value; }} }}"); + w.AppendLine ($"\t\t\tpublic override {v.Managed}? Get{v.Managed}NumberNullable () {{ return _{v.Managed}; }}"); + w.AppendLine ($"\t\t\tpublic override {v.Managed} Get{v.Managed}NumberNonNullable () {{ return _{v.Managed}.Value; }}"); + w.AppendLine ($"\t\t\tpublic override void Set{v.Managed}NumberNullable ({v.Managed}? value) {{ _{v.Managed} = value; }}"); + w.AppendLine ($"\t\t\tpublic override void Set{v.Managed}NumberNonNullable ({v.Managed} value) {{ _{v.Managed} = value; }}"); + + w.AppendLine (); + w.AppendLine ($"\t\t\t{v.Managed}[] _{v.Managed}Array;"); + w.AppendLine ($"\t\t\tpublic {v.Managed}[] {v.Managed}Array {{ get {{ return _{v.Managed}Array; }} set {{ _{v.Managed}Array = value; }} }}"); + w.AppendLine ($"\t\t\tpublic override {v.Managed}[] Get{v.Managed}Array () {{ return _{v.Managed}Array; }}"); + w.AppendLine ($"\t\t\tpublic override void Set{v.Managed}Array ({v.Managed}[] value) {{ _{v.Managed}Array = value; }}"); + + if (v.ManagedCondition != null) + w.AppendLine ("#endif"); + w.AppendLine (); + } + + w.AppendLine ("\t\t\t// BindAs: NSValue"); + foreach (var v in bindas_nsvalue) { + if (v.ManagedCondition != null) + w.AppendLine ($"#if {v.ManagedCondition}"); + + w.AppendLine ($"\t\t\t{v.Managed}? _{v.Managed};"); + w.AppendLine ($"\t\t\tpublic {v.Managed}? {v.Managed}Value {{ get {{ return _{v.Managed}; }} set {{ _{v.Managed} = value; }} }}"); + w.AppendLine ($"\t\t\tpublic override {v.Managed}? Get{v.Managed}ValueNullable () {{ return _{v.Managed}; }}"); + w.AppendLine ($"\t\t\tpublic override {v.Managed} Get{v.Managed}ValueNonNullable () {{ return _{v.Managed}.Value; }}"); + w.AppendLine ($"\t\t\tpublic override void Set{v.Managed}ValueNullable ({v.Managed}? value) {{ _{v.Managed} = value; }}"); + w.AppendLine ($"\t\t\tpublic override void Set{v.Managed}ValueNonNullable ({v.Managed} value) {{ _{v.Managed} = value; }}"); + + w.AppendLine (); + w.AppendLine ($"\t\t\t{v.Managed}[] _{v.Managed}Array;"); + w.AppendLine ($"\t\t\tpublic {v.Managed}[] {v.Managed}Array {{ get {{ return _{v.Managed}Array; }} set {{ _{v.Managed}Array = value; }} }}"); + w.AppendLine ($"\t\t\tpublic override {v.Managed}[] Get{v.Managed}Array () {{ return _{v.Managed}Array; }}"); + w.AppendLine ($"\t\t\tpublic override void Set{v.Managed}Array ({v.Managed}[] value) {{ _{v.Managed}Array = value; }}"); + + if (v.ManagedCondition != null) + w.AppendLine ("#endif"); + w.AppendLine (); + } + + w.AppendLine ("\t\t\t// BindAs: NSString"); + foreach (var v in bindas_nsstring) { + if (v.ManagedCondition != null) + w.AppendLine ($"#if {v.ManagedCondition}"); + + w.AppendLine ($"\t\t\t{v.Managed}? _{v.Managed};"); + w.AppendLine ($"\t\t\tpublic {v.Managed}? {v.Managed}Value {{ get {{ return _{v.Managed}; }} set {{ _{v.Managed} = value; }} }}"); + w.AppendLine ($"\t\t\tpublic override {v.Managed} PSmart{v.Managed}Property {{ get {{ return {v.Managed}Value.Value; }} set {{ {v.Managed}Value = value; }} }}"); + w.AppendLine ($"\t\t\tpublic override {v.Managed} GetSmart{v.Managed}Value () {{ return _{v.Managed}.Value; }}"); + w.AppendLine ($"\t\t\tpublic override {v.Managed}? GetSmartNullable{v.Managed}Value () {{ return _{v.Managed}; }}"); + w.AppendLine ($"\t\t\tpublic override void SetSmart{v.Managed}Value ({v.Managed} value) {{ _{v.Managed} = value; }}"); + w.AppendLine ($"\t\t\tpublic override void SetSmartNullable{v.Managed}Value ({v.Managed}? value) {{ _{v.Managed} = value; }}"); + + w.AppendLine (); + w.AppendLine ($"\t\t\t{v.Managed}[] _{v.Managed}Array;"); + w.AppendLine ($"\t\t\tpublic {v.Managed}[] {v.Managed}Array {{ get {{ return _{v.Managed}Array; }} set {{ _{v.Managed}Array = value; }} }}"); + w.AppendLine ($"\t\t\tpublic override {v.Managed}[] PSmart{v.Managed}Properties {{ get {{ return _{v.Managed}Array; }} set {{ _{v.Managed}Array = value; }} }}"); + w.AppendLine ($"\t\t\tpublic override {v.Managed}[] GetSmart{v.Managed}Values () {{ return _{v.Managed}Array; }}"); + w.AppendLine ($"\t\t\tpublic override void SetSmart{v.Managed}Values ({v.Managed}[] value) {{ _{v.Managed}Array = value; }}"); + + if (v.ManagedCondition != null) + w.AppendLine ("#endif"); + w.AppendLine (); + } + w.AppendLine ("\t\t}"); + + foreach (var v in bindas_nsnumber) { + if (v.ManagedCondition != null) + w.AppendLine ($"#if {v.ManagedCondition}"); + + // Bindings + w.AppendLine ("\t\t[Test]"); + w.AppendLine ($"\t\tpublic void NSNumberBindAs_{v.Managed}_Bindings ()"); + w.AppendLine ("\t\t{"); + w.AppendLine ($"\t\t\tusing (var obj = new ObjCRegistrarTest ()) {{"); + w.AppendLine ($"\t\t\t\tAssert.IsNull (obj.P{v.Managed}Number, \"initial null property\");"); + w.AppendLine ($"\t\t\t\tAssert.IsNull (obj.P{v.Managed}NumberNullable, \"initial nullable null property\");"); + w.AppendLine ($"\t\t\t\tAssert.IsNull (obj.Get{v.Managed}NumberNullable (), \"initial null method\");"); + w.AppendLine (); + + w.AppendLine ($"\t\t\t\t{v.Managed}? value = default ({v.Managed});"); + w.AppendLine ($"\t\t\t\tobj.Set{v.Managed}NumberNonNullable (value.Value);"); + w.AppendLine ($"\t\t\t\tAssert.AreEqual (value, obj.P{v.Managed}NumberNullable, \"nullable property after setting default value\");"); + w.AppendLine ($"\t\t\t\tAssert.AreEqual (value.Value, obj.P{v.Managed}NumberNonNullable, \"non-nullable property after setting default value\");"); + w.AppendLine ($"\t\t\t\tAssert.AreEqual (value, obj.Get{v.Managed}NumberNullable (), \"nullable get method after setting default value\");"); + w.AppendLine ($"\t\t\t\tAssert.AreEqual (value.Value, obj.Get{v.Managed}NumberNonNullable (), \"non-nullable get method after setting default value\");"); + w.AppendLine (); + + w.AppendLine ($"\t\t\t\tvalue = {v.ManagedNewExpression};"); + w.AppendLine ($"\t\t\t\tobj.Set{v.Managed}NumberNonNullable (value.Value);"); + w.AppendLine ($"\t\t\t\tAssert.AreEqual (value, obj.P{v.Managed}NumberNullable, \"nullable property after setting custom value\");"); + w.AppendLine ($"\t\t\t\tAssert.AreEqual (value.Value, obj.P{v.Managed}NumberNonNullable, \"non-nullable property after setting custom value\");"); + w.AppendLine ($"\t\t\t\tAssert.AreEqual (value, obj.Get{v.Managed}NumberNullable (), \"nullable get method after setting custom value\");"); + w.AppendLine ($"\t\t\t\tAssert.AreEqual (value.Value, obj.Get{v.Managed}NumberNonNullable (), \"non-nullable get method after setting custom value\");"); + w.AppendLine (); + + w.AppendLine ($"\t\t\t\tvalue = null;"); + w.AppendLine ($"\t\t\t\tobj.Set{v.Managed}NumberNullable (value);"); + w.AppendLine ($"\t\t\t\tAssert.IsNull (obj.P{v.Managed}Number, \"null property after setting null value\");"); + w.AppendLine ($"\t\t\t\tAssert.IsNull (obj.P{v.Managed}NumberNullable, \"nullable null property after setting null value\");"); + w.AppendLine ($"\t\t\t\tAssert.IsNull (obj.Get{v.Managed}NumberNullable (), \"null method after setting null value\");"); + w.AppendLine ($"\t\t\t}}"); + w.AppendLine ("\t\t}"); + w.AppendLine (); + + // Overrides + w.AppendLine ("\t\t[Test]"); + w.AppendLine ($"\t\tpublic void NSNumberBindAs_{v.Managed}_Overrides ()"); + w.AppendLine ("\t\t{"); + w.AppendLine ($"\t\t\tusing (var obj = new BindAsTestClassGenerated ()) {{"); + w.AppendLine ($"\t\t\t\tAssert.IsNull (obj.{v.Managed}Number, \"initial null\");"); + w.AppendLine ($"\t\t\t\tMessaging.void_objc_msgSend_IntPtr (obj.Handle, Selector.GetHandle (\"set{v.Managed}NumberNullable:\"), IntPtr.Zero);"); + w.AppendLine ($"\t\t\t\tAssert.IsNull (obj.{v.Managed}Number, \"null after setting null\");"); + w.AppendLine (); + + w.AppendLine ($"\t\t\t\tobj.{v.Managed}Number = {v.ManagedNewExpression};"); + w.AppendLine ($"\t\t\t\tMessaging.void_objc_msgSend_IntPtr (obj.Handle, Selector.GetHandle (\"set{v.Managed}NumberNullable:\"), IntPtr.Zero);"); + w.AppendLine ($"\t\t\t\tAssert.IsNull (obj.{v.Managed}Number, \"null after re-setting null\");"); + w.AppendLine (); + + w.AppendLine ($"\t\t\t\tvar value = {v.ManagedNewExpression};"); + w.AppendLine ($"\t\t\t\tusing (var input = new NSNumber (value))"); + w.AppendLine ($"\t\t\t\t\tMessaging.void_objc_msgSend_IntPtr (obj.Handle, Selector.GetHandle (\"set{v.Managed}NumberNullable:\"), input.Handle);"); + w.AppendLine ($"\t\t\t\tAssert.AreEqual (value, obj.{v.Managed}Number, \"after setting A\");"); + w.AppendLine (); + + w.AppendLine ($"\t\t\t\tobj.{v.Managed}Number = null;"); + w.AppendLine ($"\t\t\t\tusing (var input = new NSNumber (value))"); + w.AppendLine ($"\t\t\t\t\tMessaging.void_objc_msgSend_IntPtr (obj.Handle, Selector.GetHandle (\"set{v.Managed}NumberNonNullable:\"), input.Handle);"); + w.AppendLine ($"\t\t\t\tAssert.AreEqual (value, obj.{v.Managed}Number.Value, \"after setting B\");"); + w.AppendLine (); + + w.AppendLine ($"\t\t\t\tobj.{v.Managed}Number = null;"); + w.AppendLine ($"\t\t\t\tvar number = Runtime.GetNSObject (Messaging.IntPtr_objc_msgSend (obj.Handle, Selector.GetHandle (\"get{v.Managed}NumberNullable\")));"); + w.AppendLine ($"\t\t\t\tAssert.IsNull (number, \"null from getter A\");"); + w.AppendLine (); + + w.AppendLine ($"\t\t\t\tvalue = {v.ManagedNewExpression};"); + w.AppendLine ($"\t\t\t\tobj.{v.Managed}Number = value;"); + w.AppendLine ($"\t\t\t\tnumber = Runtime.GetNSObject (Messaging.IntPtr_objc_msgSend (obj.Handle, Selector.GetHandle (\"get{v.Managed}NumberNullable\")));"); + w.AppendLine ($"\t\t\t\tAssert.AreEqual (value, number{v.Map}, \"getter B\");"); + w.AppendLine (); + + w.AppendLine ($"\t\t\t\tvalue = {v.ManagedNewExpression};"); + w.AppendLine ($"\t\t\t\tobj.{v.Managed}Number = value;"); + w.AppendLine ($"\t\t\t\tnumber = Runtime.GetNSObject (Messaging.IntPtr_objc_msgSend (obj.Handle, Selector.GetHandle (\"get{v.Managed}NumberNonNullable\")));"); + w.AppendLine ($"\t\t\t\tAssert.AreEqual (value, number{v.Map}, \"getter C\");"); + w.AppendLine ($"\t\t\t}}"); + w.AppendLine ("\t\t}"); + + // Array_Bindings + w.AppendLine ("\t\t[Test]"); + w.AppendLine ($"\t\tpublic void NSNumberBindAs_{v.Managed}Array_Bindings ()"); + w.AppendLine ("\t\t{"); + w.AppendLine ($"\t\t\tusing (var obj = new ObjCRegistrarTest ()) {{"); + w.AppendLine ($"\t\t\t\tAssert.IsNull (obj.P{v.Managed}Array, \"initial null property\");"); + w.AppendLine ($"\t\t\t\tAssert.IsNull (obj.Get{v.Managed}Array (), \"initial null method\");"); + w.AppendLine (); + + w.AppendLine ($"\t\t\t\t{v.Managed}[] value = null;"); + w.AppendLine ($"\t\t\t\tobj.Set{v.Managed}Array (value);"); + w.AppendLine ($"\t\t\t\tAssert.AreEqual (value, obj.P{v.Managed}Array, \"nullable property after setting default value\");"); + w.AppendLine ($"\t\t\t\tAssert.AreEqual (value, obj.Get{v.Managed}Array (), \"nullable get method after setting default value\");"); + w.AppendLine (); + + w.AppendLine ($"\t\t\t\tvalue = new {v.Managed} [] {{ {v.ManagedNewExpression} }};"); + w.AppendLine ($"\t\t\t\tobj.Set{v.Managed}Array (value);"); + w.AppendLine ($"\t\t\t\tAssert.AreEqual (1, obj.P{v.Managed}Array.Length, \"nullable property after setting custom value\");"); + w.AppendLine ($"\t\t\t\tAssert.AreEqual (1, obj.Get{v.Managed}Array ().Length, \"nullable get method after setting custom value\");"); + w.AppendLine ($"\t\t\t\tAssert.AreEqual (value [0], obj.P{v.Managed}Array [0], \"nullable property after setting custom value element\");"); + w.AppendLine ($"\t\t\t\tAssert.AreEqual (value [0], obj.Get{v.Managed}Array () [0], \"nullable get method after setting custom value element\");"); + w.AppendLine (); + + w.AppendLine ($"\t\t\t\tvalue = null;"); + w.AppendLine ($"\t\t\t\tobj.Set{v.Managed}Array (value);"); + w.AppendLine ($"\t\t\t\tAssert.IsNull (obj.P{v.Managed}Array, \"null property after setting null value\");"); + w.AppendLine ($"\t\t\t\tAssert.IsNull (obj.Get{v.Managed}Array (), \"null method after setting null value\");"); + w.AppendLine ($"\t\t\t}}"); + w.AppendLine ("\t\t}"); + w.AppendLine (); + + // Array_Overrides + w.AppendLine ("\t\t[Test]"); + w.AppendLine ($"\t\tpublic void NSNumberBindAs_{v.Managed}_Array_Overrides ()"); + w.AppendLine ("\t\t{"); + w.AppendLine ($"\t\t\tusing (var obj = new BindAsTestClassGenerated ()) {{"); + w.AppendLine ($"\t\t\t\tAssert.IsNull (obj.{v.Managed}Array, \"initial null\");"); + w.AppendLine ($"\t\t\t\tMessaging.void_objc_msgSend_IntPtr (obj.Handle, Selector.GetHandle (\"set{v.Managed}Array:\"), IntPtr.Zero);"); + w.AppendLine ($"\t\t\t\tAssert.IsNull (obj.{v.Managed}Array, \"null after setting null\");"); + w.AppendLine (); + + w.AppendLine ($"\t\t\t\tobj.{v.Managed}Array = new {v.Managed} [] {{ {v.ManagedNewExpression} }};"); + w.AppendLine ($"\t\t\t\tMessaging.void_objc_msgSend_IntPtr (obj.Handle, Selector.GetHandle (\"set{v.Managed}Array:\"), IntPtr.Zero);"); + w.AppendLine ($"\t\t\t\tAssert.IsNull (obj.{v.Managed}Array, \"null after re-setting null\");"); + w.AppendLine (); + + w.AppendLine ($"\t\t\t\tvar value = new {v.Managed} [] {{ {v.ManagedNewExpression} }};"); + w.AppendLine ($"\t\t\t\tusing (var input = NSArray.FromNSObjects<{v.Managed}> ((v) => new NSNumber (v), value))"); + w.AppendLine ($"\t\t\t\t\tMessaging.void_objc_msgSend_IntPtr (obj.Handle, Selector.GetHandle (\"set{v.Managed}Array:\"), input.Handle);"); + w.AppendLine ($"\t\t\t\tAssert.AreEqual (value.Length, obj.{v.Managed}Array.Length, \"after setting A\");"); + w.AppendLine ($"\t\t\t\tAssert.AreEqual (value [0], obj.{v.Managed}Array [0], \"after setting A element\");"); + w.AppendLine (); + + w.AppendLine ($"\t\t\t\tobj.{v.Managed}Array = null;"); + w.AppendLine ($"\t\t\t\tusing (var input = NSArray.FromNSObjects<{v.Managed}> ((v) => new NSNumber (v), value))"); + w.AppendLine ($"\t\t\t\t\tMessaging.void_objc_msgSend_IntPtr (obj.Handle, Selector.GetHandle (\"set{v.Managed}Array:\"), input.Handle);"); + w.AppendLine ($"\t\t\t\tAssert.AreEqual (value.Length, obj.{v.Managed}Array.Length, \"after setting B\");"); + w.AppendLine ($"\t\t\t\tAssert.AreEqual (value [0], obj.{v.Managed}Array [0], \"after setting B element\");"); + w.AppendLine (); + + w.AppendLine ($"\t\t\t\tobj.{v.Managed}Array = null;"); + w.AppendLine ($"\t\t\t\tvar array = Runtime.GetNSObject (Messaging.IntPtr_objc_msgSend (obj.Handle, Selector.GetHandle (\"get{v.Managed}Array\")));"); + w.AppendLine ($"\t\t\t\tAssert.IsNull (array, \"null from getter A\");"); + w.AppendLine (); + + w.AppendLine ($"\t\t\t\tvalue = new {v.Managed} [] {{ {v.ManagedNewExpression} }};"); + w.AppendLine ($"\t\t\t\tobj.{v.Managed}Array = value;"); + w.AppendLine ($"\t\t\t\tarray = Runtime.GetNSObject (Messaging.IntPtr_objc_msgSend (obj.Handle, Selector.GetHandle (\"get{v.Managed}Array\")));"); + w.AppendLine ($"\t\t\t\tAssert.AreEqual (value.Length, array.Count, \"getter B\");"); + w.AppendLine ($"\t\t\t\tAssert.AreEqual (value [0], array.GetItem (0){v.Map}, \"getter B element\");"); + w.AppendLine (); + + w.AppendLine ($"\t\t\t}}"); + w.AppendLine ("\t\t}"); + + if (v.ManagedCondition != null) + w.AppendLine ("#endif"); + + w.AppendLine (); + } + + + foreach (var v in bindas_nsvalue) { + if (v.ManagedCondition != null) + w.AppendLine ($"#if {v.ManagedCondition}"); + + // Bindings + w.AppendLine ("\t\t[Test]"); + w.AppendLine ($"\t\tpublic void NSValueBindAs_{v.Managed}_Bindings ()"); + w.AppendLine ("\t\t{"); + w.AppendLine ($"\t\t\tusing (var obj = new ObjCRegistrarTest ()) {{"); + w.AppendLine ($"\t\t\t\tAssert.IsNull (obj.P{v.Managed}Value, \"initial null property\");"); + w.AppendLine ($"\t\t\t\tAssert.IsNull (obj.P{v.Managed}ValueNullable, \"initial nullable null property\");"); + w.AppendLine ($"\t\t\t\tAssert.IsNull (obj.Get{v.Managed}ValueNullable (), \"initial null method\");"); + w.AppendLine (); + + w.AppendLine ($"\t\t\t\t{v.Managed}? value = default ({v.Managed});"); + w.AppendLine ($"\t\t\t\tobj.Set{v.Managed}ValueNonNullable (value.Value);"); + w.AppendLine ($"\t\t\t\tAssert.AreEqual (value, obj.P{v.Managed}ValueNullable, \"nullable property after setting default value\");"); + w.AppendLine ($"\t\t\t\tAssert.AreEqual (value.Value, obj.P{v.Managed}ValueNonNullable, \"non-nullable property after setting default value\");"); + w.AppendLine ($"\t\t\t\tAssert.AreEqual (value, obj.Get{v.Managed}ValueNullable (), \"nullable get method after setting default value\");"); + w.AppendLine ($"\t\t\t\tAssert.AreEqual (value.Value, obj.Get{v.Managed}ValueNonNullable (), \"non-nullable get method after setting default value\");"); + w.AppendLine (); + + w.AppendLine ($"\t\t\t\tvalue = {v.ManagedNewExpression};"); + w.AppendLine ($"\t\t\t\tobj.Set{v.Managed}ValueNonNullable (value.Value);"); + w.AppendLine ($"\t\t\t\tAssert.AreEqual (value, obj.P{v.Managed}ValueNullable, \"nullable property after setting custom value\");"); + w.AppendLine ($"\t\t\t\tAssert.AreEqual (value.Value, obj.P{v.Managed}ValueNonNullable, \"non-nullable property after setting custom value\");"); + w.AppendLine ($"\t\t\t\tAssert.AreEqual (value, obj.Get{v.Managed}ValueNullable (), \"nullable get method after setting custom value\");"); + w.AppendLine ($"\t\t\t\tAssert.AreEqual (value.Value, obj.Get{v.Managed}ValueNonNullable (), \"non-nullable get method after setting custom value\");"); + w.AppendLine (); + + w.AppendLine ($"\t\t\t\tvalue = null;"); + w.AppendLine ($"\t\t\t\tobj.Set{v.Managed}ValueNullable (value);"); + w.AppendLine ($"\t\t\t\tAssert.IsNull (obj.P{v.Managed}Value, \"null property after setting null value\");"); + w.AppendLine ($"\t\t\t\tAssert.IsNull (obj.P{v.Managed}ValueNullable, \"nullable null property after setting null value\");"); + w.AppendLine ($"\t\t\t\tAssert.IsNull (obj.Get{v.Managed}ValueNullable (), \"null method after setting null value\");"); + w.AppendLine ($"\t\t\t}}"); + w.AppendLine ("\t\t}"); + w.AppendLine (); + + // Overrides + w.AppendLine ("\t\t[Test]"); + w.AppendLine ($"\t\tpublic void NSValueBindAs_{v.Managed}_Overrides ()"); + w.AppendLine ("\t\t{"); + w.AppendLine ($"\t\t\tusing (var obj = new BindAsTestClassGenerated ()) {{"); + w.AppendLine ($"\t\t\t\tAssert.IsNull (obj.{v.Managed}Value, \"initial null\");"); + w.AppendLine ($"\t\t\t\tMessaging.void_objc_msgSend_IntPtr (obj.Handle, Selector.GetHandle (\"set{v.Managed}ValueNullable:\"), IntPtr.Zero);"); + w.AppendLine ($"\t\t\t\tAssert.IsNull (obj.{v.Managed}Value, \"null after setting null\");"); + w.AppendLine (); + + w.AppendLine ($"\t\t\t\tobj.{v.Managed}Value = {v.ManagedNewExpression};"); + w.AppendLine ($"\t\t\t\tMessaging.void_objc_msgSend_IntPtr (obj.Handle, Selector.GetHandle (\"set{v.Managed}ValueNullable:\"), IntPtr.Zero);"); + w.AppendLine ($"\t\t\t\tAssert.IsNull (obj.{v.Managed}Value, \"null after re-setting null\");"); + w.AppendLine (); + + w.AppendLine ($"\t\t\t\tvar value = {v.ManagedNewExpression};"); + w.AppendLine ($"\t\t\t\tusing (var input = NSValue.{v.MapFrom} (value))"); + w.AppendLine ($"\t\t\t\tMessaging.void_objc_msgSend_IntPtr (obj.Handle, Selector.GetHandle (\"set{v.Managed}ValueNullable:\"), input.Handle);"); + w.AppendLine ($"\t\t\t\tAssert.AreEqual (value, obj.{v.Managed}Value, \"after setting A\");"); + w.AppendLine (); + + w.AppendLine ($"\t\t\t\tobj.{v.Managed}Value = null;"); + w.AppendLine ($"\t\t\t\tusing (var input = NSValue.{v.MapFrom} (value))"); + w.AppendLine ($"\t\t\t\tMessaging.void_objc_msgSend_IntPtr (obj.Handle, Selector.GetHandle (\"set{v.Managed}ValueNonNullable:\"), input.Handle);"); + w.AppendLine ($"\t\t\t\tAssert.AreEqual (value, obj.{v.Managed}Value, \"after setting B\");"); + w.AppendLine (); + + w.AppendLine ($"\t\t\t\tobj.{v.Managed}Value = null;"); + w.AppendLine ($"\t\t\t\tvar Value = Runtime.GetNSObject (Messaging.IntPtr_objc_msgSend (obj.Handle, Selector.GetHandle (\"get{v.Managed}ValueNullable\")));"); + w.AppendLine ($"\t\t\t\tAssert.IsNull (Value, \"null from getter A\");"); + w.AppendLine (); + + w.AppendLine ($"\t\t\t\tvalue = {v.ManagedNewExpression};"); + w.AppendLine ($"\t\t\t\tobj.{v.Managed}Value = value;"); + w.AppendLine ($"\t\t\t\tValue = Runtime.GetNSObject (Messaging.IntPtr_objc_msgSend (obj.Handle, Selector.GetHandle (\"get{v.Managed}ValueNullable\")));"); + w.AppendLine ($"\t\t\t\tAssert.AreEqual (value, Value{v.Map}, \"getter B\");"); + w.AppendLine (); + + w.AppendLine ($"\t\t\t\tvalue = {v.ManagedNewExpression};"); + w.AppendLine ($"\t\t\t\tobj.{v.Managed}Value = value;"); + w.AppendLine ($"\t\t\t\tValue = Runtime.GetNSObject (Messaging.IntPtr_objc_msgSend (obj.Handle, Selector.GetHandle (\"get{v.Managed}ValueNonNullable\")));"); + w.AppendLine ($"\t\t\t\tAssert.AreEqual (value, Value{v.Map}, \"getter C\");"); + w.AppendLine ($"\t\t\t}}"); + w.AppendLine ("\t\t}"); + + // Array_Bindings + w.AppendLine ("\t\t[Test]"); + w.AppendLine ($"\t\tpublic void NSValueBindAs_{v.Managed}Array_Bindings ()"); + w.AppendLine ("\t\t{"); + w.AppendLine ($"\t\t\tusing (var obj = new ObjCRegistrarTest ()) {{"); + w.AppendLine ($"\t\t\t\tAssert.IsNull (obj.P{v.Managed}Array, \"initial null property\");"); + w.AppendLine ($"\t\t\t\tAssert.IsNull (obj.Get{v.Managed}Array (), \"initial null method\");"); + w.AppendLine (); + + w.AppendLine ($"\t\t\t\t{v.Managed}[] value = null;"); + w.AppendLine ($"\t\t\t\tobj.Set{v.Managed}Array (value);"); + w.AppendLine ($"\t\t\t\tAssert.AreEqual (value, obj.P{v.Managed}Array, \"nullable property after setting default value\");"); + w.AppendLine ($"\t\t\t\tAssert.AreEqual (value, obj.Get{v.Managed}Array (), \"nullable get method after setting default value\");"); + w.AppendLine (); + + w.AppendLine ($"\t\t\t\tvalue = new {v.Managed} [] {{ {v.ManagedNewExpression} }};"); + w.AppendLine ($"\t\t\t\tobj.Set{v.Managed}Array (value);"); + w.AppendLine ($"\t\t\t\tAssert.AreEqual (1, obj.P{v.Managed}Array.Length, \"nullable property after setting custom value\");"); + w.AppendLine ($"\t\t\t\tAssert.AreEqual (1, obj.Get{v.Managed}Array ().Length, \"nullable get method after setting custom value\");"); + w.AppendLine ($"\t\t\t\tAssert.AreEqual (value [0], obj.P{v.Managed}Array [0], \"nullable property after setting custom value element\");"); + w.AppendLine ($"\t\t\t\tAssert.AreEqual (value [0], obj.Get{v.Managed}Array () [0], \"nullable get method after setting custom value element\");"); + w.AppendLine (); + + w.AppendLine ($"\t\t\t\tvalue = null;"); + w.AppendLine ($"\t\t\t\tobj.Set{v.Managed}Array (value);"); + w.AppendLine ($"\t\t\t\tAssert.IsNull (obj.P{v.Managed}Array, \"null property after setting null value\");"); + w.AppendLine ($"\t\t\t\tAssert.IsNull (obj.Get{v.Managed}Array (), \"null method after setting null value\");"); + w.AppendLine ($"\t\t\t}}"); + w.AppendLine ("\t\t}"); + w.AppendLine (); + + // Array_Overrides + w.AppendLine ("\t\t[Test]"); + w.AppendLine ($"\t\tpublic void NSValueBindAs_{v.Managed}_Array_Overrides ()"); + w.AppendLine ("\t\t{"); + w.AppendLine ($"\t\t\tusing (var obj = new BindAsTestClassGenerated ()) {{"); + w.AppendLine ($"\t\t\t\tAssert.IsNull (obj.{v.Managed}Array, \"initial null\");"); + w.AppendLine ($"\t\t\t\tMessaging.void_objc_msgSend_IntPtr (obj.Handle, Selector.GetHandle (\"set{v.Managed}Array:\"), IntPtr.Zero);"); + w.AppendLine ($"\t\t\t\tAssert.IsNull (obj.{v.Managed}Array, \"null after setting null\");"); + w.AppendLine (); + + w.AppendLine ($"\t\t\t\tobj.{v.Managed}Array = new {v.Managed} [] {{ {v.ManagedNewExpression} }};"); + w.AppendLine ($"\t\t\t\tMessaging.void_objc_msgSend_IntPtr (obj.Handle, Selector.GetHandle (\"set{v.Managed}Array:\"), IntPtr.Zero);"); + w.AppendLine ($"\t\t\t\tAssert.IsNull (obj.{v.Managed}Array, \"null after re-setting null\");"); + w.AppendLine (); + + w.AppendLine ($"\t\t\t\tvar value = new {v.Managed} [] {{ {v.ManagedNewExpression} }};"); + w.AppendLine ($"\t\t\t\tusing (var input = NSArray.FromNSObjects<{v.Managed}> ((v) => NSValue.{v.MapFrom} (v), value))"); + w.AppendLine ($"\t\t\t\t\tMessaging.void_objc_msgSend_IntPtr (obj.Handle, Selector.GetHandle (\"set{v.Managed}Array:\"), input.Handle);"); + w.AppendLine ($"\t\t\t\tAssert.AreEqual (value.Length, obj.{v.Managed}Array.Length, \"after setting A\");"); + w.AppendLine ($"\t\t\t\tAssert.AreEqual (value [0], obj.{v.Managed}Array [0], \"after setting A element\");"); + w.AppendLine (); + + w.AppendLine ($"\t\t\t\tobj.{v.Managed}Array = null;"); + w.AppendLine ($"\t\t\t\tusing (var input = NSArray.FromNSObjects<{v.Managed}> ((v) => NSValue.{v.MapFrom} (v), value))"); + w.AppendLine ($"\t\t\t\t\tMessaging.void_objc_msgSend_IntPtr (obj.Handle, Selector.GetHandle (\"set{v.Managed}Array:\"), input.Handle);"); + w.AppendLine ($"\t\t\t\tAssert.AreEqual (value.Length, obj.{v.Managed}Array.Length, \"after setting B\");"); + w.AppendLine ($"\t\t\t\tAssert.AreEqual (value [0], obj.{v.Managed}Array [0], \"after setting B element\");"); + w.AppendLine (); + + w.AppendLine ($"\t\t\t\tobj.{v.Managed}Array = null;"); + w.AppendLine ($"\t\t\t\tvar array = Runtime.GetNSObject (Messaging.IntPtr_objc_msgSend (obj.Handle, Selector.GetHandle (\"get{v.Managed}Array\")));"); + w.AppendLine ($"\t\t\t\tAssert.IsNull (array, \"null from getter A\");"); + w.AppendLine (); + + w.AppendLine ($"\t\t\t\tvalue = new {v.Managed} [] {{ {v.ManagedNewExpression} }};"); + w.AppendLine ($"\t\t\t\tobj.{v.Managed}Array = value;"); + w.AppendLine ($"\t\t\t\tarray = Runtime.GetNSObject (Messaging.IntPtr_objc_msgSend (obj.Handle, Selector.GetHandle (\"get{v.Managed}Array\")));"); + w.AppendLine ($"\t\t\t\tAssert.AreEqual (value.Length, array.Count, \"getter B\");"); + w.AppendLine ($"\t\t\t\tAssert.AreEqual (value [0], array.GetItem (0){v.Map}, \"getter B element\");"); + w.AppendLine ($"\t\t\t}}"); + w.AppendLine ("\t\t}"); + w.AppendLine (); + + if (v.ManagedCondition != null) + w.AppendLine ("#endif"); + + w.AppendLine (); + } + + foreach (var v in bindas_nsstring) { + if (v.ManagedCondition != null) + w.AppendLine ($"#if {v.ManagedCondition}"); + + // Bindings + w.AppendLine ("\t\t[Test]"); + w.AppendLine ($"\t\tpublic void NSStringBindAs_{v.Managed}_Bindings ()"); + w.AppendLine ("\t\t{"); + w.AppendLine ($"\t\t\tusing (var obj = new ObjCRegistrarTest ()) {{"); + w.AppendLine ($"\t\t\t\tAssert.Throws (() => {{ Console.WriteLine (obj.PSmart{v.Managed}Property); }}, \"initial zero property\");"); + w.AppendLine ($"\t\t\t\tAssert.Throws (() => {{ Console.WriteLine (obj.GetSmart{v.Managed}Value ()); }}, \"initial zero method\");"); + w.AppendLine ($"\t\t\t\tAssert.IsNull (obj.GetSmartNullable{v.Managed}Value (), \"initial null method\");"); + w.AppendLine (); + + w.AppendLine ($"\t\t\t\t{v.Managed}? value = default ({v.Managed});"); + w.AppendLine ($"\t\t\t\tobj.SetSmartNullable{v.Managed}Value (value);"); + w.AppendLine ($"\t\t\t\tAssert.AreEqual (value.Value, obj.PSmart{v.Managed}Property, \"zero property after setting default value\");"); + w.AppendLine ($"\t\t\t\tAssert.AreEqual (value.Value, obj.GetSmart{v.Managed}Value (), \"non-nullable property after setting default value\");"); + w.AppendLine ($"\t\t\t\tAssert.AreEqual (value, obj.GetSmartNullable{v.Managed}Value (), \"nullable get method after setting default value\");"); + w.AppendLine (); + + w.AppendLine ($"\t\t\t\tvalue = {v.ManagedNewExpression};"); + w.AppendLine ($"\t\t\t\tobj.SetSmart{v.Managed}Value (value.Value);"); + w.AppendLine ($"\t\t\t\tAssert.AreEqual (value.Value, obj.PSmart{v.Managed}Property, \"non-nullable property after setting custom value\");"); + w.AppendLine ($"\t\t\t\tAssert.AreEqual (value, obj.GetSmartNullable{v.Managed}Value (), \"nullable get method after setting custom value\");"); + w.AppendLine ($"\t\t\t\tAssert.AreEqual (value.Value, obj.GetSmart{v.Managed}Value (), \"non-nullable get method after setting custom value\");"); + w.AppendLine (); + + w.AppendLine ($"\t\t\t\tvalue = null;"); + w.AppendLine ($"\t\t\t\tobj.SetSmartNullable{v.Managed}Value (value);"); + w.AppendLine ($"\t\t\t\tAssert.Throws (() => {{ Console.WriteLine (obj.PSmart{v.Managed}Property); }}, \"null property after setting null value\");"); + w.AppendLine ($"\t\t\t\tAssert.Throws (() => {{ Console.WriteLine (obj.GetSmart{v.Managed}Value ()); }}, \"non-nullable method after setting null value\");"); + w.AppendLine ($"\t\t\t\tAssert.IsNull (obj.GetSmartNullable{v.Managed}Value (), \"null method after setting null value\");"); + w.AppendLine ($"\t\t\t}}"); + w.AppendLine ("\t\t}"); + w.AppendLine (); + + // Overrides + w.AppendLine ("\t\t[Test]"); + w.AppendLine ($"\t\tpublic void NSStringBindAs_{v.Managed}_Overrides ()"); + w.AppendLine ("\t\t{"); + w.AppendLine ($"\t\t\tusing (var obj = new BindAsTestClassGenerated ()) {{"); + w.AppendLine ($"\t\t\t\tAssert.Throws (() => {{ Console.WriteLine (obj.PSmart{v.Managed}Property); }}, \"initial null\");"); + w.AppendLine ($"\t\t\t\tMessaging.void_objc_msgSend_IntPtr (obj.Handle, Selector.GetHandle (\"setSmartNullable{v.Managed}Value:\"), IntPtr.Zero);"); + w.AppendLine ($"\t\t\t\tAssert.Throws (() => {{ Console.WriteLine (obj.PSmart{v.Managed}Property); }}, \"null after setting null\");"); + w.AppendLine (); + + w.AppendLine ($"\t\t\t\tobj.PSmart{v.Managed}Property = {v.ManagedNewExpression};"); + w.AppendLine ($"\t\t\t\tMessaging.void_objc_msgSend_IntPtr (obj.Handle, Selector.GetHandle (\"setSmartNullable{v.Managed}Value:\"), IntPtr.Zero);"); + w.AppendLine ($"\t\t\t\tAssert.Throws (() => {{ Console.WriteLine (obj.PSmart{v.Managed}Property); }}, \"null after re-setting null\");"); + w.AppendLine (); + + w.AppendLine ($"\t\t\t\tvar value = {v.ManagedNewExpression};"); + w.AppendLine ($"\t\t\t\tusing (var input = value.GetConstant ())"); + w.AppendLine ($"\t\t\t\t\tMessaging.void_objc_msgSend_IntPtr (obj.Handle, Selector.GetHandle (\"setSmartNullable{v.Managed}Value:\"), input.Handle);"); + w.AppendLine ($"\t\t\t\tAssert.AreEqual (value, obj.PSmart{v.Managed}Property, \"after setting A\");"); + w.AppendLine (); + + w.AppendLine ($"\t\t\t\tobj.PSmart{v.Managed}Property = 0;"); + w.AppendLine ($"\t\t\t\tusing (var input = value.GetConstant ())"); + w.AppendLine ($"\t\t\t\t\tMessaging.void_objc_msgSend_IntPtr (obj.Handle, Selector.GetHandle (\"setSmart{v.Managed}Value:\"), input.Handle);"); + w.AppendLine ($"\t\t\t\tAssert.AreEqual (value, obj.PSmart{v.Managed}Property, \"after setting B\");"); + w.AppendLine (); + + w.AppendLine ($"\t\t\t\tobj.PSmart{v.Managed}Property = 0;"); + w.AppendLine ($"\t\t\t\tvar Value = Runtime.GetNSObject (Messaging.IntPtr_objc_msgSend (obj.Handle, Selector.GetHandle (\"getSmartNullable{v.Managed}Value\")));"); + w.AppendLine ($"\t\t\t\tAssert.AreEqual (default ({v.Managed}).GetConstant ().ToString (), Value.ToString (), \"zero from getter A\");"); + w.AppendLine (); + + w.AppendLine ($"\t\t\t\tvalue = {v.ManagedNewExpression};"); + w.AppendLine ($"\t\t\t\tobj.PSmart{v.Managed}Property = value;"); + w.AppendLine ($"\t\t\t\tValue = Runtime.GetNSObject (Messaging.IntPtr_objc_msgSend (obj.Handle, Selector.GetHandle (\"getSmartNullable{v.Managed}Value\")));"); + w.AppendLine ($"\t\t\t\tAssert.AreEqual (value, {v.Managed}Extensions.GetValue (Value), \"getter B\");"); + w.AppendLine (); + + w.AppendLine ($"\t\t\t\tvalue = {v.ManagedNewExpression};"); + w.AppendLine ($"\t\t\t\tobj.PSmart{v.Managed}Property = value;"); + w.AppendLine ($"\t\t\t\tValue = Runtime.GetNSObject (Messaging.IntPtr_objc_msgSend (obj.Handle, Selector.GetHandle (\"getSmart{v.Managed}Value\")));"); + w.AppendLine ($"\t\t\t\tAssert.AreEqual (value, {v.Managed}Extensions.GetValue (Value), \"getter C\");"); + w.AppendLine ($"\t\t\t}}"); + w.AppendLine ("\t\t}"); + + // Array_Bindings + w.AppendLine ("\t\t[Test]"); + w.AppendLine ($"\t\tpublic void NSStringBindAs_{v.Managed}Array_Bindings ()"); + w.AppendLine ("\t\t{"); + w.AppendLine ($"\t\t\tusing (var obj = new ObjCRegistrarTest ()) {{"); + w.AppendLine ($"\t\t\t\tAssert.IsNull (obj.PSmart{v.Managed}Properties, \"initial null property\");"); + w.AppendLine ($"\t\t\t\tAssert.IsNull (obj.GetSmart{v.Managed}Values (), \"initial null method\");"); + w.AppendLine (); + + w.AppendLine ($"\t\t\t\t{v.Managed}[] value = null;"); + w.AppendLine ($"\t\t\t\tobj.SetSmart{v.Managed}Values (value);"); + w.AppendLine ($"\t\t\t\tAssert.AreEqual (value, obj.PSmart{v.Managed}Properties, \"nullable property after setting default value\");"); + w.AppendLine ($"\t\t\t\tAssert.AreEqual (value, obj.GetSmart{v.Managed}Values (), \"nullable get method after setting default value\");"); + w.AppendLine (); + + w.AppendLine ($"\t\t\t\tvalue = new {v.Managed} [] {{ {v.ManagedNewExpression} }};"); + w.AppendLine ($"\t\t\t\tobj.SetSmart{v.Managed}Values (value);"); + w.AppendLine ($"\t\t\t\tAssert.AreEqual (1, obj.PSmart{v.Managed}Properties.Length, \"nullable property after setting custom value\");"); + w.AppendLine ($"\t\t\t\tAssert.AreEqual (1, obj.GetSmart{v.Managed}Values ().Length, \"nullable get method after setting custom value\");"); + w.AppendLine ($"\t\t\t\tAssert.AreEqual (value [0], obj.PSmart{v.Managed}Properties [0], \"nullable property after setting custom value element\");"); + w.AppendLine ($"\t\t\t\tAssert.AreEqual (value [0], obj.GetSmart{v.Managed}Values () [0], \"nullable get method after setting custom value element\");"); + w.AppendLine (); + + w.AppendLine ($"\t\t\t\tvalue = null;"); + w.AppendLine ($"\t\t\t\tobj.SetSmart{v.Managed}Values (value);"); + w.AppendLine ($"\t\t\t\tAssert.IsNull (obj.PSmart{v.Managed}Properties, \"null property after setting null value\");"); + w.AppendLine ($"\t\t\t\tAssert.IsNull (obj.GetSmart{v.Managed}Values (), \"null method after setting null value\");"); + w.AppendLine ($"\t\t\t}}"); + w.AppendLine ("\t\t}"); + w.AppendLine (); + + // Array_Overrides + w.AppendLine ("\t\t[Test]"); + w.AppendLine ($"\t\tpublic void NSStringBindAs_{v.Managed}_Array_Overrides ()"); + w.AppendLine ("\t\t{"); + w.AppendLine ($"\t\t\tusing (var obj = new BindAsTestClassGenerated ()) {{"); + w.AppendLine ($"\t\t\t\tAssert.IsNull (obj.PSmart{v.Managed}Properties, \"initial null\");"); + w.AppendLine ($"\t\t\t\tMessaging.void_objc_msgSend_IntPtr (obj.Handle, Selector.GetHandle (\"setSmart{v.Managed}Values:\"), IntPtr.Zero);"); + w.AppendLine ($"\t\t\t\tAssert.IsNull (obj.PSmart{v.Managed}Properties, \"null after setting null\");"); + w.AppendLine (); + + w.AppendLine ($"\t\t\t\tobj.PSmart{v.Managed}Properties = new {v.Managed} [] {{ {v.ManagedNewExpression} }};"); + w.AppendLine ($"\t\t\t\tMessaging.void_objc_msgSend_IntPtr (obj.Handle, Selector.GetHandle (\"setSmart{v.Managed}Values:\"), IntPtr.Zero);"); + w.AppendLine ($"\t\t\t\tAssert.IsNull (obj.PSmart{v.Managed}Properties, \"null after re-setting null\");"); + w.AppendLine (); + + w.AppendLine ($"\t\t\t\tvar value = new {v.Managed} [] {{ {v.ManagedNewExpression} }};"); + w.AppendLine ($"\t\t\t\tusing (var input = NSArray.FromNSObjects<{v.Managed}> ((v) => v.GetConstant (), value))"); + w.AppendLine ($"\t\t\t\t\tMessaging.void_objc_msgSend_IntPtr (obj.Handle, Selector.GetHandle (\"setSmart{v.Managed}Values:\"), input.Handle);"); + w.AppendLine ($"\t\t\t\tAssert.AreEqual (value.Length, obj.PSmart{v.Managed}Properties.Length, \"after setting A\");"); + w.AppendLine ($"\t\t\t\tAssert.AreEqual (value [0], obj.PSmart{v.Managed}Properties [0], \"after setting A element\");"); + w.AppendLine (); + + w.AppendLine ($"\t\t\t\tobj.PSmart{v.Managed}Properties = null;"); + w.AppendLine ($"\t\t\t\tusing (var input = NSArray.FromNSObjects<{v.Managed}> ((v) => v.GetConstant (), value))"); + w.AppendLine ($"\t\t\t\t\tMessaging.void_objc_msgSend_IntPtr (obj.Handle, Selector.GetHandle (\"setSmart{v.Managed}Values:\"), input.Handle);"); + w.AppendLine ($"\t\t\t\tAssert.AreEqual (value.Length, obj.PSmart{v.Managed}Properties.Length, \"after setting B\");"); + w.AppendLine ($"\t\t\t\tAssert.AreEqual (value [0], obj.PSmart{v.Managed}Properties [0], \"after setting B element\");"); + w.AppendLine (); + + w.AppendLine ($"\t\t\t\tobj.PSmart{v.Managed}Properties = null;"); + w.AppendLine ($"\t\t\t\tvar array = Runtime.GetNSObject (Messaging.IntPtr_objc_msgSend (obj.Handle, Selector.GetHandle (\"getSmart{v.Managed}Values\")));"); + w.AppendLine ($"\t\t\t\tAssert.IsNull (array, \"null from getter A\");"); + w.AppendLine (); + + w.AppendLine ($"\t\t\t\tvalue = new {v.Managed} [] {{ {v.ManagedNewExpression} }};"); + w.AppendLine ($"\t\t\t\tobj.PSmart{v.Managed}Properties = value;"); + w.AppendLine ($"\t\t\t\tarray = Runtime.GetNSObject (Messaging.IntPtr_objc_msgSend (obj.Handle, Selector.GetHandle (\"getSmart{v.Managed}Values\")));"); + w.AppendLine ($"\t\t\t\tAssert.AreEqual (value.Length, array.Count, \"getter B\");"); + w.AppendLine ($"\t\t\t\tAssert.AreEqual (value [0], {v.Managed}Extensions.GetValue (array.GetItem (0)), \"getter B element\");"); + w.AppendLine ($"\t\t\t}}"); + w.AppendLine ("\t\t}"); + w.AppendLine (); + + if (v.ManagedCondition != null) + w.AppendLine ("#endif"); + + w.AppendLine (); + } + w.AppendLine (@" } }"); @@ -524,6 +1644,8 @@ static void Main () WriteLibTestStructH (); WriteLibTestDecompileM (); WriteLibTestPropertiesH (); + WriteLibTestMethodsH (); + WriteLibTestMethodsM (); /* binding code */ WriteApiDefinition (); diff --git a/tests/tests.sln b/tests/tests.sln index ac36fbc5d294..b4332954663c 100644 --- a/tests/tests.sln +++ b/tests/tests.sln @@ -69,6 +69,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "mscorlib-0", "bcl-test\msco EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "mscorlib-1", "bcl-test\mscorlib\mscorlib-1.csproj", "{6F47C092-2F85-43D6-2222-E687426F6BF3}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "testgenerator", "test-libraries\testgenerator.csproj", "{CD430449-8E59-4ECD-ADD9-ACF79E9E660B}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|iPhoneSimulator = Debug|iPhoneSimulator @@ -758,5 +760,31 @@ Global {6F47C092-2F85-43D6-2222-E687426F6BF3}.Release32|iPhone.Build.0 = Release|Any CPU {6F47C092-2F85-43D6-2222-E687426F6BF3}.Release64|iPhone.ActiveCfg = Release|Any CPU {6F47C092-2F85-43D6-2222-E687426F6BF3}.Release64|iPhone.Build.0 = Release|Any CPU + {CD430449-8E59-4ECD-ADD9-ACF79E9E660B}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU + {CD430449-8E59-4ECD-ADD9-ACF79E9E660B}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU + {CD430449-8E59-4ECD-ADD9-ACF79E9E660B}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU + {CD430449-8E59-4ECD-ADD9-ACF79E9E660B}.Release|iPhoneSimulator.Build.0 = Release|Any CPU + {CD430449-8E59-4ECD-ADD9-ACF79E9E660B}.Debug|iPhone.ActiveCfg = Debug|Any CPU + {CD430449-8E59-4ECD-ADD9-ACF79E9E660B}.Debug|iPhone.Build.0 = Debug|Any CPU + {CD430449-8E59-4ECD-ADD9-ACF79E9E660B}.Release|iPhone.ActiveCfg = Release|Any CPU + {CD430449-8E59-4ECD-ADD9-ACF79E9E660B}.Release|iPhone.Build.0 = Release|Any CPU + {CD430449-8E59-4ECD-ADD9-ACF79E9E660B}.Release-bitcode|iPhone.ActiveCfg = Release|Any CPU + {CD430449-8E59-4ECD-ADD9-ACF79E9E660B}.Release-bitcode|iPhone.Build.0 = Release|Any CPU + {CD430449-8E59-4ECD-ADD9-ACF79E9E660B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {CD430449-8E59-4ECD-ADD9-ACF79E9E660B}.Debug|Any CPU.Build.0 = Debug|Any CPU + {CD430449-8E59-4ECD-ADD9-ACF79E9E660B}.Release|Any CPU.ActiveCfg = Release|Any CPU + {CD430449-8E59-4ECD-ADD9-ACF79E9E660B}.Release|Any CPU.Build.0 = Release|Any CPU + {CD430449-8E59-4ECD-ADD9-ACF79E9E660B}.Release-bitcode|Any CPU.ActiveCfg = Debug|Any CPU + {CD430449-8E59-4ECD-ADD9-ACF79E9E660B}.Release-bitcode|Any CPU.Build.0 = Debug|Any CPU + {CD430449-8E59-4ECD-ADD9-ACF79E9E660B}.Release-bitcode|iPhoneSimulator.ActiveCfg = Debug|Any CPU + {CD430449-8E59-4ECD-ADD9-ACF79E9E660B}.Release-bitcode|iPhoneSimulator.Build.0 = Debug|Any CPU + {CD430449-8E59-4ECD-ADD9-ACF79E9E660B}.Debug32|iPhone.ActiveCfg = Debug|Any CPU + {CD430449-8E59-4ECD-ADD9-ACF79E9E660B}.Debug32|iPhone.Build.0 = Debug|Any CPU + {CD430449-8E59-4ECD-ADD9-ACF79E9E660B}.Debug64|iPhone.ActiveCfg = Debug|Any CPU + {CD430449-8E59-4ECD-ADD9-ACF79E9E660B}.Debug64|iPhone.Build.0 = Debug|Any CPU + {CD430449-8E59-4ECD-ADD9-ACF79E9E660B}.Release32|iPhone.ActiveCfg = Release|Any CPU + {CD430449-8E59-4ECD-ADD9-ACF79E9E660B}.Release32|iPhone.Build.0 = Release|Any CPU + {CD430449-8E59-4ECD-ADD9-ACF79E9E660B}.Release64|iPhone.ActiveCfg = Release|Any CPU + {CD430449-8E59-4ECD-ADD9-ACF79E9E660B}.Release64|iPhone.Build.0 = Release|Any CPU EndGlobalSection EndGlobal From eea87f488909a55bb81331baf6ff9dbd696e0ee2 Mon Sep 17 00:00:00 2001 From: Rolf Bjarne Kvinge Date: Mon, 3 Jul 2017 19:16:53 +0200 Subject: [PATCH 09/46] [tests] Add mtouch tests for the BindAs attribute. --- tests/common/ExecutionHelper.cs | 17 +++++++ tests/mtouch/RegistrarTest.cs | 80 +++++++++++++++++++++++++++++++++ 2 files changed, 97 insertions(+) diff --git a/tests/common/ExecutionHelper.cs b/tests/common/ExecutionHelper.cs index f0bcee1be761..6d579826d2e8 100644 --- a/tests/common/ExecutionHelper.cs +++ b/tests/common/ExecutionHelper.cs @@ -154,6 +154,18 @@ public bool HasErrorPattern (string prefix, int number, string messagePattern) return false; } + public int ErrorCount { + get { + return messages.Count ((v) => v.IsError); + } + } + + public int WarningCount { + get { + return messages.Count ((v) => v.IsWarning); + } + } + public bool HasError (string prefix, int number, string message) { foreach (var msg in messages) { @@ -163,6 +175,11 @@ public bool HasError (string prefix, int number, string message) return false; } + public void AssertErrorCount (int count, string message = "errors") + { + Assert.AreEqual (count, ErrorCount, message); + } + public void AssertErrorPattern (int number, string messagePattern) { AssertErrorPattern (MessagePrefix, number, messagePattern); diff --git a/tests/mtouch/RegistrarTest.cs b/tests/mtouch/RegistrarTest.cs index 8f7ede292ad7..3576c1e91b54 100644 --- a/tests/mtouch/RegistrarTest.cs +++ b/tests/mtouch/RegistrarTest.cs @@ -720,6 +720,86 @@ public class C } } + [Test] + public void MT4170 () + { + using (var mtouch = new MTouchTool ()) { + var code = @" + namespace NS { + using System; + using Foundation; + using ObjCRuntime; + class X : NSObject { + [Export (""a"")] + [return: BindAs (typeof (ConsoleColor), OriginalType = typeof (NSNumber))] + ConsoleColor A () { throw new NotImplementedException (); } + [Export (""b"")] + [return: BindAs (typeof (ConsoleColor?), OriginalType = typeof (NSNumber))] + ConsoleColor? B () { throw new NotImplementedException (); } + } + }"; + mtouch.Linker = MTouchLinker.DontLink; // faster + mtouch.Registrar = MTouchRegistrar.Static; + mtouch.CreateTemporaryApp (extraCode: code, extraArg: "-debug"); + mtouch.AssertExecuteFailure (MTouchAction.BuildSim, "build"); + mtouch.AssertError (4170, "The registrar can't convert from 'System.ConsoleColor' to 'Foundation.NSNumber' for the return value in the method NS.X.A.", "testApp.cs", 9); + mtouch.AssertError (4170, "The registrar can't convert from 'System.Nullable`1' to 'Foundation.NSNumber' for the return value in the method NS.X.B.", "testApp.cs", 12); + mtouch.AssertErrorCount (4 /* errors are duplicated */); + } + } + + [Test] + public void MT4172 () + { + using (var mtouch = new MTouchTool ()) { + var code = @" + namespace NS { + using System; + using Foundation; + using ObjCRuntime; + class X : NSObject { + [Export (""a:"")] + void A ([BindAs (typeof (ConsoleColor), OriginalType = typeof (NSNumber))] ConsoleColor value) {} + [Export (""b:"")] + void B ([BindAs (typeof (ConsoleColor?), OriginalType = typeof (NSNumber))] ConsoleColor? value) {} + [Export (""d:"")] + void D ([BindAs (typeof (int?[]), OriginalType = typeof (NSNumber[]))] int?[] value) {} + [Export (""e:"")] + void E ([BindAs (typeof (int), OriginalType = typeof (NSNumber))] ref int value) {} + [Export (""f:"")] + void F ([BindAs (typeof (int), OriginalType = typeof (NSNumber))] out int value) { throw new NotImplementedException (); } + [Export (""g:"")] + void G ([BindAs (typeof (int[,]), OriginalType = typeof (NSNumber[,]))] int[,] value) {} + [Export (""h:"")] + void H ([BindAs (typeof (int?[,]), OriginalType = typeof (NSNumber[,]))] int?[,] value) {} + } + enum E { + V, + } + class EClass : NSObject { + [Export (""a:"")] + void A ([BindAs (typeof (E), OriginalType = typeof (NSString))] E value) {} + [Export (""d:"")] + void D ([BindAs (typeof (E?[]), OriginalType = typeof (NSString[]))] E?[] value) {} + } + }"; + mtouch.Linker = MTouchLinker.DontLink; // faster + mtouch.Registrar = MTouchRegistrar.Static; + mtouch.CreateTemporaryApp (extraCode: code, extraArg: "-debug"); + mtouch.AssertExecuteFailure (MTouchAction.BuildSim, "build"); + mtouch.AssertError (4172, "The registrar can't convert from 'System.ConsoleColor' to 'Foundation.NSNumber' for the parameter 'value' in the method NS.X.A.", "testApp.cs", 8); + mtouch.AssertError (4172, "The registrar can't convert from 'System.Nullable`1' to 'Foundation.NSNumber' for the parameter 'value' in the method NS.X.B.", "testApp.cs", 10); + mtouch.AssertError (4172, "The registrar can't convert from 'System.Nullable`1[]' to 'Foundation.NSNumber[]' for the parameter 'value' in the method NS.X.D.", "testApp.cs", 12); + mtouch.AssertError (4172, "The registrar can't convert from 'System.Int32&' to 'Foundation.NSNumber' for the parameter 'value' in the method NS.X.E.", "testApp.cs", 14); + mtouch.AssertError (4172, "The registrar can't convert from 'System.Int32&' to 'Foundation.NSNumber' for the parameter 'value' in the method NS.X.F.", "testApp.cs", 16); + mtouch.AssertError (4172, "The registrar can't convert from 'System.Int32[0...,0...]' to 'Foundation.NSNumber[,]' for the parameter 'value' in the method NS.X.G.", "testApp.cs", 18); + mtouch.AssertError (4172, "The registrar can't convert from 'System.Nullable`1[0...,0...]' to 'Foundation.NSNumber[,]' for the parameter 'value' in the method NS.X.H.", "testApp.cs", 20); + mtouch.AssertError (4172, "The registrar can't convert from 'NS.E' to 'Foundation.NSString' for the parameter 'value' in the method NS.EClass.A.", "testApp.cs", 27); + mtouch.AssertError (4172, "The registrar can't convert from 'System.Nullable`1[]' to 'Foundation.NSString[]' for the parameter 'value' in the method NS.EClass.D.", "testApp.cs", 29); + mtouch.AssertErrorCount (9); + } + } + [Test] public void NoWarnings () { From 0730312d51fbb7deadf8f1562490457e6654279e Mon Sep 17 00:00:00 2001 From: Rolf Bjarne Kvinge Date: Mon, 3 Jul 2017 19:18:02 +0200 Subject: [PATCH 10/46] [registrar] Add support for the BindAs attribute. --- docs/website/mtouch-errors.md | 9 + runtime/delegates.t4 | 19 +- runtime/exports.t4 | 18 + runtime/frameworks.h | 23 + runtime/runtime-internal.h | 2 +- runtime/runtime.m | 72 +++- runtime/trampolines-i386.m | 4 +- runtime/trampolines-internal.h | 3 +- runtime/trampolines-invoke.m | 40 +- runtime/trampolines-varargs.m | 4 +- runtime/trampolines-x86_64.m | 4 +- runtime/trampolines.m | 601 ++++++++++++++++++++++++++- runtime/xamarin/runtime.h | 20 +- runtime/xamarin/trampolines.h | 101 +++++ src/ObjCRuntime/DynamicRegistrar.cs | 104 ++++- src/ObjCRuntime/MethodDescription.cs | 66 +-- src/ObjCRuntime/Registrar.cs | 295 +++++++++++-- src/ObjCRuntime/Runtime.cs | 36 +- tools/common/StaticRegistrar.cs | 421 ++++++++++++++++++- 19 files changed, 1693 insertions(+), 149 deletions(-) create mode 100644 runtime/frameworks.h diff --git a/docs/website/mtouch-errors.md b/docs/website/mtouch-errors.md index 79a05a67d1e8..dcd98fe18320 100644 --- a/docs/website/mtouch-errors.md +++ b/docs/website/mtouch-errors.md @@ -1544,6 +1544,10 @@ Please use a valid Objective-C identifier. Xamarin.iOS failed to generate a P/Invoke wrapper function for the mentioned. Please check the reported error message for the underlying cause. +### MT4170: The registrar can't convert from '{managed type}' to '{native type}' for the return value in the method {method}. + +### MT4172: The registrar can't convert from '{native type}' to '{managed type}' for the parameter '{parameter name}' in the method {method}. + # MT5xxx: GCC and toolchain error messages ### MT51xx: Compilation @@ -2234,3 +2238,8 @@ This indicates a bug in Xamarin.iOS. Please file a bug at [http://bugzilla.xamar ### MT8023: An instance object is required to construct a closed generic method for the open generic method: * (token reference: *). Please file a bug report at http://bugzilla.xamarin.com. This indicates a bug in Xamarin.iOS. Please file a bug at [http://bugzilla.xamarin.com](https://bugzilla.xamarin.com/enter_bug.cgi?product=iOS). + +### MT8024: Could not find a valid extension type for the smart enum '{smart_type}'. Please file a bug at https://bugzilla.xamarin.com. + +This indicates a bug in Xamarin.iOS. Please file a bug at [http://bugzilla.xamarin.com](https://bugzilla.xamarin.com/enter_bug.cgi?product=iOS). + diff --git a/runtime/delegates.t4 b/runtime/delegates.t4 index f56fb3114174..5ed2ea87adac 100644 --- a/runtime/delegates.t4 +++ b/runtime/delegates.t4 @@ -69,10 +69,11 @@ "MonoObject *", "IntPtr", "obj" ) { WrappedManagedFunction = "GetSelectorHandle" }, - new XDelegate ("MethodDescription", "UnmanagedMethodDescription", "xamarin_get_method_for_selector", + new XDelegate ("void", "void", "xamarin_get_method_for_selector", "Class", "IntPtr", "cls", "SEL", "IntPtr", "sel", - "bool", "bool", "is_static" + "bool", "bool", "is_static", + "MethodDescription *", "IntPtr", "desc" ) { WrappedManagedFunction = "GetMethodForSelector" }, new XDelegate ("MonoObject *", "IntPtr", "xamarin_get_nsobject", @@ -138,12 +139,13 @@ "int", "int", "parameter" ) { WrappedManagedFunction = "IsParameterOut" }, - new XDelegate ("MethodDescription", "UnmanagedMethodDescription", "xamarin_get_method_and_object_for_selector", + new XDelegate ("void", "void", "xamarin_get_method_and_object_for_selector", "Class", "IntPtr", "cls", "SEL", "IntPtr", "sel", "bool", "bool", "is_static", "id", "IntPtr", "obj", - "MonoObject **", "ref IntPtr", "mthis" + "MonoObject **", "ref IntPtr", "mthis", + "MethodDescription *", "IntPtr", "desc" ) { WrappedManagedFunction = "GetMethodAndObjectForSelector" }, new XDelegate ("guint32", "int", "xamarin_create_product_exception_for_error", @@ -167,6 +169,15 @@ "id", "IntPtr", "exception", "bool", "bool", "throwManagedAsDefault" ) { WrappedManagedFunction = "OnMarshalObjectiveCException" }, + + new XDelegate ("NSString *", "IntPtr", "xamarin_convert_smart_enum_to_nsstring", + "void *", "IntPtr", "value" + ) { WrappedManagedFunction = "ConvertSmartEnumToNSString" }, + + new XDelegate ("void *", "IntPtr", "xamarin_convert_nsstring_to_smart_enum", + "NSString *", "IntPtr", "value", + "MonoReflectionType *", "IntPtr", "type" + ) { WrappedManagedFunction = "ConvertNSStringToSmartEnum" }, }; delegates.CalculateLengths (); #><#+ diff --git a/runtime/exports.t4 b/runtime/exports.t4 index 66967cfdbae8..b6b0abb4a934 100644 --- a/runtime/exports.t4 +++ b/runtime/exports.t4 @@ -76,7 +76,16 @@ new Export ("MonoType *", "mono_class_get_type", "MonoClass *", "klass" ), + #endregion + + #region metadata/class-internals.h + new Export ("gboolean", "mono_class_is_nullable", + "MonoClass *", "klass" + ), + new Export ("MonoClass *", "mono_class_get_nullable_param", + "MonoClass *", "klass" + ), #endregion #region metadata/debug-helpers.h @@ -173,6 +182,12 @@ "void *", "value" ), + new Export ("MonoObject *", "mono_value_box", + "MonoDomain *", "domain", + "MonoClass *", "klass", + "void *", "val" + ), + #endregion #region metadata/profiler.h @@ -348,6 +363,9 @@ "MonoType *", "type" ), + new Export ("MonoType *", "mono_reflection_type_get_type", + "MonoReflectionType *", "reftype" + ), #endregion #region metadata/metadata.h diff --git a/runtime/frameworks.h b/runtime/frameworks.h new file mode 100644 index 000000000000..8e9c62d750cb --- /dev/null +++ b/runtime/frameworks.h @@ -0,0 +1,23 @@ +#if TARGET_OS_IOS || TARGET_OS_TV || TARGET_OS_WATCH +#define HAVE_UIKIT 1 +#else +#define HAVE_UIKIT 0 +#endif + +#if TARGET_OS_IOS || TARGET_OS_TV || TARGET_OS_WATCH || defined (__x86_64__) +#define HAVE_MAPKIT 1 +#else +#define HAVE_MAPKIT 0 +#endif + +#if !TARGET_OS_WATCH +#define HAVE_COREMEDIA 1 +#else +#define HAVE_COREMEDIA 0 +#endif + +#if !TARGET_OS_WATCH +#define HAVE_COREANIMATION 1 +#else +#define HAVE_COREANIMATION 0 +#endif diff --git a/runtime/runtime-internal.h b/runtime/runtime-internal.h index 3bdb5936ff06..ace1efaf82ed 100644 --- a/runtime/runtime-internal.h +++ b/runtime/runtime-internal.h @@ -29,7 +29,7 @@ extern "C" { #endif -void *xamarin_marshal_return_value (MonoType *mtype, const char *type, MonoObject *retval, bool retain, MonoMethod *method, guint32 *exception_gchandle); +void *xamarin_marshal_return_value (MonoType *mtype, const char *type, MonoObject *retval, bool retain, MonoMethod *method, MethodDescription *desc, guint32 *exception_gchandle); #ifdef __cplusplus } diff --git a/runtime/runtime.m b/runtime/runtime.m index f820eb6df0b3..e9c20f12487e 100644 --- a/runtime/runtime.m +++ b/runtime/runtime.m @@ -90,8 +90,12 @@ /* Local variable */ +static MonoImage *platform_image; static MonoClass *inativeobject_class; static MonoClass *nsobject_class; +static MonoClass *nsvalue_class; +static MonoClass *nsnumber_class; +static MonoClass *nsstring_class; static pthread_mutex_t framework_peer_release_lock; static MonoGHashTable *xamarin_wrapper_hash; @@ -240,8 +244,12 @@ void *iter = NULL; MonoType *p = NULL; - for (int i = 0; i < index + 1; i++) - p = mono_signature_get_params (msig, &iter); + if (index == -1) { + p = mono_signature_get_return_type (msig); + } else { + for (int i = 0; i < index + 1; i++) + p = mono_signature_get_params (msig, &iter); + } return p; } @@ -345,6 +353,18 @@ void xamarin_framework_peer_unlock () pthread_mutex_unlock (&framework_peer_release_lock); } +MonoClass * +xamarin_get_nsvalue_class () +{ + return nsvalue_class; +} + +MonoClass * +xamarin_get_nsnumber_class () +{ + return nsnumber_class; +} + bool xamarin_is_class_nsobject (MonoClass *cls) { @@ -372,6 +392,33 @@ void xamarin_framework_peer_unlock () return mono_class_is_subclass_of (cls, mono_get_array_class (), false); } +bool +xamarin_is_class_nsnumber (MonoClass *cls) +{ + // COOP: Reading managed data, must be in UNSAFE mode + MONO_ASSERT_GC_UNSAFE; + + return mono_class_is_subclass_of (cls, nsnumber_class, false); +} + +bool +xamarin_is_class_nsvalue (MonoClass *cls) +{ + // COOP: Reading managed data, must be in UNSAFE mode + MONO_ASSERT_GC_UNSAFE; + + return mono_class_is_subclass_of (cls, nsvalue_class, false); +} + +bool +xamarin_is_class_nsstring (MonoClass *cls) +{ + // COOP: Reading managed data, must be in UNSAFE mode + MONO_ASSERT_GC_UNSAFE; + + return mono_class_is_subclass_of (cls, nsstring_class, false); +} + #define MANAGED_REF_BIT (1 << 31) #define GCHANDLE_WEAK (1 << 30) #define GCHANDLE_MASK (MANAGED_REF_BIT | GCHANDLE_WEAK) @@ -1154,7 +1201,6 @@ -(void) xamarinSetGCHandle: (int) gc_handle; MonoClass *runtime_class; MonoAssembly *assembly = NULL; - MonoImage *image; MonoMethod *runtime_initialize; void* params[2]; const char *product_dll = NULL; @@ -1199,14 +1245,17 @@ -(void) xamarinSetGCHandle: (int) gc_handle; if (!assembly) xamarin_assertion_message ("Failed to load %s.", product_dll); - image = mono_assembly_get_image (assembly); + platform_image = mono_assembly_get_image (assembly); const char *objcruntime = xamarin_use_new_assemblies ? "ObjCRuntime" : PRODUCT_COMPAT_NAMESPACE ".ObjCRuntime"; const char *foundation = xamarin_use_new_assemblies ? "Foundation" : PRODUCT_COMPAT_NAMESPACE ".Foundation"; - runtime_class = get_class_from_name (image, objcruntime, "Runtime"); - inativeobject_class = get_class_from_name (image, objcruntime, "INativeObject"); - nsobject_class = get_class_from_name (image, foundation, "NSObject"); + runtime_class = get_class_from_name (platform_image, objcruntime, "Runtime"); + inativeobject_class = get_class_from_name (platform_image, objcruntime, "INativeObject"); + nsobject_class = get_class_from_name (platform_image, foundation, "NSObject"); + nsnumber_class = get_class_from_name (platform_image, foundation, "NSNumber"); + nsvalue_class = get_class_from_name (platform_image, foundation, "NSValue"); + nsstring_class = get_class_from_name (platform_image, foundation, "NSString"); mono_add_internal_call (xamarin_use_new_assemblies ? "Foundation.NSObject::xamarin_release_managed_ref" : PRODUCT_COMPAT_NAMESPACE ".Foundation.NSObject::xamarin_release_managed_ref", (const void *) xamarin_release_managed_ref); mono_add_internal_call (xamarin_use_new_assemblies ? "Foundation.NSObject::xamarin_create_managed_ref" : PRODUCT_COMPAT_NAMESPACE ".Foundation.NSObject::xamarin_create_managed_ref", (const void *) xamarin_create_managed_ref); @@ -1293,10 +1342,19 @@ -(void) xamarinSetGCHandle: (int) gc_handle; x_bundle_path = strdup (path); } +void * +xamarin_calloc (size_t size) +{ + // COOP: no managed memory access: any mode + return calloc (size, 1); +} + void xamarin_free (void *ptr) { // COOP: no managed memory access: any mode + // We use this method to free memory returned by mono, + // which means we have to use the free function mono expects. if (ptr) free (ptr); } diff --git a/runtime/trampolines-i386.m b/runtime/trampolines-i386.m index d085f2df3e4e..e1324d6678d4 100644 --- a/runtime/trampolines-i386.m +++ b/runtime/trampolines-i386.m @@ -82,7 +82,7 @@ } static void -marshal_return_value (void *context, const char *type, size_t size, void *vvalue, MonoType *mtype, bool retain, MonoMethod *method, guint32 *exception_gchandle) +marshal_return_value (void *context, const char *type, size_t size, void *vvalue, MonoType *mtype, bool retain, MonoMethod *method, MethodDescription *desc, guint32 *exception_gchandle) { MonoObject *value = (MonoObject *) vvalue; struct ParamIterator *it = (struct ParamIterator *) context; @@ -164,7 +164,7 @@ break; } - it->state->eax = (uint32_t) xamarin_marshal_return_value (mtype, type, value, retain, method, exception_gchandle); + it->state->eax = (uint32_t) xamarin_marshal_return_value (mtype, type, value, retain, method, desc, exception_gchandle); break; case _C_VOID: break; diff --git a/runtime/trampolines-internal.h b/runtime/trampolines-internal.h index 53a4026d5635..708f4436ecae 100644 --- a/runtime/trampolines-internal.h +++ b/runtime/trampolines-internal.h @@ -4,6 +4,7 @@ #include #include "xamarin/mono-runtime.h" +#include "xamarin/runtime.h" #ifdef __cplusplus extern "C" { @@ -36,7 +37,7 @@ enum IteratorAction { // type: pass NULL to start iterating. // target: can be null if not interested in the value. typedef void (*iterator_func) (enum IteratorAction action, void *context, const char *type, size_t size, void *target, guint32 *exception_gchandle); -typedef void (*marshal_return_value_func) (void *context, const char *type, size_t size, void *value, MonoType *mtype, bool retain, MonoMethod *method, guint32 *exception_gchandle); +typedef void (*marshal_return_value_func) (void *context, const char *type, size_t size, void *value, MonoType *mtype, bool retain, MonoMethod *method, MethodDescription *desc, guint32 *exception_gchandle); void xamarin_invoke_trampoline (enum TrampolineType type, id self, SEL sel, iterator_func iterator, marshal_return_value_func marshal_return_value, void *context); diff --git a/runtime/trampolines-invoke.m b/runtime/trampolines-invoke.m index 28aa4db00fd6..f648578683fd 100644 --- a/runtime/trampolines-invoke.m +++ b/runtime/trampolines-invoke.m @@ -48,7 +48,7 @@ if (has_nsobject) { self = xamarin_invoke_objc_method_implementation (self, sel, (IMP) xamarin_ctor_trampoline); - marshal_return_value (context, "|", sizeof (id), self, NULL, false, NULL, &exception_gchandle); + marshal_return_value (context, "|", sizeof (id), self, NULL, false, NULL, NULL, &exception_gchandle); xamarin_process_managed_exception_gchandle (exception_gchandle); return; } @@ -58,6 +58,7 @@ // pre-prolog SList *dispose_list = NULL; + SList *free_list = NULL; int num_arg; NSMethodSignature *sig; @@ -70,7 +71,7 @@ // prolog MonoObject *mthis = NULL; - MethodDescription desc; + MethodDescription *desc = NULL; MonoMethod *method; MonoMethodSignature *msig; int semantic; @@ -87,18 +88,24 @@ int i; int mofs = 0; + int desc_arg_count = num_arg + 2; /* 1 for the return value + 1 if this is a category instance method */ + size_t desc_size = desc_arg_count * sizeof (BindAsData) + sizeof (MethodDescription); + desc = (MethodDescription *) xamarin_calloc (desc_size); + desc->bindas_count = desc_arg_count; + free_list = s_list_prepend (free_list, desc); + if (is_ctor || is_static) { - desc = xamarin_get_method_for_selector ([self class], sel, is_static, &exception_gchandle); + xamarin_get_method_for_selector ([self class], sel, is_static, desc, &exception_gchandle); } else { - desc = xamarin_get_method_and_object_for_selector ([self class], sel, is_static, self, &mthis, &exception_gchandle); + xamarin_get_method_and_object_for_selector ([self class], sel, is_static, self, &mthis, desc, &exception_gchandle); } if (exception_gchandle != 0) goto exception_handling; - method = xamarin_get_reflection_method_method (desc.method); + method = xamarin_get_reflection_method_method (desc->method); msig = mono_method_signature (method); - semantic = desc.semantic & ArgumentSemanticMask; - isCategoryInstance = (desc.semantic & ArgumentSemanticCategoryInstance) == ArgumentSemanticCategoryInstance; + semantic = desc->semantic & ArgumentSemanticMask; + isCategoryInstance = (desc->semantic & ArgumentSemanticCategoryInstance) == ArgumentSemanticCategoryInstance; frame_length = [sig frameLength] - (sizeof (void *) * (isCategoryInstance ? 1 : 2)); arg_frame = (void **) alloca (frame_length); @@ -158,6 +165,13 @@ iterator (IteratorIterate, context, type, size, &arg, &exception_gchandle); if (exception_gchandle != 0) goto exception_handling; + if (desc->bindas [i + 1].original_type != NULL) { + arg_ptrs [i + mofs] = xamarin_generate_conversion_to_managed ((id) arg, mono_reflection_type_get_type (desc->bindas [i + 1].original_type), p, method, &exception_gchandle, (void **) &free_list); + if (exception_gchandle != 0) + goto exception_handling; + ofs++; + continue; + } switch (type [0]) { case _C_PTR: { switch (type [1]) { @@ -531,9 +545,9 @@ ret_type = [sig methodReturnType]; ret_type = xamarin_skip_encoding_flags (ret_type); if (is_ctor) { - marshal_return_value (context, "|", sizeof (id), self, mono_signature_get_return_type (msig), (desc.semantic & ArgumentSemanticRetainReturnValue) != 0, method, &exception_gchandle); + marshal_return_value (context, "|", sizeof (id), self, mono_signature_get_return_type (msig), (desc->semantic & ArgumentSemanticRetainReturnValue) != 0, method, desc, &exception_gchandle); } else if (*ret_type != 'v') { - marshal_return_value (context, ret_type, [sig methodReturnLength], retval, mono_signature_get_return_type (msig), (desc.semantic & ArgumentSemanticRetainReturnValue) != 0, method, &exception_gchandle); + marshal_return_value (context, ret_type, [sig methodReturnLength], retval, mono_signature_get_return_type (msig), (desc->semantic & ArgumentSemanticRetainReturnValue) != 0, method, desc, &exception_gchandle); } exception_handling: @@ -559,6 +573,14 @@ } s_list_free (dispose_list); } + if (free_list) { + SList *list = free_list; + while (list) { + xamarin_free (list->data); + list = list->next; + } + s_list_free (free_list); + } MONO_THREAD_DETACH; // COOP: This will switch to GC_SAFE diff --git a/runtime/trampolines-varargs.m b/runtime/trampolines-varargs.m index 81a682f2fb1f..854040593bb0 100644 --- a/runtime/trampolines-varargs.m +++ b/runtime/trampolines-varargs.m @@ -89,7 +89,7 @@ } static void -marshal_return_value (void *context, const char *type, size_t size, void *vvalue, MonoType *mtype, bool retain, MonoMethod *method, guint32 *exception_gchandle) +marshal_return_value (void *context, const char *type, size_t size, void *vvalue, MonoType *mtype, bool retain, MonoMethod *method, MethodDescription *desc, guint32 *exception_gchandle) { MonoObject *value = (MonoObject *) vvalue; struct ParamIterator *it = (struct ParamIterator *) context; @@ -157,7 +157,7 @@ break; } - state->ptr_ret = xamarin_marshal_return_value (mtype, type, value, retain, method, exception_gchandle); + state->ptr_ret = xamarin_marshal_return_value (mtype, type, value, retain, method, desc, exception_gchandle); break; case _C_VOID: break; diff --git a/runtime/trampolines-x86_64.m b/runtime/trampolines-x86_64.m index e6ce7db81066..02aa72f94154 100644 --- a/runtime/trampolines-x86_64.m +++ b/runtime/trampolines-x86_64.m @@ -303,7 +303,7 @@ } static void -marshal_return_value (void *context, const char *type, size_t size, void *vvalue, MonoType *mtype, bool retain, MonoMethod *method, guint32 *exception_gchandle) +marshal_return_value (void *context, const char *type, size_t size, void *vvalue, MonoType *mtype, bool retain, MonoMethod *method, MethodDescription *desc, guint32 *exception_gchandle) { // COOP: accessing managed memory (as input), so must be in unsafe mode. MONO_ASSERT_GC_UNSAFE; @@ -514,7 +514,7 @@ break; } - it->state->rax = (uint64_t) xamarin_marshal_return_value (mtype, type, value, retain, method, exception_gchandle); + it->state->rax = (uint64_t) xamarin_marshal_return_value (mtype, type, value, retain, method, desc, exception_gchandle); break; case _C_VOID: break; diff --git a/runtime/trampolines.m b/runtime/trampolines.m index d8901b641e2e..8ca1013136cf 100644 --- a/runtime/trampolines.m +++ b/runtime/trampolines.m @@ -12,6 +12,25 @@ #include #include +#include "frameworks.h" + +#include +#include +#include +#if HAVE_COREMEDIA +#include +#endif +#if HAVE_MAPKIT +#include +#endif +#include +#if HAVE_COREANIMATION +#include +#endif +#if HAVE_UIKIT +#include +#endif + #include #include "product.h" @@ -36,7 +55,7 @@ } void * -xamarin_marshal_return_value (MonoType *mtype, const char *type, MonoObject *retval, bool retain, MonoMethod *method, guint32 *exception_gchandle) +xamarin_marshal_return_value (MonoType *mtype, const char *type, MonoObject *retval, bool retain, MonoMethod *method, MethodDescription *desc, guint32 *exception_gchandle) { // COOP: accesses managed memory: unsafe mode. MONO_ASSERT_GC_UNSAFE; @@ -59,7 +78,10 @@ } case _C_ID: { MonoClass *r_klass = mono_object_get_class ((MonoObject *) retval); - if (r_klass == mono_get_string_class ()) { + + if (desc && desc->bindas [0].original_type != NULL) { + return xamarin_generate_conversion_to_native (retval, mono_class_get_type (r_klass), mono_reflection_type_get_type (desc->bindas [0].original_type), method, exception_gchandle); + } else if (r_klass == mono_get_string_class ()) { char *str = mono_string_to_utf8 ((MonoString *) retval); NSString *rv = [[NSString alloc] initWithUTF8String:str]; @@ -591,3 +613,578 @@ pthread_mutex_unlock (&gchandle_hash_lock); return gc_handle; } + +id +xamarin_generate_conversion_to_native (MonoObject *value, MonoType *inputType, MonoType *outputType, MonoMethod *method, guint32 *exception_gchandle) +{ + // COOP: Reads managed memory, needs to be in UNSAFE mode + MONO_ASSERT_GC_UNSAFE; + + // This method is a mirror of StaticRegistrar.GenerateConversionToNative + // These methods must be kept in sync. + + id convertedValue = NULL; + MonoClass *managedType = mono_class_from_mono_type (inputType); + MonoClass *nativeType = mono_class_from_mono_type (outputType); + + bool isManagedNullable = mono_class_is_nullable (managedType); + + MonoClass *underlyingManagedType = managedType; + MonoClass *underlyingNativeType = nativeType; + + bool isManagedArray = xamarin_is_class_array (managedType); + bool isNativeArray = xamarin_is_class_array (nativeType); + + if (isManagedArray != isNativeArray) { + *exception_gchandle = xamarin_create_bindas_exception (inputType, outputType, method); + goto exception_handling; + } + + if (isManagedArray) { + if (isManagedNullable) { + *exception_gchandle = xamarin_create_bindas_exception (inputType, outputType, method); + goto exception_handling; + } + underlyingNativeType = mono_class_get_element_class (nativeType); + underlyingManagedType = mono_class_get_element_class (managedType); + } else if (isManagedNullable) { + underlyingManagedType = mono_class_get_nullable_param (managedType); + } + + if (value) { + xamarin_managed_to_id_func func; + if (xamarin_is_class_nsnumber (underlyingNativeType)) { + func = xamarin_get_managed_to_nsnumber_func (underlyingManagedType, method, exception_gchandle); + } else if (xamarin_is_class_nsvalue (underlyingNativeType)) { + func = xamarin_get_managed_to_nsvalue_func (underlyingManagedType, method, exception_gchandle); + } else if (xamarin_is_class_nsstring (underlyingNativeType)) { + func = xamarin_get_smart_enum_to_nsstring_func (underlyingManagedType, method, exception_gchandle); + } else { + *exception_gchandle = xamarin_create_bindas_exception (inputType, outputType, method); + goto exception_handling; + } + if (*exception_gchandle != 0) + goto exception_handling; + + if (isManagedArray) { + convertedValue = xamarin_convert_managed_to_nsarray_with_func ((MonoArray *) value, func, exception_gchandle); + if (*exception_gchandle != 0) + goto exception_handling; + } else { + convertedValue = func (value, exception_gchandle); + if (*exception_gchandle != 0) + goto exception_handling; + } + } + +exception_handling: + + return convertedValue; +} + + +void * +xamarin_generate_conversion_to_managed (id value, MonoType *inputType, MonoType *outputType, MonoMethod *method, guint32 *exception_gchandle, /*SList*/ void **free_list) +{ + // COOP: Reads managed memory, needs to be in UNSAFE mode + MONO_ASSERT_GC_UNSAFE; + + // This method is a mirror of StaticRegistrar.GenerateConversionToManaged + // These methods must be kept in sync. + + void *convertedValue = NULL; + MonoClass *managedType = mono_class_from_mono_type (outputType); + MonoClass *nativeType = mono_class_from_mono_type (inputType); + + bool isManagedNullable = mono_class_is_nullable (managedType); + + MonoClass *underlyingManagedType = managedType; + MonoClass *underlyingNativeType = nativeType; + + bool isManagedArray = xamarin_is_class_array (managedType); + bool isNativeArray = xamarin_is_class_array (nativeType); + + if (isManagedArray != isNativeArray) { + *exception_gchandle = xamarin_create_bindas_exception (inputType, outputType, method); + goto exception_handling; + } + + if (isManagedArray) { + if (isManagedNullable) { + *exception_gchandle = xamarin_create_bindas_exception (inputType, outputType, method); + goto exception_handling; + } + underlyingNativeType = mono_class_get_element_class (nativeType); + underlyingManagedType = mono_class_get_element_class (managedType); + } else if (isManagedNullable) { + underlyingManagedType = mono_class_get_nullable_param (managedType); + } + + if (value) { + xamarin_id_to_managed_func func; + if (xamarin_is_class_nsnumber (underlyingNativeType)) { + func = xamarin_get_nsnumber_to_managed_func (underlyingManagedType, method, exception_gchandle); + } else if (xamarin_is_class_nsvalue (underlyingNativeType)) { + func = xamarin_get_nsvalue_to_managed_func (underlyingManagedType, method, exception_gchandle); + } else if (xamarin_is_class_nsstring (underlyingNativeType)) { + func = xamarin_get_nsstring_to_smart_enum_func (underlyingManagedType, method, exception_gchandle); + } else { + *exception_gchandle = xamarin_create_bindas_exception (inputType, outputType, method); + goto exception_handling; + } + if (*exception_gchandle != 0) + goto exception_handling; + + if (isManagedArray) { + convertedValue = xamarin_convert_nsarray_to_managed_with_func (value, underlyingManagedType, func, exception_gchandle); + if (*exception_gchandle != 0) + goto exception_handling; + } else { + convertedValue = func (value, NULL, underlyingManagedType, exception_gchandle); + if (*exception_gchandle != 0) + goto exception_handling; + *(SList **) free_list = s_list_prepend (*(SList **) free_list, convertedValue); + + if (isManagedNullable) + convertedValue = mono_value_box (mono_domain_get (), underlyingManagedType, convertedValue); + } + } + +exception_handling: + + return convertedValue; +} + +// Returns a pointer to the value type, which must be freed using xamarin_free. +// If called multiple times in succession, the returned pointer can be passed as the second ptr argument, and it need only be freed once done iterating. +void *xamarin_nsnumber_to_bool (NSNumber *number, void *ptr, MonoClass *managedType, guint32 *exception_gchandle) { BOOL *valueptr = (BOOL *) (ptr ? ptr : xamarin_calloc (sizeof (BOOL))); *valueptr = [number boolValue]; return valueptr; } +void *xamarin_nsnumber_to_sbyte (NSNumber *number, void *ptr, MonoClass *managedType, guint32 *exception_gchandle) { int8_t *valueptr = (int8_t *) (ptr ? ptr : xamarin_calloc (sizeof (int8_t))); *valueptr = [number charValue]; return valueptr; } +void *xamarin_nsnumber_to_byte (NSNumber *number, void *ptr, MonoClass *managedType, guint32 *exception_gchandle) { uint8_t *valueptr = (uint8_t *) (ptr ? ptr : xamarin_calloc (sizeof (uint8_t))); *valueptr = [number unsignedCharValue]; return valueptr; } +void *xamarin_nsnumber_to_short (NSNumber *number, void *ptr, MonoClass *managedType, guint32 *exception_gchandle) { int16_t *valueptr = (int16_t *) (ptr ? ptr : xamarin_calloc (sizeof (int16_t))); *valueptr = [number shortValue]; return valueptr; } +void *xamarin_nsnumber_to_ushort (NSNumber *number, void *ptr, MonoClass *managedType, guint32 *exception_gchandle) { uint16_t *valueptr = (uint16_t *) (ptr ? ptr : xamarin_calloc (sizeof (uint16_t))); *valueptr = [number unsignedShortValue]; return valueptr; } +void *xamarin_nsnumber_to_int (NSNumber *number, void *ptr, MonoClass *managedType, guint32 *exception_gchandle) { int32_t *valueptr = (int32_t *) (ptr ? ptr : xamarin_calloc (sizeof (int32_t))); *valueptr = [number intValue]; return valueptr; } +void *xamarin_nsnumber_to_uint (NSNumber *number, void *ptr, MonoClass *managedType, guint32 *exception_gchandle) { uint32_t *valueptr = (uint32_t *) (ptr ? ptr : xamarin_calloc (sizeof (uint32_t))); *valueptr = [number unsignedIntValue]; return valueptr; } +void *xamarin_nsnumber_to_long (NSNumber *number, void *ptr, MonoClass *managedType, guint32 *exception_gchandle) { int64_t *valueptr = (int64_t *) (ptr ? ptr : xamarin_calloc (sizeof (int64_t))); *valueptr = [number longLongValue]; return valueptr; } +void *xamarin_nsnumber_to_ulong (NSNumber *number, void *ptr, MonoClass *managedType, guint32 *exception_gchandle) { uint64_t *valueptr = (uint64_t *) (ptr ? ptr : xamarin_calloc (sizeof (uint64_t))); *valueptr = [number unsignedLongLongValue]; return valueptr; } +void *xamarin_nsnumber_to_nint (NSNumber *number, void *ptr, MonoClass *managedType, guint32 *exception_gchandle) { NSInteger *valueptr = (NSInteger *) (ptr ? ptr : xamarin_calloc (sizeof (NSInteger))); *valueptr = [number integerValue]; return valueptr; } +void *xamarin_nsnumber_to_nuint (NSNumber *number, void *ptr, MonoClass *managedType, guint32 *exception_gchandle) { NSUInteger *valueptr = (NSUInteger *) (ptr ? ptr : xamarin_calloc (sizeof (NSUInteger))); *valueptr = [number unsignedIntegerValue]; return valueptr; } +void *xamarin_nsnumber_to_float (NSNumber *number, void *ptr, MonoClass *managedType, guint32 *exception_gchandle) { float *valueptr = (float *) (ptr ? ptr : xamarin_calloc (sizeof (float))); *valueptr = [number floatValue]; return valueptr; } +void *xamarin_nsnumber_to_double (NSNumber *number, void *ptr, MonoClass *managedType, guint32 *exception_gchandle) { double *valueptr = (double *) (ptr ? ptr : xamarin_calloc (sizeof (double))); *valueptr = [number doubleValue]; return valueptr; } +#if __POINTER_WIDTH__ == 32 +void *xamarin_nsnumber_to_nfloat (NSNumber *number, void *ptr, MonoClass *managedType, guint32 *exception_gchandle) { float *valueptr = (float *) (ptr ? ptr : xamarin_calloc (sizeof (float))); *valueptr = [number floatValue]; return valueptr; } +#elif __POINTER_WIDTH__ == 64 +void *xamarin_nsnumber_to_nfloat (NSNumber *number, void *ptr, MonoClass *managedType, guint32 *exception_gchandle) { double *valueptr = (double *) (ptr ? ptr : xamarin_calloc (sizeof (double))); *valueptr = [number doubleValue]; return valueptr; } +#else + #error Invalid pointer size. +#endif + +// Returns a pointer to the value type, which must be freed using xamarin_free. +// If called multiple times in succession, the returned pointer can be passed as the second ptr argument, and it need only be freed once done iterating. +void *xamarin_nsvalue_to_nsrange (NSValue *value, void *ptr, MonoClass *managedType, guint32 *exception_gchandle) { NSRange *valueptr = (NSRange *) (ptr ? ptr : xamarin_calloc (sizeof (NSRange))); *valueptr = [value rangeValue]; return valueptr; } +#if HAVE_UIKIT // Yep, these CoreGraphics-looking category method is defined in UIKit. +void *xamarin_nsvalue_to_cgaffinetransform (NSValue *value, void *ptr, MonoClass *managedType, guint32 *exception_gchandle) { CGAffineTransform *valueptr = (CGAffineTransform *) (ptr ? ptr : xamarin_calloc (sizeof (CGAffineTransform))); *valueptr = [value CGAffineTransformValue]; return valueptr; } +void *xamarin_nsvalue_to_cgpoint (NSValue *value, void *ptr, MonoClass *managedType, guint32 *exception_gchandle) { CGPoint *valueptr = (CGPoint *) (ptr ? ptr : xamarin_calloc (sizeof (CGPoint))); *valueptr = [value CGPointValue]; return valueptr; } +void *xamarin_nsvalue_to_cgrect (NSValue *value, void *ptr, MonoClass *managedType, guint32 *exception_gchandle) { CGRect *valueptr = (CGRect *) (ptr ? ptr : xamarin_calloc (sizeof (CGRect))); *valueptr = [value CGRectValue]; return valueptr; } +void *xamarin_nsvalue_to_cgsize (NSValue *value, void *ptr, MonoClass *managedType, guint32 *exception_gchandle) { CGSize *valueptr = (CGSize *) (ptr ? ptr : xamarin_calloc (sizeof (CGSize))); *valueptr = [value CGSizeValue]; return valueptr; } +void *xamarin_nsvalue_to_cgvector (NSValue *value, void *ptr, MonoClass *managedType, guint32 *exception_gchandle) { CGVector *valueptr = (CGVector *) (ptr ? ptr : xamarin_calloc (sizeof (CGVector))); *valueptr = [value CGVectorValue]; return valueptr; } +#endif +#if HAVE_COREANIMATION +void *xamarin_nsvalue_to_catransform3d (NSValue *value, void *ptr, MonoClass *managedType, guint32 *exception_gchandle) { CATransform3D *valueptr = (CATransform3D *) (ptr ? ptr : xamarin_calloc (sizeof (CATransform3D))); *valueptr = [value CATransform3DValue]; return valueptr; } +#endif +#if HAVE_MAPKIT // Yep, this is defined in MapKit. +void *xamarin_nsvalue_to_cllocationcoordinate2d (NSValue *value, void *ptr, MonoClass *managedType, guint32 *exception_gchandle) { CLLocationCoordinate2D *valueptr = (CLLocationCoordinate2D *) (ptr ? ptr : xamarin_calloc (sizeof (CLLocationCoordinate2D))); *valueptr = [value MKCoordinateValue]; return valueptr; } +#endif +#if HAVE_COREMEDIA +void *xamarin_nsvalue_to_cmtime (NSValue *value, void *ptr, MonoClass *managedType, guint32 *exception_gchandle) { CMTime *valueptr = (CMTime *) (ptr ? ptr : xamarin_calloc (sizeof (CMTime))); *valueptr = [value CMTimeValue]; return valueptr; } +void *xamarin_nsvalue_to_cmtimemapping (NSValue *value, void *ptr, MonoClass *managedType, guint32 *exception_gchandle) { CMTimeMapping *valueptr = (CMTimeMapping *) (ptr ? ptr : xamarin_calloc (sizeof (CMTimeMapping))); *valueptr = [value CMTimeMappingValue]; return valueptr; } +void *xamarin_nsvalue_to_cmtimerange (NSValue *value, void *ptr, MonoClass *managedType, guint32 *exception_gchandle) { CMTimeRange *valueptr = (CMTimeRange *) (ptr ? ptr : xamarin_calloc (sizeof (CMTimeRange))); *valueptr = [value CMTimeRangeValue]; return valueptr; } +#endif +#if HAVE_MAPKIT +void *xamarin_nsvalue_to_mkcoordinatespan (NSValue *value, void *ptr, MonoClass *managedType, guint32 *exception_gchandle) { MKCoordinateSpan *valueptr = (MKCoordinateSpan *) (ptr ? ptr : xamarin_calloc (sizeof (MKCoordinateSpan))); *valueptr = [value MKCoordinateSpanValue]; return valueptr; } +#endif +void *xamarin_nsvalue_to_scnmatrix4 (NSValue *value, void *ptr, MonoClass *managedType, guint32 *exception_gchandle) { SCNMatrix4 *valueptr = (SCNMatrix4 *) (ptr ? ptr : xamarin_calloc (sizeof (SCNMatrix4))); *valueptr = [value SCNMatrix4Value]; return valueptr; } +void * +xamarin_nsvalue_to_scnvector3 (NSValue *value, void *ptr, MonoClass *managedType, guint32 *exception_gchandle) +{ +#if TARGET_OS_IOS && defined (__arm__) + // In earlier versions of iOS [NSValue SCNVector3Value] would return 4 + // floats. This does not cause problems on 64-bit architectures, because + // the 4 floats end up in floating point registers that doesn't need to be + // preserved. On 32-bit architectures it becomes a real problem though, + // since objc_msgSend_stret will be called, and the return value will be + // written to the stack. Writing 4 floats to the stack, when clang + // allocates 3 bytes, is a bad idea. There's no radar since this has + // already been fixed in iOS, it only affects older versions. + + // So we have to avoid the SCNVector3Value selector on 32-bit + // architectures, since we can't influence how clang generates the call. + // Instead use [NSValue getValue:]. Interestingly enough this function has + // the same bug: it will write 4 floats on 32-bit architectures (and + // amazingly 4 *doubles* on 64-bit architectures - this has been filed as + // radar 33104111), but since we control the input buffer, we can just + // allocate the necessary bytes. And for good measure allocate 32 bytes, + // just to be sure. + + // Just to complicate matters, everything works fine on watchOS because + // armv7k does not use objc_msgSend_stret for this signature, this only + // happens on iOS. + SCNVector3 *valueptr = (SCNVector3 *) xamarin_calloc (32); + [value getValue: valueptr]; + if (ptr) { + memcpy (ptr, valueptr, sizeof (SCNVector3)); + xamarin_free (valueptr); + valueptr = (SCNVector3 *) ptr; + } +#else + SCNVector3 *valueptr = (SCNVector3 *) (ptr ? ptr : xamarin_calloc (sizeof (SCNVector3))); + *valueptr = [value SCNVector3Value]; +#endif + + return valueptr; +} +void *xamarin_nsvalue_to_scnvector4 (NSValue *value, void *ptr, MonoClass *managedType, guint32 *exception_gchandle) { SCNVector4 *valueptr = (SCNVector4 *) (ptr ? ptr : xamarin_calloc (sizeof (SCNVector4))); *valueptr = [value SCNVector4Value]; return valueptr; } +#if HAVE_UIKIT +void *xamarin_nsvalue_to_uiedgeinsets (NSValue *value, void *ptr, MonoClass *managedType, guint32 *exception_gchandle) { UIEdgeInsets *valueptr = (UIEdgeInsets *) (ptr ? ptr : xamarin_calloc (sizeof (UIEdgeInsets))); *valueptr = [value UIEdgeInsetsValue]; return valueptr; } +void *xamarin_nsvalue_to_uioffset (NSValue *value, void *ptr, MonoClass *managedType, guint32 *exception_gchandle) { UIOffset *valueptr = (UIOffset *) (ptr ? ptr : xamarin_calloc (sizeof (UIOffset))); *valueptr = [value UIOffsetValue]; return valueptr; } +#endif + +id xamarin_bool_to_nsnumber (MonoObject *value, guint32 *exception_gchandle) { return [NSNumber numberWithBool: *(BOOL *) mono_object_unbox (value)]; } +id xamarin_sbyte_to_nsnumber (MonoObject *value, guint32 *exception_gchandle) { return [NSNumber numberWithChar: *(int8_t *) mono_object_unbox (value)]; } +id xamarin_byte_to_nsnumber (MonoObject *value, guint32 *exception_gchandle) { return [NSNumber numberWithUnsignedChar: *(uint8_t *) mono_object_unbox (value)]; } +id xamarin_short_to_nsnumber (MonoObject *value, guint32 *exception_gchandle) { return [NSNumber numberWithShort: *(int16_t *) mono_object_unbox (value)]; } +id xamarin_ushort_to_nsnumber (MonoObject *value, guint32 *exception_gchandle) { return [NSNumber numberWithUnsignedShort: *(uint16_t *) mono_object_unbox (value)]; } +id xamarin_int_to_nsnumber (MonoObject *value, guint32 *exception_gchandle) { return [NSNumber numberWithInt: *(int32_t *) mono_object_unbox (value)]; } +id xamarin_uint_to_nsnumber (MonoObject *value, guint32 *exception_gchandle) { return [NSNumber numberWithUnsignedInt: *(uint32_t *) mono_object_unbox (value)]; } +id xamarin_long_to_nsnumber (MonoObject *value, guint32 *exception_gchandle) { return [NSNumber numberWithLongLong: *(int64_t *) mono_object_unbox (value)]; } +id xamarin_ulong_to_nsnumber (MonoObject *value, guint32 *exception_gchandle) { return [NSNumber numberWithUnsignedLongLong: *(uint64_t *) mono_object_unbox (value)]; } +id xamarin_nint_to_nsnumber (MonoObject *value, guint32 *exception_gchandle) { return [NSNumber numberWithInteger: *(NSInteger *) mono_object_unbox (value)]; } +id xamarin_nuint_to_nsnumber (MonoObject *value, guint32 *exception_gchandle) { return [NSNumber numberWithUnsignedInteger: *(NSUInteger *) mono_object_unbox (value)]; } +id xamarin_float_to_nsnumber (MonoObject *value, guint32 *exception_gchandle) { return [NSNumber numberWithFloat: *(float *) mono_object_unbox (value)]; } +id xamarin_double_to_nsnumber (MonoObject *value, guint32 *exception_gchandle) { return [NSNumber numberWithDouble: *(double *) mono_object_unbox (value)]; } +#if __POINTER_WIDTH__ == 32 +id xamarin_nfloat_to_nsnumber (MonoObject *value, guint32 *exception_gchandle) { return [NSNumber numberWithFloat: *(float *) mono_object_unbox (value)]; } +#elif __POINTER_WIDTH__ == 64 +id xamarin_nfloat_to_nsnumber (MonoObject *value, guint32 *exception_gchandle) { return [NSNumber numberWithDouble: *(double *) mono_object_unbox (value)]; } +#else + #error Invalid pointer size. +#endif + +id xamarin_nsrange_to_nsvalue (MonoObject *value, guint32 *exception_gchandle) { return [NSValue valueWithRange: *(NSRange *) mono_object_unbox (value)]; } +#if HAVE_UIKIT // yep, these CoreGraphics-looking category methods are defined in UIKit +id xamarin_cgaffinetransform_to_nsvalue (MonoObject *value, guint32 *exception_gchandle) { return [NSValue valueWithCGAffineTransform: *(CGAffineTransform *) mono_object_unbox (value)]; } +id xamarin_cgpoint_to_nsvalue (MonoObject *value, guint32 *exception_gchandle) { return [NSValue valueWithCGPoint: *(CGPoint *) mono_object_unbox (value)]; } +id xamarin_cgrect_to_nsvalue (MonoObject *value, guint32 *exception_gchandle) { return [NSValue valueWithCGRect: *(CGRect *) mono_object_unbox (value)]; } +id xamarin_cgsize_to_nsvalue (MonoObject *value, guint32 *exception_gchandle) { return [NSValue valueWithCGSize: *(CGSize *) mono_object_unbox (value)]; } +id xamarin_cgvector_to_nsvalue (MonoObject *value, guint32 *exception_gchandle) { return [NSValue valueWithCGVector: *(CGVector *) mono_object_unbox (value)]; } +#endif +#if HAVE_COREANIMATION +id xamarin_catransform3d_to_nsvalue (MonoObject *value, guint32 *exception_gchandle) { return [NSValue valueWithCATransform3D: *(CATransform3D *) mono_object_unbox (value)]; } +#endif +#if HAVE_MAPKIT // Yep, this is defined in MapKit. +id xamarin_cllocationcoordinate2d_to_nsvalue (MonoObject *value, guint32 *exception_gchandle) { return [NSValue valueWithMKCoordinate: *(CLLocationCoordinate2D *) mono_object_unbox (value)]; } +#endif +#if HAVE_COREMEDIA +id xamarin_cmtime_to_nsvalue (MonoObject *value, guint32 *exception_gchandle) { return [NSValue valueWithCMTime: *(CMTime *) mono_object_unbox (value)]; } +id xamarin_cmtimemapping_to_nsvalue (MonoObject *value, guint32 *exception_gchandle) { return [NSValue valueWithCMTimeMapping: *(CMTimeMapping *) mono_object_unbox (value)]; } +id xamarin_cmtimerange_to_nsvalue (MonoObject *value, guint32 *exception_gchandle) { return [NSValue valueWithCMTimeRange: *(CMTimeRange *) mono_object_unbox (value)]; } +#endif +#if HAVE_MAPKIT +id xamarin_mkcoordinatespan_to_nsvalue (MonoObject *value, guint32 *exception_gchandle) { return [NSValue valueWithMKCoordinateSpan: *(MKCoordinateSpan *) mono_object_unbox (value)]; } +#endif +id xamarin_scnmatrix4_to_nsvalue (MonoObject *value, guint32 *exception_gchandle) { return [NSValue valueWithSCNMatrix4: *(SCNMatrix4 *) mono_object_unbox (value)]; } +id xamarin_scnvector3_to_nsvalue (MonoObject *value, guint32 *exception_gchandle) { return [NSValue valueWithSCNVector3: *(SCNVector3 *) mono_object_unbox (value)]; } +id xamarin_scnvector4_to_nsvalue (MonoObject *value, guint32 *exception_gchandle) { return [NSValue valueWithSCNVector4: *(SCNVector4 *) mono_object_unbox (value)]; } +#if HAVE_UIKIT +id xamarin_uiedgeinsets_to_nsvalue (MonoObject *value, guint32 *exception_gchandle) { return [NSValue valueWithUIEdgeInsets: *(UIEdgeInsets *) mono_object_unbox (value)]; } +id xamarin_uioffset_to_nsvalue (MonoObject *value, guint32 *exception_gchandle) { return [NSValue valueWithUIOffset: *(UIOffset *) mono_object_unbox (value)]; } +#endif + +static void * +xamarin_get_nsnumber_converter (MonoClass *managedType, MonoMethod *method, bool to_managed, guint32 *exception_gchandle) +{ + void * func = NULL; + char *fullname = xamarin_class_get_full_name (managedType, exception_gchandle); + if (*exception_gchandle != 0) + goto exception_handling; + + if (!strcmp (fullname, "System.SByte")) { + func = to_managed ? (void *) xamarin_nsnumber_to_sbyte : (void *) xamarin_sbyte_to_nsnumber; + } else if (!strcmp (fullname, "System.Byte")) { + func = to_managed ? (void *) xamarin_nsnumber_to_byte : (void *) xamarin_byte_to_nsnumber; + } else if (!strcmp (fullname, "System.Int16")) { + func = to_managed ? (void *) xamarin_nsnumber_to_short : (void *) xamarin_short_to_nsnumber; + } else if (!strcmp (fullname, "System.UInt16")) { + func = to_managed ? (void *) xamarin_nsnumber_to_ushort : (void *) xamarin_ushort_to_nsnumber; + } else if (!strcmp (fullname, "System.Int32")) { + func = to_managed ? (void *) xamarin_nsnumber_to_int : (void *) xamarin_int_to_nsnumber; + } else if (!strcmp (fullname, "System.UInt32")) { + func = to_managed ? (void *) xamarin_nsnumber_to_uint : (void *) xamarin_uint_to_nsnumber; + } else if (!strcmp (fullname, "System.Int64")) { + func = to_managed ? (void *) xamarin_nsnumber_to_long : (void *) xamarin_long_to_nsnumber; + } else if (!strcmp (fullname, "System.UInt64")) { + func = to_managed ? (void *) xamarin_nsnumber_to_ulong : (void *) xamarin_ulong_to_nsnumber; + } else if (!strcmp (fullname, "System.nint")) { + func = to_managed ? (void *) xamarin_nsnumber_to_nint : (void *) xamarin_nint_to_nsnumber; + } else if (!strcmp (fullname, "System.nuint")) { + func = to_managed ? (void *) xamarin_nsnumber_to_nuint : (void *) xamarin_nuint_to_nsnumber; + } else if (!strcmp (fullname, "System.Single")) { + func = to_managed ? (void *) xamarin_nsnumber_to_float : (void *) xamarin_float_to_nsnumber; + } else if (!strcmp (fullname, "System.Double")) { + func = to_managed ? (void *) xamarin_nsnumber_to_double : (void *) xamarin_double_to_nsnumber; + } else if (!strcmp (fullname, "System.nfloat")) { + func = to_managed ? (void *) xamarin_nsnumber_to_nfloat : (void *) xamarin_nfloat_to_nsnumber; + } else if (!strcmp (fullname, "System.Boolean")) { + func = to_managed ? (void *) xamarin_nsnumber_to_bool : (void *) xamarin_bool_to_nsnumber; + } else { + *exception_gchandle = xamarin_create_bindas_exception (mono_class_get_type (managedType), mono_class_get_type (xamarin_get_nsnumber_class ()), method); + goto exception_handling; + } + +exception_handling: + xamarin_free (fullname); + + return func; +} + +static void * +xamarin_get_nsvalue_converter (MonoClass *managedType, MonoMethod *method, bool to_managed, guint32 *exception_gchandle) +{ + void * func = NULL; + char *fullname = xamarin_class_get_full_name (managedType, exception_gchandle); + if (*exception_gchandle != 0) + goto exception_handling; + +#if MONOMAC + if (xamarin_use_new_assemblies && !strncmp (fullname, "MonoMac.", 8)) { + char *tmp_to_name = xamarin_strdup_printf ("%s", fullname + 8); + xamarin_free (fullname); + fullname = tmp_to_name; + } +#endif + + if (!strcmp (fullname, "Foundation.NSRange")) { + func = to_managed ? (void *) xamarin_nsvalue_to_nsrange : (void *) xamarin_nsrange_to_nsvalue; +#if HAVE_UIKIT // yep, these CoreGraphics-looking category methods are defined in UIKit + } else if (!strcmp (fullname, "CoreGraphics.CGAffineTransform")) { + func = to_managed ? (void *) xamarin_nsvalue_to_cgaffinetransform : (void *) xamarin_cgaffinetransform_to_nsvalue; + } else if (!strcmp (fullname, "CoreGraphics.CGPoint")) { + func = to_managed ? (void *) xamarin_nsvalue_to_cgpoint : (void *) xamarin_cgpoint_to_nsvalue; + } else if (!strcmp (fullname, "CoreGraphics.CGRect")) { + func = to_managed ? (void *) xamarin_nsvalue_to_cgrect : (void *) xamarin_cgrect_to_nsvalue; + } else if (!strcmp (fullname, "CoreGraphics.CGSize")) { + func = to_managed ? (void *) xamarin_nsvalue_to_cgsize : (void *) xamarin_cgsize_to_nsvalue; + } else if (!strcmp (fullname, "CoreGraphics.CGVector")) { + func = to_managed ? (void *) xamarin_nsvalue_to_cgvector : (void *) xamarin_cgvector_to_nsvalue; +#endif +#if HAVE_COREANIMATION + } else if (!strcmp (fullname, "CoreAnimation.CATransform3D")) { + func = to_managed ? (void *) xamarin_nsvalue_to_catransform3d : (void *) xamarin_catransform3d_to_nsvalue; +#endif +#if HAVE_MAPKIT // Yep, this is defined in MapKit. + } else if (!strcmp (fullname, "CoreLocation.CLLocationCoordinate2D")) { + func = to_managed ? (void *) xamarin_nsvalue_to_cllocationcoordinate2d : (void *) xamarin_cllocationcoordinate2d_to_nsvalue; +#endif +#if HAVE_COREMEDIA + } else if (!strcmp (fullname, "CoreMedia.CMTime")) { + func = to_managed ? (void *) xamarin_nsvalue_to_cmtime : (void *) xamarin_cmtime_to_nsvalue; + } else if (!strcmp (fullname, "CoreMedia.CMTimeMapping")) { + func = to_managed ? (void *) xamarin_nsvalue_to_cmtimemapping : (void *) xamarin_cmtimemapping_to_nsvalue; + } else if (!strcmp (fullname, "CoreMedia.CMTimeRange")) { + func = to_managed ? (void *) xamarin_nsvalue_to_cmtimerange : (void *) xamarin_cmtimerange_to_nsvalue; +#endif +#if HAVE_MAPKIT + } else if (!strcmp (fullname, "MapKit.MKCoordinateSpan")) { + func = to_managed ? (void *) xamarin_nsvalue_to_mkcoordinatespan : (void *) xamarin_mkcoordinatespan_to_nsvalue; +#endif + } else if (!strcmp (fullname, "SceneKit.SCNMatrix4")) { + func = to_managed ? (void *) xamarin_nsvalue_to_scnmatrix4 : (void *) xamarin_scnmatrix4_to_nsvalue; + } else if (!strcmp (fullname, "SceneKit.SCNVector3")) { + func = to_managed ? (void *) xamarin_nsvalue_to_scnvector3 : (void *) xamarin_scnvector3_to_nsvalue; + } else if (!strcmp (fullname, "SceneKit.SCNVector4")) { + func = to_managed ? (void *) xamarin_nsvalue_to_scnvector4 : (void *) xamarin_scnvector4_to_nsvalue; +#if HAVE_UIKIT + } else if (!strcmp (fullname, "UIKit.UIEdgeInsets")) { + func = to_managed ? (void *) xamarin_nsvalue_to_uiedgeinsets : (void *) xamarin_uiedgeinsets_to_nsvalue; + } else if (!strcmp (fullname, "UIKit.UIOffset")) { + func = to_managed ? (void *) xamarin_nsvalue_to_uioffset : (void *) xamarin_uioffset_to_nsvalue; +#endif + } else { + *exception_gchandle = xamarin_create_bindas_exception (mono_class_get_type (managedType), mono_class_get_type (xamarin_get_nsvalue_class ()), method); + goto exception_handling; + } + +exception_handling: + xamarin_free (fullname); + + return func; +} + +xamarin_id_to_managed_func +xamarin_get_nsnumber_to_managed_func (MonoClass *managedType, MonoMethod *method, guint32 *exception_gchandle) +{ + return (xamarin_id_to_managed_func) xamarin_get_nsnumber_converter (managedType, method, true, exception_gchandle); +} + +xamarin_managed_to_id_func +xamarin_get_managed_to_nsnumber_func (MonoClass *managedType, MonoMethod *method, guint32 *exception_gchandle) +{ + return (xamarin_managed_to_id_func) xamarin_get_nsnumber_converter (managedType, method, false, exception_gchandle); +} + +xamarin_id_to_managed_func +xamarin_get_nsvalue_to_managed_func (MonoClass *managedType, MonoMethod *method, guint32 *exception_gchandle) +{ + return (xamarin_id_to_managed_func) xamarin_get_nsvalue_converter (managedType, method, true, exception_gchandle); +} + +xamarin_managed_to_id_func +xamarin_get_managed_to_nsvalue_func (MonoClass *managedType, MonoMethod *method, guint32 *exception_gchandle) +{ + return (xamarin_managed_to_id_func) xamarin_get_nsvalue_converter (managedType, method, false, exception_gchandle); +} + +void * +xamarin_smart_enum_to_nsstring (MonoObject *value, guint32 *exception_gchandle) +{ + int handle = mono_gchandle_new (value, FALSE); + NSString *rv = xamarin_convert_smart_enum_to_nsstring (GINT_TO_POINTER (handle), exception_gchandle); + mono_gchandle_free (handle); + return rv; +} + +void * +xamarin_nsstring_to_smart_enum (id value, void *ptr, MonoClass *managedType, guint32 *exception_gchandle) +{ + void *rv = xamarin_convert_nsstring_to_smart_enum (value, mono_type_get_object (mono_domain_get (), mono_class_get_type (managedType)), exception_gchandle); + if (*exception_gchandle != 0) + return ptr; + int handle = GPOINTER_TO_INT (rv); + int size = mono_class_value_size (managedType, NULL); + if (!ptr) + ptr = xamarin_calloc (size); + void *value_ptr = mono_object_unbox (mono_gchandle_get_target (handle)); + memcpy (ptr, value_ptr, size); + mono_gchandle_free (handle); + return ptr; +} + +xamarin_id_to_managed_func +xamarin_get_nsstring_to_smart_enum_func (MonoClass *managedType, MonoMethod *method, guint32 *exception_gchandle) +{ + return xamarin_nsstring_to_smart_enum; +} + +xamarin_managed_to_id_func +xamarin_get_smart_enum_to_nsstring_func (MonoClass *managedType, MonoMethod *method, guint32 *exception_gchandle) +{ + return (xamarin_managed_to_id_func) xamarin_smart_enum_to_nsstring; +} + +NSArray * +xamarin_convert_managed_to_nsarray_with_func (MonoArray *array, xamarin_managed_to_id_func convert, guint32 *exception_gchandle) +{ + id *buf = NULL; + NSArray *rv = NULL; + + if (array == NULL) + return NULL; + + int length = mono_array_length (array); + if (length == 0) + return [NSArray array]; + + buf = (id *) malloc (sizeof (id) * length); + MonoClass *element_class = mono_class_get_element_class (mono_object_get_class ((MonoObject *) array)); + int element_size = mono_class_value_size (element_class, NULL); + char *ptr = (char *) mono_array_addr_with_size (array, element_size, 0); + for (int i = 0; i < length; i++) { + MonoObject *value = mono_value_box (mono_domain_get (), element_class, ptr + element_size * i); + buf [i] = convert (value, exception_gchandle); + if (*exception_gchandle != 0) + goto exception_handling; + } + rv = [NSArray arrayWithObjects: buf count: length]; + +exception_handling: + free (buf); + + return rv; +} + +MonoArray * +xamarin_convert_nsarray_to_managed_with_func (NSArray *array, MonoClass *managedElementType, xamarin_id_to_managed_func convert, guint32 *exception_gchandle) +{ + if (array == NULL) + return NULL; + + int length = [array count]; + MonoArray *rv = mono_array_new (mono_domain_get (), managedElementType, length); + + if (length == 0) + return rv; + + void *valueptr = NULL; + int element_size = mono_class_value_size (managedElementType, NULL); + char *ptr = (char *) mono_array_addr_with_size (rv, element_size, 0); + for (int i = 0; i < length; i++) { + valueptr = convert ([array objectAtIndex: i], valueptr, managedElementType, exception_gchandle); + if (*exception_gchandle != 0) + goto exception_handling; + memcpy (ptr, valueptr, element_size); + ptr += element_size; + } + +exception_handling: + xamarin_free (valueptr); + + return rv; +} + +NSNumber * +xamarin_convert_managed_to_nsnumber (MonoObject *value, MonoClass *managedType, MonoMethod *method, guint32 *exception_gchandle) +{ + xamarin_managed_to_id_func convert = xamarin_get_managed_to_nsnumber_func (managedType, method, exception_gchandle); + if (*exception_gchandle != 0) + return NULL; + + return convert (value, exception_gchandle); +} + +NSValue * +xamarin_convert_managed_to_nsvalue (MonoObject *value, MonoClass *managedType, MonoMethod *method, guint32 *exception_gchandle) +{ + xamarin_managed_to_id_func convert = xamarin_get_managed_to_nsvalue_func (managedType, method, exception_gchandle); + if (*exception_gchandle != 0) + return NULL; + + return convert (value, exception_gchandle); +} + +guint32 +xamarin_create_bindas_exception (MonoType *inputType, MonoType *outputType, MonoMethod *method) +{ + guint32 exception_gchandle; + char *to_name = NULL; + char *from_name = NULL; + char *method_full_name = NULL; + char *msg = NULL; + + from_name = xamarin_type_get_full_name (inputType, &exception_gchandle); + if (exception_gchandle != 0) + goto exception_handling; + to_name = xamarin_type_get_full_name (outputType, &exception_gchandle); + if (exception_gchandle != 0) + goto exception_handling; + + method_full_name = mono_method_full_name (method, TRUE); + msg = xamarin_strdup_printf ("Internal error: can't convert from '%s' to '%s' in %s. Please file a bug report with a test case (https://bugzilla.xamarin.com).", + from_name, to_name, method_full_name); + exception_gchandle = mono_gchandle_new ((MonoObject *) xamarin_create_exception (msg), false); + +exception_handling: + xamarin_free (to_name); + xamarin_free (from_name); + xamarin_free (method_full_name); + xamarin_free (msg); + return exception_gchandle; +} diff --git a/runtime/xamarin/runtime.h b/runtime/xamarin/runtime.h index 32affb4bee21..e4b6b47ed50f 100644 --- a/runtime/xamarin/runtime.h +++ b/runtime/xamarin/runtime.h @@ -83,9 +83,19 @@ struct MTRegistrationMap { int full_token_reference_count; }; +typedef struct { + MonoReflectionType *original_type; +} BindAsData; + typedef struct { MonoReflectionMethod *method; int32_t semantic; + int32_t bindas_count; // The number of elements available in the bindas_types array. + // An array of BindAs original types. Element 0 is for the return value, + // the rest are for parameters (parameters start at 1 even for void methods). + // The array must contain space for the return value and all the parameters, + // even for those that don't have BindAs attributes (the original_type entry will be NULL). + BindAsData bindas[]; } MethodDescription; // This has a managed equivalent in NSObject2.cs @@ -122,6 +132,9 @@ int xamarin_objc_type_size (const char *type); bool xamarin_is_class_nsobject (MonoClass *cls); bool xamarin_is_class_inativeobject (MonoClass *cls); bool xamarin_is_class_array (MonoClass *cls); +bool xamarin_is_class_nsnumber (MonoClass *cls); +bool xamarin_is_class_nsvalue (MonoClass *cls); +bool xamarin_is_class_nsstring (MonoClass *cls); MonoType * xamarin_get_parameter_type (MonoMethod *managed_method, int index); MonoObject * xamarin_get_nsobject_with_type_for_ptr (id self, bool owns, MonoType* type, guint32 *exception_gchandle); MonoObject * xamarin_get_nsobject_with_type_for_ptr_created (id self, bool owns, MonoType *type, int32_t *created, guint32 *exception_gchandle); @@ -136,6 +149,7 @@ void xamarin_rethrow_managed_exception (guint32 original_gchandle, guint32 *ex MonoException * xamarin_create_exception (const char *msg); id xamarin_get_handle (MonoObject *obj, guint32 *exception_gchandle); char * xamarin_strdup_printf (const char *msg, ...); +void * xamarin_calloc (size_t size); void xamarin_free (void *ptr); MonoMethod * xamarin_get_reflection_method_method (MonoReflectionMethod *method); void xamarin_framework_peer_lock (); @@ -182,6 +196,8 @@ void xamarin_throw_product_exception (int code, const char *message); NSString * xamarin_print_all_exceptions (MonoObject *exc); id xamarin_invoke_objc_method_implementation (id self, SEL sel, IMP xamarin_impl); +MonoClass * xamarin_get_nsnumber_class (); +MonoClass * xamarin_get_nsvalue_class (); bool xamarin_is_managed_exception_marshaling_disabled (); @@ -225,7 +241,7 @@ MonoObject* xamarin_get_class (Class ptr, guint32 *exception_gchandle) MonoObject* xamarin_get_selector (SEL ptr, guint32 *exception_gchandle); Class xamarin_get_class_handle (MonoObject *obj, guint32 *exception_gchandle); SEL xamarin_get_selector_handle (MonoObject *obj, guint32 *exception_gchandle); -MethodDescription xamarin_get_method_for_selector (Class cls, SEL sel, bool is_static, guint32 *exception_gchandle); +void xamarin_get_method_for_selector (Class cls, SEL sel, bool is_static, MethodDescription *desc, guint32 *exception_gchandle); bool xamarin_has_nsobject (id obj, guint32 *exception_gchandle); MonoObject* xamarin_get_nsobject (id obj, guint32 *exception_gchandle); id xamarin_get_handle_for_inativeobject (MonoObject *obj, guint32 *exception_gchandle); @@ -239,7 +255,7 @@ MonoObject* xamarin_get_nsobject_with_type (id obj, void *type, int32_t * void xamarin_dispose (MonoObject *mobj, guint32 *exception_gchandle); bool xamarin_is_parameter_transient (MonoReflectionMethod *method, int parameter /* 0-based */, guint32 *exception_gchandle); bool xamarin_is_parameter_out (MonoReflectionMethod *method, int parameter /* 0-based */, guint32 *exception_gchandle); -MethodDescription xamarin_get_method_and_object_for_selector (Class cls, SEL sel, bool is_static, id self, MonoObject **mthis, guint32 *exception_gchandle); +void xamarin_get_method_and_object_for_selector (Class cls, SEL sel, bool is_static, id self, MonoObject **mthis, MethodDescription *desc, guint32 *exception_gchandle); guint32 xamarin_create_product_exception_for_error (int code, const char *message, guint32 *exception_gchandle); #ifdef __cplusplus diff --git a/runtime/xamarin/trampolines.h b/runtime/xamarin/trampolines.h index b963943ebacf..19c6a680009b 100644 --- a/runtime/xamarin/trampolines.h +++ b/runtime/xamarin/trampolines.h @@ -47,6 +47,107 @@ enum ArgumentSemantic /* Xcode 4.4 doesn't like this ': int' */ { ArgumentSemanticCategoryInstance = 1 << 11, }; +/* Conversion functions */ + +// Function to convert from id to managed. If `ptr` is passed, the value type +// will be stored in this pointer. Otherwise a memory is allocated, and the +// return value must be freed using xamarin_free. +// Returns: a pointer to the value type. In case of an exception, 'ptr' is returned (and no memory allocated in any circumstances). +typedef void * (*xamarin_id_to_managed_func) (id value, void *ptr, MonoClass *managedType, guint32 *exception_gchandle); +// Function to convert from managed to id. +typedef id (*xamarin_managed_to_id_func) (MonoObject *value, guint32 *exception_gchandle); + +id xamarin_generate_conversion_to_native (MonoObject *value, MonoType *inputType, MonoType *outputType, MonoMethod *method, guint32 *exception_gchandle); +void * xamarin_generate_conversion_to_managed (id value, MonoType *inputType, MonoType *outputType, MonoMethod *method, guint32 *exception_gchandle, /*SList*/ void **free_list); +NSNumber * xamarin_convert_managed_to_nsnumber (MonoObject *value, MonoType *managedType, MonoType *nativeType, MonoMethod *method, guint32 *exception_gchandle); +NSValue * xamarin_convert_managed_to_nsvalue (MonoObject *value, MonoType *managedType, MonoType *nativeType, MonoMethod *method, guint32 *exception_gchandle); +NSString * xamarin_convert_managed_to_nsstring (MonoObject *value, MonoType *managedType, MonoType *nativeType, MonoMethod *method, guint32 *exception_gchandle); +MonoObject * xamarin_convert_nsnumber_to_managed (NSNumber *value, MonoType *nativeType, MonoType *managedType, MonoMethod *method, guint32 *exception_gchandle); +MonoObject * xamarin_convert_nsvalue_to_managed (NSValue *value, MonoType *nativeType, MonoType *managedType, MonoMethod *method, guint32 *exception_gchandle); +MonoObject * xamarin_convert_nsstring_to_managed (NSString *value, MonoType *nativeType, MonoType *managedType, MonoMethod *method, guint32 *exception_gchandle); +guint32 xamarin_create_bindas_exception (MonoType *inputType, MonoType *outputType, MonoMethod *method); + +xamarin_id_to_managed_func xamarin_get_nsnumber_to_managed_func (MonoClass *managedType, MonoMethod *method, guint32 *exception_gchandle); +xamarin_managed_to_id_func xamarin_get_managed_to_nsnumber_func (MonoClass *managedType, MonoMethod *method, guint32 *exception_gchandle); + +xamarin_id_to_managed_func xamarin_get_nsvalue_to_managed_func (MonoClass *managedType, MonoMethod *method, guint32 *exception_gchandle); +xamarin_managed_to_id_func xamarin_get_managed_to_nsvalue_func (MonoClass *managedType, MonoMethod *method, guint32 *exception_gchandle); + +xamarin_id_to_managed_func xamarin_get_nsstring_to_smart_enum_func (MonoClass *managedType, MonoMethod *method, guint32 *exception_gchandle); +xamarin_managed_to_id_func xamarin_get_smart_enum_to_nsstring_func (MonoClass *managedType, MonoMethod *method, guint32 *exception_gchandle); + +NSArray * xamarin_convert_managed_to_nsarray_with_func (MonoArray *array, xamarin_managed_to_id_func convert, guint32 *exception_gchandle); +MonoArray * xamarin_convert_nsarray_to_managed_with_func (NSArray *array, MonoClass *managedElementType, xamarin_id_to_managed_func convert, guint32 *exception_gchandle); + +// Returns a pointer to the value type, which must be freed using xamarin_free. +void *xamarin_nsnumber_to_bool (NSNumber *number, void *ptr, MonoClass *managedType, guint32 *exception_gchandle); +void *xamarin_nsnumber_to_sbyte (NSNumber *number, void *ptr, MonoClass *managedType, guint32 *exception_gchandle); +void *xamarin_nsnumber_to_byte (NSNumber *number, void *ptr, MonoClass *managedType, guint32 *exception_gchandle); +void *xamarin_nsnumber_to_short (NSNumber *number, void *ptr, MonoClass *managedType, guint32 *exception_gchandle); +void *xamarin_nsnumber_to_ushort (NSNumber *number, void *ptr, MonoClass *managedType, guint32 *exception_gchandle); +void *xamarin_nsnumber_to_int (NSNumber *number, void *ptr, MonoClass *managedType, guint32 *exception_gchandle); +void *xamarin_nsnumber_to_uint (NSNumber *number, void *ptr, MonoClass *managedType, guint32 *exception_gchandle); +void *xamarin_nsnumber_to_long (NSNumber *number, void *ptr, MonoClass *managedType, guint32 *exception_gchandle); +void *xamarin_nsnumber_to_ulong (NSNumber *number, void *ptr, MonoClass *managedType, guint32 *exception_gchandle); +void *xamarin_nsnumber_to_nint (NSNumber *number, void *ptr, MonoClass *managedType, guint32 *exception_gchandle); +void *xamarin_nsnumber_to_nuint (NSNumber *number, void *ptr, MonoClass *managedType, guint32 *exception_gchandle); +void *xamarin_nsnumber_to_float (NSNumber *number, void *ptr, MonoClass *managedType, guint32 *exception_gchandle); +void *xamarin_nsnumber_to_double (NSNumber *number, void *ptr, MonoClass *managedType, guint32 *exception_gchandle); +void *xamarin_nsnumber_to_nfloat (NSNumber *number, void *ptr, MonoClass *managedType, guint32 *exception_gchandle); + +// Returns a pointer to the value type, which must be freed using xamarin_free +void *xamarin_nsvalue_to_nsrange (NSValue *value, void *ptr, MonoClass *managedType, guint32 *exception_ghandle); +void *xamarin_nsvalue_to_cgaffinetransform (NSValue *value, void *ptr, MonoClass *managedType, guint32 *exception_ghandle); +void *xamarin_nsvalue_to_cgpoint (NSValue *value, void *ptr, MonoClass *managedType, guint32 *exception_ghandle); +void *xamarin_nsvalue_to_cgrect (NSValue *value, void *ptr, MonoClass *managedType, guint32 *exception_ghandle); +void *xamarin_nsvalue_to_cgsize (NSValue *value, void *ptr, MonoClass *managedType, guint32 *exception_ghandle); +void *xamarin_nsvalue_to_cgvector (NSValue *value, void *ptr, MonoClass *managedType, guint32 *exception_ghandle); +void *xamarin_nsvalue_to_catransform3d (NSValue *value, void *ptr, MonoClass *managedType, guint32 *exception_ghandle); +void *xamarin_nsvalue_to_cllocationcoordinate2d (NSValue *value, void *ptr, MonoClass *managedType, guint32 *exception_ghandle); +void *xamarin_nsvalue_to_cmtime (NSValue *value, void *ptr, MonoClass *managedType, guint32 *exception_ghandle); +void *xamarin_nsvalue_to_cmtimemapping (NSValue *value, void *ptr, MonoClass *managedType, guint32 *exception_ghandle); +void *xamarin_nsvalue_to_cmtimerange (NSValue *value, void *ptr, MonoClass *managedType, guint32 *exception_ghandle); +void *xamarin_nsvalue_to_mkcoordinatespan (NSValue *value, void *ptr, MonoClass *managedType, guint32 *exception_ghandle); +void *xamarin_nsvalue_to_scnmatrix4 (NSValue *value, void *ptr, MonoClass *managedType, guint32 *exception_ghandle); +void *xamarin_nsvalue_to_scnvector3 (NSValue *value, void *ptr, MonoClass *managedType, guint32 *exception_ghandle); +void *xamarin_nsvalue_to_scnvector4 (NSValue *value, void *ptr, MonoClass *managedType, guint32 *exception_ghandle); +void *xamarin_nsvalue_to_uiedgeinsets (NSValue *value, void *ptr, MonoClass *managedType, guint32 *exception_ghandle); +void *xamarin_nsvalue_to_uioffset (NSValue *value, void *ptr, MonoClass *managedType, guint32 *exception_ghandle); + +id xamarin_bool_to_nsnumber (MonoObject *value, guint32 *exception_gchandle); +id xamarin_sbyte_to_nsnumber (MonoObject *value, guint32 *exception_gchandle); +id xamarin_byte_to_nsnumber (MonoObject *value, guint32 *exception_gchandle); +id xamarin_short_to_nsnumber (MonoObject *value, guint32 *exception_gchandle); +id xamarin_ushort_to_nsnumber (MonoObject *value, guint32 *exception_gchandle); +id xamarin_int_to_nsnumber (MonoObject *value, guint32 *exception_gchandle); +id xamarin_uint_to_nsnumber (MonoObject *value, guint32 *exception_gchandle); +id xamarin_long_to_nsnumber (MonoObject *value, guint32 *exception_gchandle); +id xamarin_ulong_to_nsnumber (MonoObject *value, guint32 *exception_gchandle); +id xamarin_nint_to_nsnumber (MonoObject *value, guint32 *exception_gchandle); +id xamarin_nuint_to_nsnumber (MonoObject *value, guint32 *exception_gchandle); +id xamarin_float_to_nsnumber (MonoObject *value, guint32 *exception_gchandle); +id xamarin_double_to_nsnumber (MonoObject *value, guint32 *exception_gchandle); +id xamarin_nfloat_to_nsnumber (MonoObject *value, guint32 *exception_gchandle); +id xamarin_nfloat_to_nsnumber (MonoObject *value, guint32 *exception_gchandle); + +id xamarin_nsrange_to_nsvalue (MonoObject *value, guint32 *exception_gchandle); +id xamarin_cgaffinetransform_to_nsvalue (MonoObject *value, guint32 *exception_gchandle); +id xamarin_cgpoint_to_nsvalue (MonoObject *value, guint32 *exception_gchandle); +id xamarin_cgrect_to_nsvalue (MonoObject *value, guint32 *exception_gchandle); +id xamarin_cgsize_to_nsvalue (MonoObject *value, guint32 *exception_gchandle); +id xamarin_cgvector_to_nsvalue (MonoObject *value, guint32 *exception_gchandle); +id xamarin_catransform3d_to_nsvalue (MonoObject *value, guint32 *exception_gchandle); +id xamarin_cllocationcoordinate2d_to_nsvalue (MonoObject *value, guint32 *exception_gchandle); +id xamarin_cmtime_to_nsvalue (MonoObject *value, guint32 *exception_gchandle); +id xamarin_cmtimemapping_to_nsvalue (MonoObject *value, guint32 *exception_gchandle); +id xamarin_cmtimerange_to_nsvalue (MonoObject *value, guint32 *exception_gchandle); +id xamarin_mkcoordinatespan_to_nsvalue (MonoObject *value, guint32 *exception_gchandle); +id xamarin_scnmatrix4_to_nsvalue (MonoObject *value, guint32 *exception_gchandle); +id xamarin_scnvector3_to_nsvalue (MonoObject *value, guint32 *exception_gchandle); +id xamarin_scnvector4_to_nsvalue (MonoObject *value, guint32 *exception_gchandle); +id xamarin_uiedgeinsets_to_nsvalue (MonoObject *value, guint32 *exception_gchandle); +id xamarin_uioffset_to_nsvalue (MonoObject *value, guint32 *exception_gchandle); + /* Copied from SGen */ static inline void diff --git a/src/ObjCRuntime/DynamicRegistrar.cs b/src/ObjCRuntime/DynamicRegistrar.cs index b10d69020e8d..21c923cce369 100644 --- a/src/ObjCRuntime/DynamicRegistrar.cs +++ b/src/ObjCRuntime/DynamicRegistrar.cs @@ -178,6 +178,27 @@ public void RegisterMethod (Type type, MethodInfo minfo, ExportAttribute ea) throw exceptions.Count == 1 ? exceptions [0] : new AggregateException (exceptions); } + protected override IEnumerable FindMethods (Type type, string name) + { + foreach (var method in type.GetMethods (BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static | BindingFlags.Instance | BindingFlags.DeclaredOnly)) + if (method.Name == name) + yield return method; + } + + protected override PropertyInfo FindProperty (Type type, string name) + { + return type.GetProperty (name, BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static | BindingFlags.Instance | BindingFlags.DeclaredOnly); + } + + public override Type FindType (Type relative, string @namespace, string name) + { + foreach (var type in relative.Assembly.GetTypes ()) { + if (type.Namespace == @namespace && type.Name == name) + return type; + } + return null; + } + protected override int GetValueTypeSize (Type type) { return Marshal.SizeOf (type); @@ -209,6 +230,54 @@ protected override IEnumerable CollectTypes (Assembly assembly) return assembly.GetTypes (); } + protected override BindAsAttribute GetBindAsAttribute (PropertyInfo property) + { + return property?.GetCustomAttribute (false); + } + + protected override BindAsAttribute GetBindAsAttribute (MethodBase method, int parameter_index) + { + ICustomAttributeProvider provider; + + if (method == null) + return null; + + var minfo = method as MethodInfo; + if (minfo != null) { + minfo = minfo.GetBaseDefinition (); + if (parameter_index == -1) { + provider = minfo.ReturnTypeCustomAttributes; + } else { + provider = minfo.GetParameters () [parameter_index]; + } + } else { + var cinfo = method as ConstructorInfo; + if (parameter_index == -1) { + throw new Exception (); + } else { + provider = cinfo.GetParameters () [parameter_index]; + } + } + + var attribs = provider.GetCustomAttributes (typeof (BindAsAttribute), false); + if (attribs.Length == 0) + return null; + + if (attribs.Length != 1) + throw new AmbiguousMatchException (/* FIXME */); + + return (BindAsAttribute) attribs [0]; + } + + protected override Type GetNullableType (Type type) + { + if (!type.IsGenericType) + return null; + if (type.GetGenericTypeDefinition () != typeof (Nullable<>)) + return null; + return type.GetGenericArguments () [0]; + } + protected override ConnectAttribute GetConnectAttribute (PropertyInfo property) { return SharedDynamic.GetOneAttribute (property); @@ -500,9 +569,14 @@ protected override bool HasModelAttribute (Type type) return type.IsDefined (typeof (ModelAttribute), false); } - protected override bool IsArray (Type type) + protected override bool IsArray (Type type, out int rank) { - return type.IsArray; + if (!type.IsArray) { + rank = 0; + return false; + } + rank = type.GetArrayRank (); + return true; } protected override bool IsByRef (Type type) @@ -535,6 +609,14 @@ protected override bool IsDelegate (Type type) return type.IsSubclassOf (typeof (System.Delegate)); } + protected override bool IsNullable (Type type) + { + if (!type.IsGenericType) + return false; + + return type.GetGenericTypeDefinition () == typeof (Nullable<>); + } + protected override bool IsEnum (Type type, out bool isNativeEnum) { isNativeEnum = false; @@ -809,25 +891,25 @@ public void AddCustomType (Type type) custom_type_map [type] = null; } - public UnmanagedMethodDescription GetMethodDescriptionAndObject (Type type, IntPtr selector, bool is_static, IntPtr obj, ref IntPtr mthis) + public void GetMethodDescriptionAndObject (Type type, IntPtr selector, bool is_static, IntPtr obj, ref IntPtr mthis, IntPtr desc) { var sel = new Selector (selector); var res = GetMethodNoThrow (type, type, sel.Name, is_static); if (res == null) throw ErrorHelper.CreateError (8006, "Failed to find the selector '{0}' on the type '{1}'", sel.Name, type.FullName); - var md = res.MethodDescription; - - if (md.IsInstanceCategory) { + if (res.IsInstanceCategory) { mthis = IntPtr.Zero; } else { var nsobj = Runtime.GetNSObject (obj, Runtime.MissingCtorResolution.ThrowConstructor1NotFound, true); mthis = ObjectWrapper.Convert (nsobj); - if (res.Method.ContainsGenericParameters) - return new MethodDescription (FindClosedMethod (nsobj.GetType (), res.Method), res.ArgumentSemantic).GetUnmanagedDescription (); + if (res.Method.ContainsGenericParameters) { + res.WriteUnmanagedDescription (desc, FindClosedMethod (nsobj.GetType (), res.Method)); + return; + } } - return md.GetUnmanagedDescription (); + res.WriteUnmanagedDescription (desc); } internal static MethodInfo FindClosedMethod (Type closed_type, MethodBase open_method) @@ -856,7 +938,7 @@ internal static MethodInfo FindClosedMethod (Type closed_type, MethodBase open_m throw ErrorHelper.CreateError (8003, "Failed to find the closed generic method '{0}' on the type '{1}'.", open_method.Name, closed_type.FullName); } - public UnmanagedMethodDescription GetMethodDescription (Type type, IntPtr selector, bool is_static) + public void GetMethodDescription (Type type, IntPtr selector, bool is_static, IntPtr desc) { var sel = new Selector (selector); var res = GetMethodNoThrow (type, type, sel.Name, is_static); @@ -865,7 +947,7 @@ public UnmanagedMethodDescription GetMethodDescription (Type type, IntPtr select if (type.IsGenericType && res.Method is ConstructorInfo) throw ErrorHelper.CreateError (4133, "Cannot construct an instance of the type '{0}' from Objective-C because the type is generic.", type.FullName); - return res.MethodDescription.GetUnmanagedDescription (); + res.WriteUnmanagedDescription (desc); } ObjCMethod GetMethodNoThrow (Type original_type, Type type, string selector, bool is_static) diff --git a/src/ObjCRuntime/MethodDescription.cs b/src/ObjCRuntime/MethodDescription.cs index a4736e1e54b9..ba76c00b3f98 100644 --- a/src/ObjCRuntime/MethodDescription.cs +++ b/src/ObjCRuntime/MethodDescription.cs @@ -1,72 +1,16 @@ using System; using System.Reflection; +#if !XAMCORE_2_0 namespace XamCore.ObjCRuntime { -#if XAMCORE_2_0 - internal -#else - public -#endif - struct MethodDescription { -#if XAMCORE_2_0 || MONOTOUCH - internal -#else - public -#endif - MethodBase method; - -#if XAMCORE_2_0 || MONOTOUCH - internal -#else - public -#endif - ArgumentSemantic semantic; - -#if !COREBUILD - - // The ArgumentSemantic enum is public, and - // I don't want to add another enum value there which - // is just an internal implementation detail, so just - // use a constant instead. Eventually we'll use an internal - // enum instead. - const int RetainReturnValueFlag = 1 << 10; - const int InstanceCategoryFlag = 1 << 11; - - internal bool IsInstanceCategory { - get { return (semantic & (ArgumentSemantic) InstanceCategoryFlag) == (ArgumentSemantic) InstanceCategoryFlag; } - } - - public MethodDescription (MethodBase method, ArgumentSemantic semantic) { - var minfo = method as MethodInfo; - var retainReturnValue = minfo != null && minfo.GetBaseDefinition ().ReturnTypeCustomAttributes.IsDefined (typeof (ReleaseAttribute), false); - var instanceCategory = minfo != null && XamCore.Registrar.DynamicRegistrar.HasThisAttributeImpl (minfo); - - // bitfields and a default value of -1 don't go very well together. - if (semantic == ArgumentSemantic.None) - semantic = ArgumentSemantic.Assign; - - if (retainReturnValue) - semantic = semantic | (ArgumentSemantic) (RetainReturnValueFlag); - if (instanceCategory) - semantic |= (ArgumentSemantic) (InstanceCategoryFlag); - - this.method = method; - this.semantic = semantic; - } - - internal UnmanagedMethodDescription GetUnmanagedDescription () { - return new UnmanagedMethodDescription (ObjectWrapper.Convert (method), semantic); - } -#endif // !COREBUILD - } - - internal struct UnmanagedMethodDescription { - public IntPtr method; + public struct MethodDescription { + public MethodBase method; public ArgumentSemantic semantic; - public UnmanagedMethodDescription (IntPtr method, ArgumentSemantic semantic) { + public MethodDescription (MethodBase method, ArgumentSemantic semantic) { this.method = method; this.semantic = semantic; } } } +#endif // !XAMCORE_2_0 diff --git a/src/ObjCRuntime/Registrar.cs b/src/ObjCRuntime/Registrar.cs index fff21ff7440d..227d616fc6b5 100644 --- a/src/ObjCRuntime/Registrar.cs +++ b/src/ObjCRuntime/Registrar.cs @@ -483,11 +483,10 @@ internal class ObjCMethod : ObjCMember { Trampoline trampoline; bool? is_static; bool? is_ctor; -#if !MMP && !MTOUCH - MethodDescription? methodDescription; -#endif TType[] parameters; + TType[] native_parameters; TType return_type; + TType native_return_type; public ObjCMethod (Registrar registrar, ObjCType declaringType, TMethod method) : base (registrar, declaringType) @@ -530,14 +529,54 @@ public bool IsConstructor { } #if !MMP && !MTOUCH - public MethodDescription MethodDescription { + // The ArgumentSemantic enum is public, and + // I don't want to add another enum value there which + // is just an internal implementation detail, so just + // use a constant instead. Eventually we'll use an internal + // enum instead. + const int RetainReturnValueFlag = 1 << 10; + const int InstanceCategoryFlag = 1 << 11; + + internal bool IsInstanceCategory { get { - if (!methodDescription.HasValue) { - // This should never be called from the static registrar - methodDescription = new MethodDescription ((System.Reflection.MethodBase) (object) Method, ArgumentSemantic); - } - - return methodDescription.Value; + return DynamicRegistrar.HasThisAttributeImpl (Method); + } + } + + internal void WriteUnmanagedDescription (IntPtr desc) + { + WriteUnmanagedDescription (desc, (System.Reflection.MethodBase) (object) Method); + } + + internal void WriteUnmanagedDescription (IntPtr desc, System.Reflection.MethodBase method_base) + { + var semantic = ArgumentSemantic; + var minfo = method_base as System.Reflection.MethodInfo; + var retainReturnValue = minfo != null && minfo.GetBaseDefinition ().ReturnTypeCustomAttributes.IsDefined (typeof (ReleaseAttribute), false); + var instanceCategory = minfo != null && DynamicRegistrar.HasThisAttributeImpl (minfo); + + // bitfields and a default value of -1 don't go very well together. + if (semantic == ArgumentSemantic.None) + semantic = ArgumentSemantic.Assign; + + if (retainReturnValue) + semantic |= (ArgumentSemantic) (RetainReturnValueFlag); + if (instanceCategory) + semantic |= (ArgumentSemantic) (InstanceCategoryFlag); + + var bindas_count = Marshal.ReadInt32 (desc + IntPtr.Size + 4); + if (bindas_count < 1 + Parameters.Length) + throw ErrorHelper.CreateError (8018, $"Internal consistency error: BindAs array is not big enough (expected at least {1 + parameters.Length} elements, got {bindas_count} elements) for {method_base.DeclaringType.FullName + "." + method_base.Name}. Please file a bug report at https://bugzilla.xamarin.com."); + + Marshal.WriteIntPtr (desc, ObjectWrapper.Convert (method_base)); + Marshal.WriteInt32 (desc + IntPtr.Size, (int) semantic); + + if (!IsConstructor && ReturnType != NativeReturnType) + Marshal.WriteIntPtr (desc + IntPtr.Size + 8, ObjectWrapper.Convert (NativeReturnType)); + for (int i = 0; i < NativeParameters.Length; i++) { + if (parameters [i] == native_parameters [i]) + continue; + Marshal.WriteIntPtr (desc + IntPtr.Size + 8 + IntPtr.Size * (i + 1), ObjectWrapper.Convert (native_parameters [i])); } } #endif @@ -556,9 +595,109 @@ public TType [] Parameters { } set { parameters = value; + native_parameters = null; } } + public TType [] NativeParameters { + get { + if (native_parameters == null && Parameters != null) { + native_parameters = new TType [parameters.Length]; + for (int i = 0; i < parameters.Length; i++) { + var originalType = Registrar.GetBindAsAttribute (this, i)?.OriginalType; + if (originalType != null) { + if (!IsValidToManagedTypeConversion (originalType, parameters [i])) + throw Registrar.CreateException (4172, Method, $"The registrar can't convert from '{Registrar.GetTypeFullName (parameters [i])}' to '{originalType.FullName}' for the parameter '{Registrar.GetParameterName (Method, i)}' in the method {DescriptiveMethodName}."); + native_parameters [i] = originalType; + } else { + native_parameters [i] = parameters [i]; + } + } + } + return native_parameters; + } + } + + bool IsValidToManagedTypeConversion (TType inputType, TType outputType) + { + var nullableType = Registrar.GetNullableType (outputType); + var isNullable = nullableType != null; + var arrayRank = 0; + var isArray = Registrar.IsArray (outputType, out arrayRank); + + TType underlyingOutputType = outputType; + TType underlyingInputType = inputType; + if (isNullable) { + underlyingOutputType = nullableType; + } else if (isArray) { + if (arrayRank != 1) + return false; + if (!Registrar.IsArray (inputType)) + return false; + underlyingOutputType = Registrar.GetElementType (outputType); + underlyingInputType = Registrar.GetElementType (inputType); + } + var outputTypeName = Registrar.GetTypeFullName (underlyingOutputType); + + if (Registrar.Is (underlyingInputType, Foundation, "NSNumber")) { + switch (outputTypeName) { + case "System.Byte": + case "System.SByte": + case "System.Int16": + case "System.UInt16": + case "System.Int32": + case "System.UInt32": + case "System.Int64": + case "System.UInt64": + case "System.nint": + case "System.nuint": + case "System.Single": + case "System.Double": + case "System.nfloat": + case "System.Boolean": + return true; + default: + return false; + } + } else if (Registrar.Is (underlyingInputType, Foundation, "NSValue")) { + // Remove 'MonoMac.' namespace prefix to make switch smaller + if (!Registrar.IsDualBuild && outputTypeName.StartsWith ("MonoMac.", StringComparison.Ordinal)) + outputTypeName = outputTypeName.Substring ("MonoMac.".Length); + + switch (outputTypeName) { + case "CoreAnimation.CATransform3D": + case "CoreGraphics.CGAffineTransform": + case "CoreGraphics.CGPoint": + case "CoreGraphics.CGRect": + case "CoreGraphics.CGSize": + case "CoreGraphics.CGVector": + case "CoreLocation.CLLocationCoordinate2D": + case "CoreMedia.CMTime": + case "CoreMedia.CMTimeMapping": + case "CoreMedia.CMTimeRange": + case "MapKit.MKCoordinateSpan": + case "Foundation.NSRange": + case "SceneKit.SCNMatrix4": + case "SceneKit.SCNVector3": + case "SceneKit.SCNVector4": + case "UIKit.UIEdgeInsets": + case "UIKit.UIOffset": + return true; + default: + return false; + } + } else if (Registrar.Is (underlyingInputType, Foundation, "NSString")) { + return Registrar.IsSmartEnum (underlyingOutputType); + } else { + return false; + } + } + + bool IsValidToNativeTypeConversion (TType inputType, TType outputType) + { + return IsValidToManagedTypeConversion (inputType: outputType, outputType: inputType); + } + public bool HasReturnType { get { return return_type != null; @@ -573,6 +712,27 @@ public TType ReturnType { } set { return_type = value; + native_return_type = null; + } + } + + public TType NativeReturnType { + get { + if (native_return_type == null) { + if (Registrar.Is (ReturnType, "System", "Void")) { + native_return_type = ReturnType; + } else { + var originalType = Registrar.GetBindAsAttribute (this, -1)?.OriginalType; + if (originalType != null) { + if (!IsValidToManagedTypeConversion (originalType, ReturnType)) + throw Registrar.CreateException (4170, Method, $"The registrar can't convert from '{Registrar.GetTypeFullName (ReturnType)}' to '{originalType.FullName}' for the return value in the method {DescriptiveMethodName}."); + native_return_type = originalType; + } else { + native_return_type = ReturnType; + } + } + } + return native_return_type; } } @@ -621,25 +781,25 @@ public Trampoline Trampoline { var mi = (System.Reflection.MethodInfo) Method; bool is_stret; #if __WATCHOS__ - is_stret = Runtime.Arch == Arch.DEVICE ? Stret.ArmNeedStret (mi.ReturnType) : Stret.X86NeedStret (mi.ReturnType); + is_stret = Runtime.Arch == Arch.DEVICE ? Stret.ArmNeedStret (NativeReturnType) : Stret.X86NeedStret (NativeReturnType); #elif MONOMAC - is_stret = IntPtr.Size == 8 ? Stret.X86_64NeedStret (mi.ReturnType) : Stret.X86NeedStret (mi.ReturnType); + is_stret = IntPtr.Size == 8 ? Stret.X86_64NeedStret (NativeReturnType) : Stret.X86NeedStret (NativeReturnType); #elif __IOS__ if (Runtime.Arch == Arch.DEVICE) { - is_stret = IntPtr.Size == 4 && Stret.ArmNeedStret (mi.ReturnType); + is_stret = IntPtr.Size == 4 && Stret.ArmNeedStret (NativeReturnType); } else { - is_stret = IntPtr.Size == 4 ? Stret.X86NeedStret (mi.ReturnType) : Stret.X86_64NeedStret (mi.ReturnType); + is_stret = IntPtr.Size == 4 ? Stret.X86NeedStret (NativeReturnType) : Stret.X86_64NeedStret (NativeReturnType); } #elif __TVOS__ - is_stret = Runtime.Arch == Arch.SIMULATOR && Stret.X86_64NeedStret (mi.ReturnType); + is_stret = Runtime.Arch == Arch.SIMULATOR && Stret.X86_64NeedStret (NativeReturnType); #else #error unknown architecture #endif var is_static_trampoline = IsStatic && !IsCategoryInstance; - var is_value_type = Registrar.IsValueType (ReturnType) && !Registrar.IsEnum (ReturnType); + var is_value_type = Registrar.IsValueType (NativeReturnType) && !Registrar.IsEnum (NativeReturnType); - if (is_value_type && Registrar.IsGenericType (ReturnType)) - throw Registrar.CreateException (4104, Method, "The registrar cannot marshal the return value of type `{0}` in the method `{1}.{2}`.", Registrar.GetTypeFullName (ReturnType), Registrar.GetTypeFullName (DeclaringType.Type), Registrar.GetDescriptiveMethodName (Method)); + if (is_value_type && Registrar.IsGenericType (NativeReturnType)) + throw Registrar.CreateException (4104, Method, "The registrar cannot marshal the return value of type `{0}` in the method `{1}.{2}`.", Registrar.GetTypeFullName (NativeReturnType), Registrar.GetTypeFullName (DeclaringType.Type), Registrar.GetDescriptiveMethodName (Method)); if (is_stret) { if (Registrar.IsSimulatorOrDesktop && !Registrar.Is64Bits) { @@ -703,7 +863,7 @@ public bool ValidateSignature (ref List exceptions) string ComputeSignature () { - return Registrar.ComputeSignature (DeclaringType.Type, Method, this, IsCategoryInstance); + return Registrar.ComputeSignature (DeclaringType.Type, null, this, IsCategoryInstance); } public override string ToString () @@ -854,11 +1014,15 @@ protected virtual void OnRegisterCategory (ObjCType type, ref List ex protected abstract List GetAvailabilityAttributes (TType obj); // must only return attributes for the current platform. protected abstract Version GetSDKVersion (); protected abstract TType GetProtocolAttributeWrapperType (TType type); // Return null if no attribute is found. Do not consider base types. + protected abstract BindAsAttribute GetBindAsAttribute (TMethod method, int parameter_index); // If parameter_index = -1 then get the attribute for the return type. Return null if no attribute is found. Must consider base method. + protected abstract BindAsAttribute GetBindAsAttribute (TProperty property); + protected abstract TType GetNullableType (TType type); // For T? returns T. For T returns null. protected abstract bool HasReleaseAttribute (TMethod method); // Returns true of the method's return type/value has a [Release] attribute. protected abstract bool IsINativeObject (TType type); protected abstract bool IsValueType (TType type); - protected abstract bool IsArray (TType type); + protected abstract bool IsArray (TType type, out int rank); protected abstract bool IsEnum (TType type, out bool isNativeEnum); + protected abstract bool IsNullable (TType type); protected abstract bool IsDelegate (TType type); protected abstract bool IsGenericType (TType type); protected abstract bool IsGenericMethod (TMethod method); @@ -876,6 +1040,9 @@ protected virtual void OnRegisterCategory (ObjCType type, ref List ex protected abstract Exception CreateException (int code, Exception innerException, TMethod method, string message, params object[] args); protected abstract Exception CreateException (int code, Exception innerException, TType type, string message, params object [] args); protected abstract string PlatformName { get; } + public abstract TType FindType (TType relative, string @namespace, string name); + protected abstract IEnumerable FindMethods (TType type, string name); // will return null if nothing was found + protected abstract TProperty FindProperty (TType type, string name); // will return null if nothing was found protected abstract string GetAssemblyName (TAssembly assembly); protected abstract string GetTypeFullName (TType type); @@ -895,12 +1062,81 @@ public Registrar () IsDualBuild = IsDualBuildImpl; } - bool IsEnum (TType type) + protected bool IsArray (TType type) + { + int rank; + return IsArray (type, out rank); + } + + protected bool IsEnum (TType type) { bool dummy; return IsEnum (type, out dummy); } + public BindAsAttribute GetBindAsAttribute (ObjCMethod method, int parameter_index) + { + var attrib = GetBindAsAttribute (method.Method, parameter_index); + if (attrib != null) + return attrib; + + if (!method.IsPropertyAccessor) + return null; + + return GetBindAsAttribute (FindProperty (method.DeclaringType.Type, method.MethodName.Substring (4))); + } + + bool IsSmartEnum (TType type) + { + TMethod getConstant, getValue; + return IsSmartEnum (type, out getConstant, out getValue); + } + + public bool IsSmartEnum (TType type, out TMethod getConstantMethod, out TMethod getValueMethod) + { + getConstantMethod = null; + getValueMethod = null; + + if (!IsEnum (type)) + return false; + + var extension = FindType (type, type.Namespace, type.Name + "Extensions"); + if (extension == null) + return false; + + var getConstantMethods = FindMethods (extension, "GetConstant"); + foreach (var m in getConstantMethods) { + if (!Is (GetReturnType (m), Foundation, "NSString")) + continue; + var parameters = GetParameters (m); + if (parameters?.Length != 1) + continue; + if (!AreEqual (parameters [0], type)) + continue; + getConstantMethod = m; + break; + } + if (getConstantMethod == null) + return false; + + var getValueMethods = FindMethods (extension, "GetValue"); + foreach (var m in getValueMethods) { + if (!AreEqual (GetReturnType (m), type)) + continue; + var parameters = GetParameters (m); + if (parameters?.Length != 1) + continue; + if (!Is (parameters [0], Foundation, "NSString")) + continue; + getValueMethod = m; + break; + } + if (getValueMethod == null) + return false; + + return true; + } + protected string GetMemberName (ObjCMember member) { var method = member as ObjCMethod; @@ -1086,6 +1322,11 @@ protected virtual bool IsNSObject (TType type) return false; } + protected virtual bool AreEqual (TType a, TType b) + { + return a == b; + } + protected bool Is (TType type, string @namespace, string name) { string ns, n; @@ -1148,8 +1389,8 @@ bool VerifyNonGenericMethod (ref List exceptions, TType declaringType void VerifyInSdk (ref List exceptions, ObjCMethod method) { - if (method.HasReturnType || (method.Method != null && !method.IsConstructor && method.ReturnType != null)) - VerifyTypeInSDK (ref exceptions, method.ReturnType, returnTypeOf: method); + if (method.HasReturnType || (method.Method != null && !method.IsConstructor && method.NativeReturnType != null)) + VerifyTypeInSDK (ref exceptions, method.NativeReturnType, returnTypeOf: method); if (method.HasParameters || (method.Method != null && method.Parameters != null)) { foreach (var p in method.Parameters) @@ -2060,7 +2301,7 @@ public string ComputeSignature (TType DeclaringType, TMethod Method, ObjCMember if (is_ctor) { signature.Append ('@'); } else { - var ReturnType = Method != null ? GetReturnType (Method) : method.ReturnType; + var ReturnType = Method != null ? GetReturnType (Method) : method.NativeReturnType; signature.Append (ToSignature (ReturnType, member, ref success)); if (!success) throw CreateException (4104, Method, "The registrar cannot marshal the return value of type `{0}` in the method `{1}.{2}`.", GetTypeFullName (ReturnType), GetTypeFullName (DeclaringType), GetDescriptiveMethodName (Method)); @@ -2072,7 +2313,7 @@ public string ComputeSignature (TType DeclaringType, TMethod Method, ObjCMember if (Method != null) { parameters = GetParameters (Method); } else { - parameters = method.Parameters; + parameters = method.NativeParameters; } if (parameters != null) { @@ -2163,7 +2404,9 @@ protected string ToSignature (TType type, ObjCMember member, ref bool success, b { bool isNativeEnum; - switch (GetTypeFullName (type)) { + var typeFullName = GetTypeFullName (type); + + switch (typeFullName) { case "System.IntPtr": return "^v"; case "System.SByte": return "c"; case "System.Byte": return "C"; diff --git a/src/ObjCRuntime/Runtime.cs b/src/ObjCRuntime/Runtime.cs index 0cccffad8d86..97876b838ad5 100644 --- a/src/ObjCRuntime/Runtime.cs +++ b/src/ObjCRuntime/Runtime.cs @@ -308,6 +308,34 @@ static IntPtr GetFunctionPointer (Delegate d) return Marshal.GetFunctionPointerForDelegate (d); } + // value_handle: GCHandle to a (smart) enum value + // returns: a handle to a native NSString * + static IntPtr ConvertSmartEnumToNSString (IntPtr value_handle) + { + var value = GCHandle.FromIntPtr (value_handle).Target; + var smart_type = value.GetType (); + MethodBase getConstantMethod, getValueMethod; + if (!Registrar.IsSmartEnum (smart_type, out getConstantMethod, out getValueMethod)) + throw ErrorHelper.CreateError (8024, $"Could not find a valid extension type for the smart enum '{smart_type.FullName}'. Please file a bug at https://bugzilla.xamarin.com."); + var rv = (NSString) ((MethodInfo) getConstantMethod).Invoke (null, new object [] { value }); + rv.DangerousRetain ().DangerousAutorelease (); + return rv.Handle; + } + + + // value: native NSString * + // returns: GCHandle to a (smart) enum value. Caller must free the GCHandle. + static IntPtr ConvertNSStringToSmartEnum (IntPtr value, IntPtr type) + { + var smart_type = (Type) ObjectWrapper.Convert (type); + var str = GetNSObject (value); + MethodBase getConstantMethod, getValueMethod; + if (!Registrar.IsSmartEnum (smart_type, out getConstantMethod, out getValueMethod)) + throw ErrorHelper.CreateError (8024, $"Could not find a valid extension type for the smart enum '{smart_type.FullName}'. Please file a bug at https://bugzilla.xamarin.com."); + var rv = ((MethodInfo) getValueMethod).Invoke (null, new object [] { str }); + return GCHandle.ToIntPtr (GCHandle.Alloc (rv)); + } + #region Wrappers for delegate callbacks static void RegisterNSObject (IntPtr managed_obj, IntPtr native_obj) { @@ -556,10 +584,10 @@ static IntPtr GetSelectorHandle (IntPtr sel) return ((Selector) ObjectWrapper.Convert (sel)).Handle; } - static UnmanagedMethodDescription GetMethodForSelector (IntPtr cls, IntPtr sel, bool is_static) + static void GetMethodForSelector (IntPtr cls, IntPtr sel, bool is_static, IntPtr desc) { // This is called by the old registrar code. - return Registrar.GetMethodDescription (Class.Lookup (cls), sel, is_static); + Registrar.GetMethodDescription (Class.Lookup (cls), sel, is_static, desc); } static IntPtr GetNSObjectWrapped (IntPtr ptr) @@ -675,9 +703,9 @@ static bool IsParameterOut (IntPtr info, int parameter) return parameters [parameter].IsOut; } - static UnmanagedMethodDescription GetMethodAndObjectForSelector (IntPtr klass, IntPtr sel, bool is_static, IntPtr obj, ref IntPtr mthis) + static void GetMethodAndObjectForSelector (IntPtr klass, IntPtr sel, bool is_static, IntPtr obj, ref IntPtr mthis, IntPtr desc) { - return Registrar.GetMethodDescriptionAndObject (Class.Lookup (klass), sel, is_static, obj, ref mthis); + Registrar.GetMethodDescriptionAndObject (Class.Lookup (klass), sel, is_static, obj, ref mthis, desc); } static int CreateProductException (int code, string msg) diff --git a/tools/common/StaticRegistrar.cs b/tools/common/StaticRegistrar.cs index c39fd7ffec9c..93d1e9e47a44 100644 --- a/tools/common/StaticRegistrar.cs +++ b/tools/common/StaticRegistrar.cs @@ -563,6 +563,44 @@ public StaticRegistrar (Target target) this.Target = target; } + protected override PropertyDefinition FindProperty (TypeReference type, string name) + { + var td = type.Resolve (); + if (td?.HasProperties != true) + return null; + + foreach (var prop in td.Properties) { + if (prop.Name == name) + return prop; + } + + return null; + } + + protected override IEnumerable FindMethods (TypeReference type, string name) + { + var td = type.Resolve (); + if (td?.HasMethods != true) + return null; + + List list = null; + foreach (var method in td.Methods) { + if (method.Name != name) + continue; + + if (list == null) + list = new List (); + + list.Add (method); + } + return list; + } + + public override TypeReference FindType (TypeReference relative, string @namespace, string name) + { + return relative.Resolve ().Module.GetType (@namespace, name); + } + protected override bool LaxMode { get { return IsSingleAssembly; @@ -910,6 +948,11 @@ bool IsNativeEnum (TypeDefinition td) return IsDualBuild && SharedStatic.HasAttribute (td, ObjCRuntime, StringConstants.NativeAttribute); } + protected override bool IsNullable (TypeReference type) + { + return GetNullableType (type) != null; + } + protected override bool IsEnum (TypeReference tr, out bool isNativeEnum) { var type = tr.Resolve (); @@ -921,9 +964,16 @@ protected override bool IsEnum (TypeReference tr, out bool isNativeEnum) return type.IsEnum; } - protected override bool IsArray (TypeReference type) + protected override bool IsArray (TypeReference type, out int rank) { - return type is ArrayType; + var arrayType = type as ArrayType; + if (arrayType == null) { + rank = 0; + return false; + } + + rank = arrayType.Rank; + return true; } protected override bool IsGenericType (TypeReference type) @@ -960,6 +1010,17 @@ protected override TypeReference GetGenericTypeDefinition (TypeReference type) return type; } + protected override bool AreEqual (TypeReference a, TypeReference b) + { + if (a == b) + return true; + + if (a == null ^ b == null) + return false; + + return SharedStatic.TypeMatch (a, b); + } + protected override bool VerifyIsConstrainedToNSObject (TypeReference type, out TypeReference constrained_type) { constrained_type = null; @@ -1481,6 +1542,70 @@ protected override TypeReference GetProtocolAttributeWrapperType (TypeReference return null; } + protected override BindAsAttribute GetBindAsAttribute (PropertyDefinition property) + { + CustomAttribute attrib; + + if (property == null) + return null; + + property = GetBasePropertyInTypeHierarchy (property); + + if (!SharedStatic.TryGetAttributeImpl (property, ObjCRuntime, "BindAsAttribute", out attrib)) + return null; + + return CreateBindAsAttribute (attrib, property); + } + + protected override BindAsAttribute GetBindAsAttribute (MethodDefinition method, int parameter_index) + { + CustomAttribute attrib; + + if (method == null) + return null; + + method = GetBaseMethodInTypeHierarchy (method); + + if (!SharedStatic.TryGetAttributeImpl (parameter_index == -1 ? (ICustomAttributeProvider) method.MethodReturnType : method.Parameters [parameter_index], ObjCRuntime, "BindAsAttribute", out attrib)) + return null; + + return CreateBindAsAttribute (attrib, method); + } + + static BindAsAttribute CreateBindAsAttribute (CustomAttribute attrib, IMemberDefinition member) + { + TypeReference originalType = null; + if (attrib.HasFields) { + foreach (var field in attrib.Fields) { + switch (field.Name) { + case "OriginalType": + originalType = ((TypeReference) field.Argument.Value); + break; + default: + throw ErrorHelper.CreateError (4124, "Invalid BindAsAttribute found on '{0}.{1}': unknown field {2}. Please file a bug report at https://bugzilla.xamarin.com", member.DeclaringType.FullName, member.Name, field.Name); + } + } + } + + switch (attrib.ConstructorArguments.Count) { + case 1: + var t1 = (TypeReference) attrib.ConstructorArguments [0].Value; + return new BindAsAttribute (t1 != null ? t1.Resolve () : null) { OriginalType = originalType }; + default: + throw ErrorHelper.CreateError (4124, "Invalid BindAsAttribute found on '{0}.{1}'. Please file a bug report at https://bugzilla.xamarin.com", member.DeclaringType.FullName, member.Name); + } + } + + protected override TypeReference GetNullableType (TypeReference type) + { + var git = type as GenericInstanceType; + if (git == null) + return null; + if (!git.GetElementType ().Is ("System", "Nullable`1")) + return null; + return git.GenericArguments [0]; + } + protected override ConnectAttribute GetConnectAttribute (PropertyDefinition property) { CustomAttribute attrib; @@ -2151,7 +2276,7 @@ string GetObjCSignature (ObjCMethod method, List exceptions) sb.Append ((method.IsStatic && !method.IsCategoryInstance) ? '+' : '-'); sb.Append ('('); - sb.Append (isCtor ? "id" : this.ToObjCParameterType (method.ReturnType, GetDescriptiveMethodName (method.Method), exceptions, method.Method)); + sb.Append (isCtor ? "id" : this.ToObjCParameterType (method.NativeReturnType, GetDescriptiveMethodName (method.Method), exceptions, method.Method)); sb.Append (')'); var split = method.Selector.Split (':'); @@ -2166,7 +2291,7 @@ string GetObjCSignature (ObjCMethod method, List exceptions) sb.Append (split [i]); sb.Append (':'); sb.Append ('('); - sb.Append (ToObjCParameterType (method.Parameters [i + indexOffset], method.DescriptiveMethodName, exceptions, method.Method)); + sb.Append (ToObjCParameterType (method.NativeParameters [i + indexOffset], method.DescriptiveMethodName, exceptions, method.Method)); sb.Append (')'); sb.AppendFormat ("p{0}", i); } @@ -2729,7 +2854,7 @@ void Specialize (AutoIndentStringBuilder sb, ObjCMethod method, List } var rettype = string.Empty; - var returntype = method.Method.ReturnType; + var returntype = method.ReturnType; var isStatic = method.IsStatic; var isInstanceCategory = method.IsCategoryInstance; var isCtor = false; @@ -2753,7 +2878,7 @@ void Specialize (AutoIndentStringBuilder sb, ObjCMethod method, List case Trampoline.X86_DoubleABI_StretTrampoline: case Trampoline.StaticStret: case Trampoline.Stret: - switch (returntype.FullName) { + switch (method.NativeReturnType.FullName) { case "System.Int64": rettype = "long long"; break; @@ -2767,7 +2892,7 @@ void Specialize (AutoIndentStringBuilder sb, ObjCMethod method, List rettype = "double"; break; default: - rettype = ToObjCParameterType (returntype, descriptiveMethodName, exceptions, method.Method); + rettype = ToObjCParameterType (method.NativeReturnType, descriptiveMethodName, exceptions, method.Method); break; } break; @@ -2912,7 +3037,8 @@ void Specialize (AutoIndentStringBuilder sb, ObjCMethod method, List var param = method.Method.Parameters [i]; var paramBase = baseMethod.Parameters [i]; var type = method.Parameters [i]; - var objctype = ToObjCParameterType (type, descriptiveMethodName, exceptions, method.Method); + var nativetype = method.NativeParameters [i]; + var objctype = ToObjCParameterType (nativetype, descriptiveMethodName, exceptions, method.Method); var original_objctype = objctype; var isRef = type.IsByReference; var isOut = param.IsOut || paramBase.IsOut; @@ -2921,7 +3047,12 @@ void Specialize (AutoIndentStringBuilder sb, ObjCMethod method, List var td = type.Resolve (); var isVariadic = i + 1 == num_arg && method.IsVariadic; - if (isRef) { + if (type != nativetype) { + GenerateConversionToManaged (nativetype, type, setup_call_stack, descriptiveMethodName, ref exceptions, method, $"p{i}", $"arg_ptrs [{i}]", $"mono_class_from_mono_type (xamarin_get_parameter_type (managed_method, {i}))"); + if (isRef || isOut) + throw new Exception (); + continue; + } else if (isRef) { type = type.GetElementType (); td = type.Resolve (); original_objctype = ToObjCParameterType (type, descriptiveMethodName, exceptions, method.Method); @@ -3292,7 +3423,9 @@ void Specialize (AutoIndentStringBuilder sb, ObjCMethod method, List var type = returntype.Resolve () ?? returntype; var retain = method.RetainReturnValue; - if (returntype.IsValueType) { + if (returntype != method.NativeReturnType) { + GenerateConversionToNative (returntype, method.NativeReturnType, setup_return, descriptiveMethodName, ref exceptions, method, "retval", "res", "mono_class_from_mono_type (xamarin_get_parameter_type (managed_method, -1))"); + } else if (returntype.IsValueType) { setup_return.AppendLine ("res = *({0} *) mono_object_unbox ((MonoObject *) retval);", rettype); } else if (isArray) { var elementType = ((ArrayType) returntype).ElementType; @@ -3320,7 +3453,7 @@ void Specialize (AutoIndentStringBuilder sb, ObjCMethod method, List setup_return.AppendLine ("goto exception_handling;"); setup_return.AppendLine ("}"); } else { - throw ErrorHelper.CreateError (App, 4111, method.Method, "The registrar cannot build a signature for type `{0}' in method `{1}`.", returntype.FullName, descriptiveMethodName); + throw ErrorHelper.CreateError (App, 4111, method.Method, "The registrar cannot build a signature for type `{0}' in method `{1}`.", method.NativeReturnType.FullName, descriptiveMethodName); } setup_return.AppendLine ("}"); @@ -3491,8 +3624,8 @@ void Specialize (AutoIndentStringBuilder sb, ObjCMethod method, List var objc_signature = new StringBuilder ().Append (rettype).Append (":"); if (method.Method.HasParameters) { - for (int i = 0; i < method.Method.Parameters.Count; i++) - objc_signature.Append (ToObjCParameterType (method.Method.Parameters [i].ParameterType, descriptiveMethodName, exceptions, method.Method)).Append (":"); + for (int i = 0; i < method.NativeParameters.Length; i++) + objc_signature.Append (ToObjCParameterType (method.NativeParameters [i], descriptiveMethodName, exceptions, method.Method)).Append (":"); } Body existing; @@ -3513,9 +3646,9 @@ void Specialize (AutoIndentStringBuilder sb, ObjCMethod method, List if (merge_bodies) { methods.Append ("static "); methods.Append (rettype).Append (" ").Append (b.Name).Append (" (id self, SEL _cmd, MonoMethod **managed_method_ptr"); - var pcount = method.Method.HasParameters ? method.Method.Parameters.Count : 0; + var pcount = method.Method.HasParameters ? method.NativeParameters.Length : 0; for (int i = (isInstanceCategory ? 1 : 0); i < pcount; i++) { - methods.Append (", ").Append (ToObjCParameterType (method.Method.Parameters [i].ParameterType, descriptiveMethodName, exceptions, method.Method)); + methods.Append (", ").Append (ToObjCParameterType (method.NativeParameters [i], descriptiveMethodName, exceptions, method.Method)); methods.Append (" ").Append ("p").Append (i.ToString ()); } if (isCtor) @@ -3582,6 +3715,253 @@ void Specialize (AutoIndentStringBuilder sb, ObjCMethod method, List } } + string GetManagedToNSNumberFunc (TypeReference managedType, TypeReference inputType, TypeReference outputType, string descriptiveMethodName) + { + var typeName = managedType.FullName; + switch (typeName) { + case "System.SByte": return "xamarin_sbyte_to_nsnumber"; + case "System.Byte": return "xamarin_byte_to_nsnumber"; + case "System.Int16": return "xamarin_short_to_nsnumber"; + case "System.UInt16": return "xamarin_ushort_to_nsnumber"; + case "System.Int32": return "xamarin_int_to_nsnumber"; + case "System.UInt32": return "xamarin_uint_to_nsnumber"; + case "System.Int64": return "xamarin_long_to_nsnumber"; + case "System.UInt64": return "xamarin_ulong_to_nsnumber"; + case "System.nint": return "xamarin_nint_to_nsnumber"; + case "System.nuint": return "xamarin_nuint_to_nsnumber"; + case "System.Single": return "xamarin_float_to_nsnumber"; + case "System.Double": return "xamarin_double_to_nsnumber"; + case "System.nfloat": return "xamarin_nfloat_to_nsnumber"; + case "System.Boolean": return "xamarin_bool_to_nsnumber"; + default: + throw ErrorHelper.CreateError (99, $"Internal error: can't convert from '{inputType.FullName}' to '{outputType.FullName}' in {descriptiveMethodName}. Please file a bug report with a test case (https://bugzilla.xamarin.com)."); + } + } + + string GetNSNumberToManagedFunc (TypeReference managedType, TypeReference inputType, TypeReference outputType, string descriptiveMethodName, out string nativeType) + { + var typeName = managedType.FullName; + switch (typeName) { + case "System.SByte": nativeType = "int8_t"; return "xamarin_nsnumber_to_sbyte"; + case "System.Byte": nativeType = "uint8_t"; return "xamarin_nsnumber_to_byte"; + case "System.Int16": nativeType = "int16_t"; return "xamarin_nsnumber_to_short"; + case "System.UInt16": nativeType = "uint16_t"; return "xamarin_nsnumber_to_ushort"; + case "System.Int32": nativeType = "int32_t"; return "xamarin_nsnumber_to_int"; + case "System.UInt32": nativeType = "uint32_t"; return "xamarin_nsnumber_to_uint"; + case "System.Int64": nativeType = "int64_t"; return "xamarin_nsnumber_to_long"; + case "System.UInt64": nativeType = "uint64_t"; return "xamarin_nsnumber_to_ulong"; + case "System.nint": nativeType = "NSInteger"; return "xamarin_nsnumber_to_nint"; + case "System.nuint": nativeType = "NSUInteger"; return "xamarin_nsnumber_to_nuint"; + case "System.Single": nativeType = "float"; return "xamarin_nsnumber_to_float"; + case "System.Double": nativeType = "double"; return "xamarin_nsnumber_to_double"; + case "System.nfloat": nativeType = "CGFloat"; return "xamarin_nsnumber_to_nfloat"; + case "System.Boolean": nativeType = "BOOL"; return "xamarin_nsnumber_to_bool"; + default: + throw ErrorHelper.CreateError (99, $"Internal error: can't convert from '{inputType.FullName}' to '{outputType.FullName}' in {descriptiveMethodName}. Please file a bug report with a test case (https://bugzilla.xamarin.com)."); + } + } + + string GetNSValueToManagedFunc (TypeReference managedType, TypeReference inputType, TypeReference outputType, string descriptiveMethodName, out string nativeType) + { + var underlyingTypeName = managedType.FullName; + + // Remove 'MonoMac.' namespace prefix to make switch smaller + if (!Registrar.IsDualBuild && underlyingTypeName.StartsWith ("MonoMac.", StringComparison.Ordinal)) + underlyingTypeName = underlyingTypeName.Substring ("MonoMac.".Length); + + switch (underlyingTypeName) { + case "Foundation.NSRange": nativeType = "NSRange"; return "xamarin_nsvalue_to_nsrange"; + case "CoreGraphics.CGAffineTransform": nativeType = "CGAffineTransform"; return "xamarin_nsvalue_to_cgaffinetransform"; + case "CoreGraphics.CGPoint": nativeType = "CGPoint"; return "xamarin_nsvalue_to_cgpoint"; + case "CoreGraphics.CGRect": nativeType = "CGRect"; return "xamarin_nsvalue_to_cgrect"; + case "CoreGraphics.CGSize": nativeType = "CGSize"; return "xamarin_nsvalue_to_cgsize"; + case "CoreGraphics.CGVector": nativeType = "CGVector"; return "xamarin_nsvalue_to_cgvector"; + case "CoreAnimation.CATransform3D": nativeType = "CATransform3D"; return "xamarin_nsvalue_to_catransform3d"; + case "CoreLocation.CLLocationCoordinate2D": nativeType = "CLLocationCoordinate2D"; return "xamarin_nsvalue_to_cllocationcoordinate2d"; + case "CoreMedia.CMTime": nativeType = "CMTime"; return "xamarin_nsvalue_to_cmtime"; + case "CoreMedia.CMTimeMapping": nativeType = "CMTimeMapping"; return "xamarin_nsvalue_to_cmtimemapping"; + case "CoreMedia.CMTimeRange": nativeType = "CMTimeRange"; return "xamarin_nsvalue_to_cmtimerange"; + case "MapKit.MKCoordinateSpan": nativeType = "MKCoordinateSpan"; return "xamarin_nsvalue_to_mkcoordinatespan"; + case "SceneKit.SCNMatrix4": nativeType = "SCNMatrix4"; return "xamarin_nsvalue_to_scnmatrix4"; + case "SceneKit.SCNVector3": nativeType = "SCNVector3"; return "xamarin_nsvalue_to_scnvector3"; + case "SceneKit.SCNVector4": nativeType = "SCNVector4"; return "xamarin_nsvalue_to_scnvector4"; + case "UIKit.UIEdgeInsets": nativeType = "UIEdgeInsets"; return "xamarin_nsvalue_to_uiedgeinsets"; + case "UIKit.UIOffset": nativeType = "UIOffset"; return "xamarin_nsvalue_to_uioffset"; + default: + throw ErrorHelper.CreateError (99, $"Internal error: can't convert from '{inputType.FullName}' to '{outputType.FullName}' in {descriptiveMethodName}. Please file a bug report with a test case (https://bugzilla.xamarin.com)."); + } + } + + string GetManagedToNSValueFunc (TypeReference managedType, TypeReference inputType, TypeReference outputType, string descriptiveMethodName) + { + var underlyingTypeName = managedType.FullName; + + // Remove 'MonoMac.' namespace prefix to make switch smaller + if (!Registrar.IsDualBuild && underlyingTypeName.StartsWith ("MonoMac.", StringComparison.Ordinal)) + underlyingTypeName = underlyingTypeName.Substring ("MonoMac.".Length); + + switch (underlyingTypeName) { + case "Foundation.NSRange": return "xamarin_nsrange_to_nsvalue"; + case "CoreGraphics.CGAffineTransform": return "xamarin_cgaffinetransform_to_nsvalue"; + case "CoreGraphics.CGPoint": return "xamarin_cgpoint_to_nsvalue"; + case "CoreGraphics.CGRect": return "xamarin_cgrect_to_nsvalue"; + case "CoreGraphics.CGSize": return "xamarin_cgsize_to_nsvalue"; + case "CoreGraphics.CGVector": return "xamarin_cgvector_to_nsvalue"; + case "CoreAnimation.CATransform3D": return "xamarin_catransform3d_to_nsvalue"; + case "CoreLocation.CLLocationCoordinate2D": return "xamarin_cllocationcoordinate2d_to_nsvalue"; + case "CoreMedia.CMTime": return "xamarin_cmtime_to_nsvalue"; + case "CoreMedia.CMTimeMapping": return "xamarin_cmtimemapping_to_nsvalue"; + case "CoreMedia.CMTimeRange": return "xamarin_cmtimerange_to_nsvalue"; + case "MapKit.MKCoordinateSpan": return "xamarin_mkcoordinatespan_to_nsvalue"; + case "SceneKit.SCNMatrix4": return "xamarin_scnmatrix4_to_nsvalue"; + case "SceneKit.SCNVector3": return "xamarin_scnvector3_to_nsvalue"; + case "SceneKit.SCNVector4": return "xamarin_scnvector4_to_nsvalue"; + case "UIKit.UIEdgeInsets": return "xamarin_uiedgeinsets_to_nsvalue"; + case "UIKit.UIOffset": return "xamarin_uioffset_to_nsvalue"; + default: + throw ErrorHelper.CreateError (99, $"Internal error: can't convert from '{inputType.FullName}' to '{outputType.FullName}' in {descriptiveMethodName}. Please file a bug report with a test case (https://bugzilla.xamarin.com)."); + } + } + + string GetNSStringToSmartEnumFunc (TypeReference managedType, TypeReference inputType, TypeReference outputType, string descriptiveMethodName, string parameterClass, out string nativeType) + { + nativeType = "NSString *"; + return $"xamarin_get_nsstring_to_smart_enum_func ({parameterClass}, managed_method, &exception_gchandle)"; + } + + string GetSmartEnumToNSStringFunc (TypeReference managedType, TypeReference inputType, TypeReference outputType, string descriptiveMethodName, string parameterClass) + { + return $"xamarin_get_smart_enum_to_nsstring_func ({parameterClass}, managed_method, &exception_gchandle)"; + } + + void GenerateConversionToManaged (TypeReference inputType, TypeReference outputType, AutoIndentStringBuilder sb, string descriptiveMethodName, ref List exceptions, ObjCMethod method, string inputName, string outputName, string managedClassExpression) + { + // This is a mirror of the native method xamarin_generate_conversion_to_managed (for the dynamic registrar). + // These methods must be kept in sync. + var managedType = outputType; + var nativeType = inputType; + + var isManagedNullable = IsNullable (managedType); + + var underlyingManagedType = managedType; + var underlyingNativeType = nativeType; + + var isManagedArray = IsArray (managedType); + var isNativeArray = IsArray (nativeType); + + if (isManagedArray != isNativeArray) + throw ErrorHelper.CreateError (99, $"Internal error: can't convert from '{inputType.FullName}' to '{outputType.FullName}' in {descriptiveMethodName}. Please file a bug report with a test case (https://bugzilla.xamarin.com)."); + + if (isManagedArray) { + if (isManagedNullable) + throw ErrorHelper.CreateError (99, $"Internal error: can't convert from '{inputType.FullName}' to '{outputType.FullName}' in {descriptiveMethodName}. Please file a bug report with a test case (https://bugzilla.xamarin.com)."); + underlyingNativeType = GetElementType (nativeType); + underlyingManagedType = GetElementType (managedType); + managedClassExpression = $"mono_class_get_element_class ({managedClassExpression})"; + } else if (isManagedNullable) { + underlyingManagedType = GetNullableType (managedType); + managedClassExpression = $"mono_class_get_nullable_param ({managedClassExpression})"; + } + + if (isManagedNullable || isManagedArray) + sb.AppendLine ($"if ({inputName}) {{"); + + string func; + string nativeTypeName; + if (underlyingNativeType.Is (Foundation, "NSNumber")) { + func = GetNSNumberToManagedFunc (underlyingManagedType, inputType, outputType, descriptiveMethodName, out nativeTypeName); + } else if (underlyingNativeType.Is (Foundation, "NSValue")) { + func = GetNSValueToManagedFunc (underlyingManagedType, inputType, outputType, descriptiveMethodName, out nativeTypeName); + } else if (underlyingNativeType.Is (Foundation, "NSString")) { + func = GetNSStringToSmartEnumFunc (underlyingManagedType, inputType, outputType, descriptiveMethodName, managedClassExpression, out nativeTypeName); + } else { + throw ErrorHelper.CreateError (99, $"Internal error: can't convert from '{inputType.FullName}' to '{outputType.FullName}' in {descriptiveMethodName}. Please file a bug report with a test case (https://bugzilla.xamarin.com)."); + } + var classVariableName = $"{inputName}_conv_class"; + body_setup.AppendLine ($"MonoClass *{classVariableName} = NULL;"); + sb.AppendLine ($"{classVariableName} = {managedClassExpression};"); + if (isManagedArray) { + sb.AppendLine ($"{outputName} = xamarin_convert_nsarray_to_managed_with_func ({inputName}, {classVariableName}, (xamarin_id_to_managed_func) {func}, &exception_gchandle);"); + sb.AppendLine ("if (exception_gchandle != 0) goto exception_handling;"); + } else { + var tmpName = $"{inputName}_conv_tmp"; + body_setup.AppendLine ($"{nativeTypeName} {tmpName};"); + if (isManagedNullable) { + var tmpName2 = $"{inputName}_conv_ptr"; + body_setup.AppendLine ($"void *{tmpName2} = NULL;"); + sb.AppendLine ($"{tmpName2} = {func} ({inputName}, &{tmpName}, {classVariableName}, &exception_gchandle);"); + sb.AppendLine ("if (exception_gchandle != 0) goto exception_handling;"); + sb.AppendLine ($"{outputName} = mono_value_box (mono_domain_get (), {classVariableName}, {tmpName2});"); + } else { + sb.AppendLine ($"{outputName} = {func} ({inputName}, &{tmpName}, {classVariableName}, &exception_gchandle);"); + sb.AppendLine ("if (exception_gchandle != 0) goto exception_handling;"); + } + } + + if (isManagedNullable || isManagedArray) { + sb.AppendLine ($"}} else {{"); + sb.AppendLine ($"{outputName} = NULL;"); + sb.AppendLine ($"}}"); + } + } + + void GenerateConversionToNative (TypeReference inputType, TypeReference outputType, AutoIndentStringBuilder sb, string descriptiveMethodName, ref List exceptions, ObjCMethod method, string inputName, string outputName, string managedClassExpression) + { + // This is a mirror of the native method xamarin_generate_conversion_to_native (for the dynamic registrar). + // These methods must be kept in sync. + var managedType = inputType; + var nativeType = outputType; + + var isManagedNullable = IsNullable (managedType); + + var underlyingManagedType = managedType; + var underlyingNativeType = nativeType; + + var isManagedArray = IsArray (managedType); + var isNativeArray = IsArray (nativeType); + + if (isManagedArray != isNativeArray) + throw ErrorHelper.CreateError (99, $"Internal error: can't convert from '{inputType.FullName}' to '{outputType.FullName}' in {descriptiveMethodName}. Please file a bug report with a test case (https://bugzilla.xamarin.com)."); + + if (isManagedArray) { + if (isManagedNullable) + throw ErrorHelper.CreateError (99, $"Internal error: can't convert from '{inputType.FullName}' to '{outputType.FullName}' in {descriptiveMethodName}. Please file a bug report with a test case (https://bugzilla.xamarin.com)."); + underlyingNativeType = GetElementType (nativeType); + underlyingManagedType = GetElementType (managedType); + managedClassExpression = $"mono_class_get_element_class ({managedClassExpression})"; + } else if (isManagedNullable) { + underlyingManagedType = GetNullableType (managedType); + managedClassExpression = $"mono_class_get_nullable_param ({managedClassExpression})"; + } + + if (isManagedNullable || isManagedArray) + sb.AppendLine ($"if ({inputName}) {{"); + + string func; + if (underlyingNativeType.Is (Foundation, "NSNumber")) { + func = GetManagedToNSNumberFunc (underlyingManagedType, inputType, outputType, descriptiveMethodName); + } else if (underlyingNativeType.Is (Foundation, "NSValue")) { + func = GetManagedToNSValueFunc (underlyingManagedType, inputType, outputType, descriptiveMethodName); + } else if (underlyingNativeType.Is (Foundation, "NSString")) { + func = GetSmartEnumToNSStringFunc (underlyingManagedType, inputType, outputType, descriptiveMethodName, managedClassExpression); + } else { + throw ErrorHelper.CreateError (99, $"Internal error: can't convert from '{inputType.FullName}' to '{outputType.FullName}' in {descriptiveMethodName}. Please file a bug report with a test case (https://bugzilla.xamarin.com)."); + } + + if (isManagedArray) { + sb.AppendLine ($"{outputName} = xamarin_convert_managed_to_nsarray_with_func ((MonoArray *) {inputName}, (xamarin_managed_to_id_func) {func}, &exception_gchandle);"); + } else { + sb.AppendLine ($"{outputName} = {func} ({inputName}, &exception_gchandle);"); + } + sb.AppendLine ($"if (exception_gchandle != 0) goto exception_handling;"); + + if (isManagedNullable || isManagedArray) { + sb.AppendLine ($"}} else {{"); + sb.AppendLine ($"{outputName} = NULL;"); + sb.AppendLine ($"}}"); + } + } + class Body { public string Code; public string Signature; @@ -3888,6 +4268,17 @@ class ProtocolAttribute : Attribute { public Version FormalSinceVersion { get; set; } } + class BindAsAttribute : Attribute + { + public BindAsAttribute (TypeDefinition type) + { + this.Type = type; + } + + public TypeDefinition Type { get; set; } + public TypeReference OriginalType { get; set; } + } + public sealed class ProtocolMemberAttribute : Attribute { public ProtocolMemberAttribute () {} From 1165a1aac6b619d12e1555aebfbcb8d6ade8e8fc Mon Sep 17 00:00:00 2001 From: Rolf Bjarne Kvinge Date: Tue, 4 Jul 2017 18:33:31 +0200 Subject: [PATCH 11/46] [registrar] Don't require private mono symbols for BindAs support. The two functions mono_class_is_nullable and mono_class_get_nullable_param are private mono symbols, which means we can't call them when using Xamarin.Mac with libmono from a dynamic library. Implement a fallback for this case, where we call a managed method when these functions are not available (and restrict this workaround to Xamarin.Mac only, since it's not needed for Xamarin.iOS). --- runtime/exports.t4 | 4 +-- runtime/mono-runtime.h.t4 | 7 +++++ runtime/mono-runtime.m.t4 | 10 ++++++ runtime/runtime.m | 48 ++++++++++++++++++++++++++++- runtime/trampolines.m | 18 +++++++---- runtime/xamarin/runtime.h | 2 ++ src/ObjCRuntime/DynamicRegistrar.cs | 2 +- src/ObjCRuntime/Registrar.cs | 2 +- src/ObjCRuntime/Runtime.mac.cs | 6 ++++ tools/common/StaticRegistrar.cs | 25 +++++++++------ 10 files changed, 104 insertions(+), 20 deletions(-) diff --git a/runtime/exports.t4 b/runtime/exports.t4 index b6b0abb4a934..7d29a3f1d658 100644 --- a/runtime/exports.t4 +++ b/runtime/exports.t4 @@ -79,11 +79,11 @@ #endregion #region metadata/class-internals.h - new Export ("gboolean", "mono_class_is_nullable", + new Export (true, "gboolean", "mono_class_is_nullable", "MonoClass *", "klass" ), - new Export ("MonoClass *", "mono_class_get_nullable_param", + new Export (true, "MonoClass *", "mono_class_get_nullable_param", "MonoClass *", "klass" ), #endregion diff --git a/runtime/mono-runtime.h.t4 b/runtime/mono-runtime.h.t4 index 1149fc5ef946..a5921a7c057e 100644 --- a/runtime/mono-runtime.h.t4 +++ b/runtime/mono-runtime.h.t4 @@ -238,6 +238,13 @@ MONO_API <#= export.ReturnType #> <#= export.EntryPoint #> (<#= export.ArgumentSignature #>); <# } #> +<# foreach (var export in exports) { + if (!export.Optional) + continue; #> +bool +<#= export.EntryPoint #>_exists (); + +<# } #> #ifdef __cplusplus } /* extern "C" */ #endif diff --git a/runtime/mono-runtime.m.t4 b/runtime/mono-runtime.m.t4 index 118fa52449e0..87ce21417a8f 100644 --- a/runtime/mono-runtime.m.t4 +++ b/runtime/mono-runtime.m.t4 @@ -130,6 +130,16 @@ MONO_API <#= export.ReturnType #> <# } #> return <#= export.EntryPoint #>_func (<#= export.ArgumentNames #>); } +<# } #> + +<# foreach (var export in exports) { + if (!export.Optional) + continue; #> +bool +<#= export.EntryPoint #>_exists () +{ + return <#= export.EntryPoint #>_func != NULL; +} <# } #> #else diff --git a/runtime/runtime.m b/runtime/runtime.m index e9c20f12487e..cb39d62e54c5 100644 --- a/runtime/runtime.m +++ b/runtime/runtime.m @@ -96,6 +96,7 @@ static MonoClass *nsvalue_class; static MonoClass *nsnumber_class; static MonoClass *nsstring_class; +static MonoClass *runtime_class; static pthread_mutex_t framework_peer_release_lock; static MonoGHashTable *xamarin_wrapper_hash; @@ -419,6 +420,52 @@ void xamarin_framework_peer_unlock () return mono_class_is_subclass_of (cls, nsstring_class, false); } +// Returns if a MonoClass is nullable. +// Will also return the element type (it the type is nullable, and if out pointer is not NULL). +bool +xamarin_is_class_nullable (MonoClass *cls, MonoClass **element_type, guint32 *exception_gchandle) +{ +#ifdef DYNAMIC_MONO_RUNTIME + // mono_class_is_nullable/mono_class_get_nullable_param are private + // functions, and as such we can't call find them in libmono.dylib. In + // this case we manually call a managed function to do the work for us (we + // don't use the normal delegate mechanism, because how it's currently + // implemented it would inflict size costs on all platforms, not just + // Xamarin.Mac). + if (!mono_class_is_nullable_exists () || !mono_class_get_nullable_param_exists ()) { + static MonoMethod *get_nullable_type = NULL; + + if (get_nullable_type == NULL) + get_nullable_type = mono_class_get_method_from_name (runtime_class, "GetNullableType", 1); + + void *args [1] { mono_type_get_object (mono_domain_get (), mono_class_get_type (cls)) }; + MonoObject *exc = NULL; + MonoReflectionType *nullable_type = (MonoReflectionType *) mono_runtime_invoke (get_nullable_type, NULL, args, &exc); + if (exc != NULL) { + *exception_gchandle = mono_gchandle_new (exc, FALSE); + return false; + } + + if (element_type != NULL && nullable_type != NULL) + *element_type = mono_class_from_mono_type (mono_reflection_type_get_type (nullable_type)); + return nullable_type != NULL; + } +#endif + + bool rv = mono_class_is_nullable (cls); + if (rv && element_type) + *element_type = mono_class_get_nullable_param (cls); + return rv; +} + +MonoClass * +xamarin_get_nullable_type (MonoClass *cls, guint32 *exception_gchandle) +{ + MonoClass *rv = NULL; + xamarin_is_class_nullable (cls, &rv, exception_gchandle); + return rv; +} + #define MANAGED_REF_BIT (1 << 31) #define GCHANDLE_WEAK (1 << 30) #define GCHANDLE_MASK (MANAGED_REF_BIT | GCHANDLE_WEAK) @@ -1199,7 +1246,6 @@ -(void) xamarinSetGCHandle: (int) gc_handle; // COOP: accessing managed memory: UNSAFE mode MONO_ASSERT_GC_UNSAFE; - MonoClass *runtime_class; MonoAssembly *assembly = NULL; MonoMethod *runtime_initialize; void* params[2]; diff --git a/runtime/trampolines.m b/runtime/trampolines.m index 8ca1013136cf..e5111923c1ed 100644 --- a/runtime/trampolines.m +++ b/runtime/trampolines.m @@ -627,14 +627,17 @@ MonoClass *managedType = mono_class_from_mono_type (inputType); MonoClass *nativeType = mono_class_from_mono_type (outputType); - bool isManagedNullable = mono_class_is_nullable (managedType); - MonoClass *underlyingManagedType = managedType; MonoClass *underlyingNativeType = nativeType; bool isManagedArray = xamarin_is_class_array (managedType); bool isNativeArray = xamarin_is_class_array (nativeType); + MonoClass *nullableManagedType = NULL; + bool isManagedNullable = xamarin_is_class_nullable (managedType, &nullableManagedType, exception_gchandle); + if (*exception_gchandle != 0) + goto exception_handling; + if (isManagedArray != isNativeArray) { *exception_gchandle = xamarin_create_bindas_exception (inputType, outputType, method); goto exception_handling; @@ -648,7 +651,7 @@ underlyingNativeType = mono_class_get_element_class (nativeType); underlyingManagedType = mono_class_get_element_class (managedType); } else if (isManagedNullable) { - underlyingManagedType = mono_class_get_nullable_param (managedType); + underlyingManagedType = nullableManagedType; } if (value) { @@ -696,14 +699,17 @@ MonoClass *managedType = mono_class_from_mono_type (outputType); MonoClass *nativeType = mono_class_from_mono_type (inputType); - bool isManagedNullable = mono_class_is_nullable (managedType); - MonoClass *underlyingManagedType = managedType; MonoClass *underlyingNativeType = nativeType; bool isManagedArray = xamarin_is_class_array (managedType); bool isNativeArray = xamarin_is_class_array (nativeType); + MonoClass *nullableManagedType = NULL; + bool isManagedNullable = xamarin_is_class_nullable (managedType, &nullableManagedType, exception_gchandle); + if (*exception_gchandle != 0) + goto exception_handling; + if (isManagedArray != isNativeArray) { *exception_gchandle = xamarin_create_bindas_exception (inputType, outputType, method); goto exception_handling; @@ -717,7 +723,7 @@ underlyingNativeType = mono_class_get_element_class (nativeType); underlyingManagedType = mono_class_get_element_class (managedType); } else if (isManagedNullable) { - underlyingManagedType = mono_class_get_nullable_param (managedType); + underlyingManagedType = nullableManagedType; } if (value) { diff --git a/runtime/xamarin/runtime.h b/runtime/xamarin/runtime.h index e4b6b47ed50f..0b78f9ca6453 100644 --- a/runtime/xamarin/runtime.h +++ b/runtime/xamarin/runtime.h @@ -135,6 +135,8 @@ bool xamarin_is_class_array (MonoClass *cls); bool xamarin_is_class_nsnumber (MonoClass *cls); bool xamarin_is_class_nsvalue (MonoClass *cls); bool xamarin_is_class_nsstring (MonoClass *cls); +bool xamarin_is_class_nullable (MonoClass *cls, MonoClass **element_type, guint32 *exception_gchandle); +MonoClass * xamarin_get_nullable_type (MonoClass *cls, guint32 *exception_gchandle); MonoType * xamarin_get_parameter_type (MonoMethod *managed_method, int index); MonoObject * xamarin_get_nsobject_with_type_for_ptr (id self, bool owns, MonoType* type, guint32 *exception_gchandle); MonoObject * xamarin_get_nsobject_with_type_for_ptr_created (id self, bool owns, MonoType *type, int32_t *created, guint32 *exception_gchandle); diff --git a/src/ObjCRuntime/DynamicRegistrar.cs b/src/ObjCRuntime/DynamicRegistrar.cs index 21c923cce369..ee63bef9c17f 100644 --- a/src/ObjCRuntime/DynamicRegistrar.cs +++ b/src/ObjCRuntime/DynamicRegistrar.cs @@ -269,7 +269,7 @@ protected override BindAsAttribute GetBindAsAttribute (MethodBase method, int pa return (BindAsAttribute) attribs [0]; } - protected override Type GetNullableType (Type type) + public override Type GetNullableType (Type type) { if (!type.IsGenericType) return null; diff --git a/src/ObjCRuntime/Registrar.cs b/src/ObjCRuntime/Registrar.cs index 227d616fc6b5..f48eddfc4eaa 100644 --- a/src/ObjCRuntime/Registrar.cs +++ b/src/ObjCRuntime/Registrar.cs @@ -1016,7 +1016,7 @@ protected virtual void OnRegisterCategory (ObjCType type, ref List ex protected abstract TType GetProtocolAttributeWrapperType (TType type); // Return null if no attribute is found. Do not consider base types. protected abstract BindAsAttribute GetBindAsAttribute (TMethod method, int parameter_index); // If parameter_index = -1 then get the attribute for the return type. Return null if no attribute is found. Must consider base method. protected abstract BindAsAttribute GetBindAsAttribute (TProperty property); - protected abstract TType GetNullableType (TType type); // For T? returns T. For T returns null. + public abstract TType GetNullableType (TType type); // For T? returns T. For T returns null. protected abstract bool HasReleaseAttribute (TMethod method); // Returns true of the method's return type/value has a [Release] attribute. protected abstract bool IsINativeObject (TType type); protected abstract bool IsValueType (TType type); diff --git a/src/ObjCRuntime/Runtime.mac.cs b/src/ObjCRuntime/Runtime.mac.cs index 3709cf740b09..9d7fe260b54d 100644 --- a/src/ObjCRuntime/Runtime.mac.cs +++ b/src/ObjCRuntime/Runtime.mac.cs @@ -140,6 +140,12 @@ unsafe static void InitializePlatform (InitializationOptions* options) ResourcesPath = Path.Combine (basePath, "Resources"); FrameworksPath = Path.Combine (basePath, "Frameworks"); } + + [Preserve] + static IntPtr GetNullableType (IntPtr type) + { + return ObjectWrapper.Convert (Registrar.GetNullableType ((Type) ObjectWrapper.Convert (type))); + } #endif // !COREBUILD } } diff --git a/tools/common/StaticRegistrar.cs b/tools/common/StaticRegistrar.cs index 93d1e9e47a44..77321eec0cf3 100644 --- a/tools/common/StaticRegistrar.cs +++ b/tools/common/StaticRegistrar.cs @@ -1596,7 +1596,7 @@ static BindAsAttribute CreateBindAsAttribute (CustomAttribute attrib, IMemberDef } } - protected override TypeReference GetNullableType (TypeReference type) + public override TypeReference GetNullableType (TypeReference type) { var git = type as GenericInstanceType; if (git == null) @@ -3852,15 +3852,20 @@ void GenerateConversionToManaged (TypeReference inputType, TypeReference outputT if (isManagedArray != isNativeArray) throw ErrorHelper.CreateError (99, $"Internal error: can't convert from '{inputType.FullName}' to '{outputType.FullName}' in {descriptiveMethodName}. Please file a bug report with a test case (https://bugzilla.xamarin.com)."); + var classVariableName = $"{inputName}_conv_class"; + body_setup.AppendLine ($"MonoClass *{classVariableName} = NULL;"); if (isManagedArray) { if (isManagedNullable) throw ErrorHelper.CreateError (99, $"Internal error: can't convert from '{inputType.FullName}' to '{outputType.FullName}' in {descriptiveMethodName}. Please file a bug report with a test case (https://bugzilla.xamarin.com)."); underlyingNativeType = GetElementType (nativeType); underlyingManagedType = GetElementType (managedType); - managedClassExpression = $"mono_class_get_element_class ({managedClassExpression})"; + sb.AppendLine ($"{classVariableName} = mono_class_get_element_class ({managedClassExpression});"); } else if (isManagedNullable) { underlyingManagedType = GetNullableType (managedType); - managedClassExpression = $"mono_class_get_nullable_param ({managedClassExpression})"; + sb.AppendLine ($"{classVariableName} = xamarin_get_nullable_type ({managedClassExpression}, &exception_gchandle);"); + sb.AppendLine ($"if (exception_gchandle != 0) goto exception_handling;"); + } else { + sb.AppendLine ($"{classVariableName} = {managedClassExpression};"); } if (isManagedNullable || isManagedArray) @@ -3877,9 +3882,6 @@ void GenerateConversionToManaged (TypeReference inputType, TypeReference outputT } else { throw ErrorHelper.CreateError (99, $"Internal error: can't convert from '{inputType.FullName}' to '{outputType.FullName}' in {descriptiveMethodName}. Please file a bug report with a test case (https://bugzilla.xamarin.com)."); } - var classVariableName = $"{inputName}_conv_class"; - body_setup.AppendLine ($"MonoClass *{classVariableName} = NULL;"); - sb.AppendLine ($"{classVariableName} = {managedClassExpression};"); if (isManagedArray) { sb.AppendLine ($"{outputName} = xamarin_convert_nsarray_to_managed_with_func ({inputName}, {classVariableName}, (xamarin_id_to_managed_func) {func}, &exception_gchandle);"); sb.AppendLine ("if (exception_gchandle != 0) goto exception_handling;"); @@ -3923,15 +3925,20 @@ void GenerateConversionToNative (TypeReference inputType, TypeReference outputTy if (isManagedArray != isNativeArray) throw ErrorHelper.CreateError (99, $"Internal error: can't convert from '{inputType.FullName}' to '{outputType.FullName}' in {descriptiveMethodName}. Please file a bug report with a test case (https://bugzilla.xamarin.com)."); + var classVariableName = $"{inputName}_conv_class"; + body_setup.AppendLine ($"MonoClass *{classVariableName} = NULL;"); if (isManagedArray) { if (isManagedNullable) throw ErrorHelper.CreateError (99, $"Internal error: can't convert from '{inputType.FullName}' to '{outputType.FullName}' in {descriptiveMethodName}. Please file a bug report with a test case (https://bugzilla.xamarin.com)."); underlyingNativeType = GetElementType (nativeType); underlyingManagedType = GetElementType (managedType); - managedClassExpression = $"mono_class_get_element_class ({managedClassExpression})"; + sb.AppendLine ($"{classVariableName} = mono_class_get_element_class ({managedClassExpression});"); } else if (isManagedNullable) { underlyingManagedType = GetNullableType (managedType); - managedClassExpression = $"mono_class_get_nullable_param ({managedClassExpression})"; + sb.AppendLine ($"{classVariableName} = xamarin_get_nullable_type ({managedClassExpression}, &exception_gchandle);"); + sb.AppendLine ($"if (exception_gchandle != 0) goto exception_handling;"); + } else { + sb.AppendLine ($"{classVariableName} = {managedClassExpression};"); } if (isManagedNullable || isManagedArray) @@ -3943,7 +3950,7 @@ void GenerateConversionToNative (TypeReference inputType, TypeReference outputTy } else if (underlyingNativeType.Is (Foundation, "NSValue")) { func = GetManagedToNSValueFunc (underlyingManagedType, inputType, outputType, descriptiveMethodName); } else if (underlyingNativeType.Is (Foundation, "NSString")) { - func = GetSmartEnumToNSStringFunc (underlyingManagedType, inputType, outputType, descriptiveMethodName, managedClassExpression); + func = GetSmartEnumToNSStringFunc (underlyingManagedType, inputType, outputType, descriptiveMethodName, classVariableName); } else { throw ErrorHelper.CreateError (99, $"Internal error: can't convert from '{inputType.FullName}' to '{outputType.FullName}' in {descriptiveMethodName}. Please file a bug report with a test case (https://bugzilla.xamarin.com)."); } From 30a3b27358ee8d6d611cf3649c9b99c47f5ae046 Mon Sep 17 00:00:00 2001 From: Rolf Bjarne Kvinge Date: Tue, 4 Jul 2017 18:56:42 +0200 Subject: [PATCH 12/46] [generator] Don't use extension methods in generated code. Extension methods require the containing class' namespace to be in a `using`, which we can't easily do (currently) for user code, since we hardcode all our `using`s. So just call the static extension method directly instead of relying on syntax sugar. --- src/generator.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/generator.cs b/src/generator.cs index 63272def0727..33f1f797a2f2 100644 --- a/src/generator.cs +++ b/src/generator.cs @@ -1284,14 +1284,14 @@ string GetToBindAsWrapper (MemberInformation minfo = null, ParameterInfo pi = nu } temp = string.Format ("{3}NSValue.From{0} ({2}{1});", typeStr, denullify, parameterName, nullCheck); } else if (originalType == TypeManager.NSString && IsSmartEnum (retType)) { - temp = $"{parameterName}.GetConstant ();"; + temp = $"{FormatType (retType.DeclaringType, retType)}Extensions.GetConstant ({parameterName});"; } else if (originalType.IsArray) { var arrType = originalType.GetElementType (); var arrRetType = TypeManager.GetUnderlyingNullableType (retType.GetElementType ()) ?? retType.GetElementType (); var valueConverter = string.Empty; if (arrType == TypeManager.NSString) - valueConverter = $"o.GetConstant (), {parameterName});"; + valueConverter = $"{FormatType (retType.DeclaringType, arrRetType)}Extensions.GetConstant (o), {parameterName});"; else if (arrType == TypeManager.NSNumber) { var cast = arrRetType.IsEnum ? "(int)" : string.Empty; valueConverter = $"new NSNumber ({cast}o{denullify}), {parameterName});"; From 4ee7792151d8615d5449fdb39a118c3717337c15 Mon Sep 17 00:00:00 2001 From: Rolf Bjarne Kvinge Date: Tue, 4 Jul 2017 18:58:17 +0200 Subject: [PATCH 13/46] [tests] Use a smart enum type available in all profiles for generated tests. --- tests/test-libraries/testgenerator.cs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/tests/test-libraries/testgenerator.cs b/tests/test-libraries/testgenerator.cs index 506f6bfafd7b..0b61979f587f 100644 --- a/tests/test-libraries/testgenerator.cs +++ b/tests/test-libraries/testgenerator.cs @@ -96,7 +96,7 @@ class BindAsData }; static BindAsData [] bindas_nsstring = new [] { - new BindAsData { Managed = "AVMediaTypes", ManagedNewExpression = "AVMediaTypes.Audio" }, + new BindAsData { Managed = "SecKeyAlgorithm", ManagedNewExpression = "SecKeyAlgorithm.RsaSignatureRaw" }, }; static string GetNativeName (char t) @@ -363,6 +363,7 @@ static void WriteApiDefinition () #endif using ObjCRuntime; using SceneKit; +using Security; #if HAVE_UIKIT using UIKit; #endif @@ -779,6 +780,7 @@ static void WriteRegistrarTests () #endif using ObjCRuntime; using SceneKit; +using Security; #if HAVE_UIKIT using UIKit; #endif From 66a25bb6efcb496e27e20a377298d5932a562354 Mon Sep 17 00:00:00 2001 From: Rolf Bjarne Kvinge Date: Tue, 4 Jul 2017 20:07:15 +0200 Subject: [PATCH 14/46] [mtouch] Minor adjustments to get error messages right. --- src/ObjCRuntime/Registrar.cs | 8 ++++--- tests/mtouch/RegistrarTest.cs | 39 ++++++++++++++++++++++++++--------- 2 files changed, 34 insertions(+), 13 deletions(-) diff --git a/src/ObjCRuntime/Registrar.cs b/src/ObjCRuntime/Registrar.cs index f48eddfc4eaa..8c7f2c337a9e 100644 --- a/src/ObjCRuntime/Registrar.cs +++ b/src/ObjCRuntime/Registrar.cs @@ -2327,9 +2327,11 @@ public string ComputeSignature (TType DeclaringType, TMethod Method, ObjCMember } else { signature.Append (ToSignature (type, member, ref success)); } - if (!success) - throw CreateException (4136, Method, "The registrar cannot marshal the parameter type '{0}' of the parameter '{1}' in the method '{2}.{3}'", - GetTypeFullName (type), GetParameterName (Method, i), GetTypeFullName (DeclaringType), GetDescriptiveMethodName (Method)); + if (!success) { + var mi = Method ?? method.Method; + throw CreateException (4136, mi, "The registrar cannot marshal the parameter type '{0}' of the parameter '{1}' in the method '{2}.{3}'", + GetTypeFullName (GetParameters (mi) [i]), GetParameterName (mi, i), GetTypeFullName (DeclaringType), GetDescriptiveMethodName (mi)); + } } } return signature.ToString (); diff --git a/tests/mtouch/RegistrarTest.cs b/tests/mtouch/RegistrarTest.cs index 3576c1e91b54..3767652d7da0 100644 --- a/tests/mtouch/RegistrarTest.cs +++ b/tests/mtouch/RegistrarTest.cs @@ -1071,15 +1071,25 @@ public override void Arg1 (UIView t) {} public void GenericType_WithInvalidParameterTypes () { var code = @" + using System.Collections.Generic; + using Foundation; class Open : NSObject where U: NSObject { [Export (""bar:"")] public void Bar (List arg) {} // Not OK, can't marshal lists. } + class C { static void Main () {} } "; - Verify (R.Static, code, false, - ".*Test.cs.*: error MT4136: The registrar cannot marshal the parameter type 'System.Collections.Generic.List`1' of the parameter 'arg' in the method 'Open`1.Bar(System.Collections.Generic.List`1)'"); + using (var mtouch = new MTouchTool ()) { + mtouch.CreateTemporaryCacheDirectory (); + mtouch.CreateTemporaryApp (code: code, extraArg: "-debug:full"); + mtouch.Registrar = MTouchRegistrar.Static; + mtouch.Linker = MTouchLinker.DontLink; // faster test + mtouch.AssertExecuteFailure (MTouchAction.BuildSim, "build"); + mtouch.AssertError (4136, "The registrar cannot marshal the parameter type 'System.Collections.Generic.List`1' of the parameter 'arg' in the method 'Open`1.Bar(System.Collections.Generic.List`1)'", "testApp.cs", 7); + mtouch.AssertErrorCount (1); + } } [Test] @@ -1192,6 +1202,7 @@ public virtual void Foo (System.Action func) {} public void GenericMethods2 () { var str1 = @" +using Foundation; class NullableGenericTestClass : NSObject where T: struct { [Export (""init:"")] @@ -1231,15 +1242,23 @@ public T T3 () throw new System.NotImplementedException (); } } +class C { static void Main () {} } "; - Verify (R.Static, str1, false, - ".*Test.cs.*: error MT4113: The registrar found a generic method: 'NullableGenericTestClass`1.Z1(System.Nullable`1)'. Exporting generic methods is not supported, and will lead to random behavior and/or crashes", - ".*Test.cs.*: error MT4113: The registrar found a generic method: 'NullableGenericTestClass`1.Z2()'. Exporting generic methods is not supported, and will lead to random behavior and/or crashes", - ".*Test.cs.*: error MT4113: The registrar found a generic method: 'NullableGenericTestClass`1.Z3(Z)'. Exporting generic methods is not supported, and will lead to random behavior and/or crashes", - ".*Test.cs.*: error MT4128: The registrar found an invalid generic parameter type 'T' in the parameter foo of the method 'NullableGenericTestClass`1.T1(T)'. The generic parameter must have an 'NSObject' constraint.", - ".*Test.cs.*: error MT4128: The registrar found an invalid generic parameter type 'System.Nullable`1' in the parameter foo of the method 'NullableGenericTestClass`1.T2(System.Nullable`1)'. The generic parameter must have an 'NSObject' constraint.", - ".*Test.cs.*: error MT4129: The registrar found an invalid generic return type 'T' in the method 'NullableGenericTestClass`1.T3()'. The generic return type must have an 'NSObject' constraint.", - ".*Test.cs.*: error MT4136: The registrar cannot marshal the parameter type 'System.Nullable`1' of the parameter 'foo' in the method 'NullableGenericTestClass`1..ctor(System.Nullable`1)'"); + using (var mtouch = new MTouchTool ()) { + mtouch.CreateTemporaryCacheDirectory (); + mtouch.CreateTemporaryApp (code: str1, extraArg: "-debug:full"); + mtouch.Registrar = MTouchRegistrar.Static; + mtouch.Linker = MTouchLinker.DontLink; // faster test + mtouch.AssertExecuteFailure (MTouchAction.BuildSim, "build"); + mtouch.AssertError (4113, "The registrar found a generic method: 'NullableGenericTestClass`1.Z1(System.Nullable`1)'. Exporting generic methods is not supported, and will lead to random behavior and/or crashes", "testApp.cs", 12); + mtouch.AssertError (4113, "The registrar found a generic method: 'NullableGenericTestClass`1.Z2()'. Exporting generic methods is not supported, and will lead to random behavior and/or crashes", "testApp.cs", 17); + mtouch.AssertError (4113, "The registrar found a generic method: 'NullableGenericTestClass`1.Z3(Z)'. Exporting generic methods is not supported, and will lead to random behavior and/or crashes", "testApp.cs", 23); + mtouch.AssertError (4128, "The registrar found an invalid generic parameter type 'T' in the parameter foo of the method 'NullableGenericTestClass`1.T1(T)'. The generic parameter must have an 'NSObject' constraint.", "testApp.cs", 28); + mtouch.AssertError (4128, "The registrar found an invalid generic parameter type 'System.Nullable`1' in the parameter foo of the method 'NullableGenericTestClass`1.T2(System.Nullable`1)'. The generic parameter must have an 'NSObject' constraint.", "testApp.cs", 33); + mtouch.AssertError (4129, "The registrar found an invalid generic return type 'T' in the method 'NullableGenericTestClass`1.T3()'. The generic return type must have an 'NSObject' constraint.", "testApp.cs", 38); + mtouch.AssertError (4136, "The registrar cannot marshal the parameter type 'System.Nullable`1' of the parameter 'foo' in the method 'NullableGenericTestClass`1..ctor(System.Nullable`1)'", "testApp.cs", 6); + mtouch.AssertErrorCount (7); + } } [Test] From 580b55215700b5af530eab9e4144ef50c94bb88e Mon Sep 17 00:00:00 2001 From: Rolf Bjarne Kvinge Date: Tue, 4 Jul 2017 20:10:54 +0200 Subject: [PATCH 15/46] [registrar] Make mmp-specific code mmp-specific. --- tools/common/StaticRegistrar.cs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/tools/common/StaticRegistrar.cs b/tools/common/StaticRegistrar.cs index 77321eec0cf3..e7415ab1acd3 100644 --- a/tools/common/StaticRegistrar.cs +++ b/tools/common/StaticRegistrar.cs @@ -3765,9 +3765,11 @@ string GetNSValueToManagedFunc (TypeReference managedType, TypeReference inputTy { var underlyingTypeName = managedType.FullName; +#if MMP // Remove 'MonoMac.' namespace prefix to make switch smaller if (!Registrar.IsDualBuild && underlyingTypeName.StartsWith ("MonoMac.", StringComparison.Ordinal)) underlyingTypeName = underlyingTypeName.Substring ("MonoMac.".Length); +#endif switch (underlyingTypeName) { case "Foundation.NSRange": nativeType = "NSRange"; return "xamarin_nsvalue_to_nsrange"; @@ -3796,9 +3798,11 @@ string GetManagedToNSValueFunc (TypeReference managedType, TypeReference inputTy { var underlyingTypeName = managedType.FullName; +#if MMP // Remove 'MonoMac.' namespace prefix to make switch smaller if (!Registrar.IsDualBuild && underlyingTypeName.StartsWith ("MonoMac.", StringComparison.Ordinal)) underlyingTypeName = underlyingTypeName.Substring ("MonoMac.".Length); +#endif switch (underlyingTypeName) { case "Foundation.NSRange": return "xamarin_nsrange_to_nsvalue"; From 5e7768e5ff8412c4f289d11dbc86e38538399424 Mon Sep 17 00:00:00 2001 From: Rolf Bjarne Kvinge Date: Wed, 5 Jul 2017 08:54:42 +0200 Subject: [PATCH 16/46] [mtouch] Minor adjustments to get error messages right. --- src/ObjCRuntime/Registrar.cs | 2 +- tests/mtouch/RegistrarTest.cs | 36 ++++++++++++++++++++++------------- 2 files changed, 24 insertions(+), 14 deletions(-) diff --git a/src/ObjCRuntime/Registrar.cs b/src/ObjCRuntime/Registrar.cs index 8c7f2c337a9e..dcb107b5f6ad 100644 --- a/src/ObjCRuntime/Registrar.cs +++ b/src/ObjCRuntime/Registrar.cs @@ -2304,7 +2304,7 @@ public string ComputeSignature (TType DeclaringType, TMethod Method, ObjCMember var ReturnType = Method != null ? GetReturnType (Method) : method.NativeReturnType; signature.Append (ToSignature (ReturnType, member, ref success)); if (!success) - throw CreateException (4104, Method, "The registrar cannot marshal the return value of type `{0}` in the method `{1}.{2}`.", GetTypeFullName (ReturnType), GetTypeFullName (DeclaringType), GetDescriptiveMethodName (Method)); + throw CreateException (4104, Method ?? method.Method, "The registrar cannot marshal the return value of type `{0}` in the method `{1}.{2}`.", GetTypeFullName (ReturnType), GetTypeFullName (DeclaringType), GetDescriptiveMethodName (Method ?? method.Method)); } signature.Append (isBlockSignature ? "@?" : "@:"); diff --git a/tests/mtouch/RegistrarTest.cs b/tests/mtouch/RegistrarTest.cs index 3767652d7da0..16e30ac71076 100644 --- a/tests/mtouch/RegistrarTest.cs +++ b/tests/mtouch/RegistrarTest.cs @@ -23,6 +23,8 @@ enum R { public void InvalidParameterTypes () { var code = @" +using System; +using Foundation; class Foo : NSObject { [Export (""bar1:"")] public void Bar1 (object[] arg) @@ -82,20 +84,28 @@ public object[] Bar11 { set {} } } +class C { static void Main () {} } "; - Verify (R.Static, code, false, - ".*/Test.cs(.*): error MT4138: The registrar cannot marshal the property type 'System.Object' of the property 'Foo.Bar10'.", - ".*/Test.cs(.*): error MT4136: The registrar cannot marshal the parameter type 'System.Object[]' of the parameter 'arg' in the method 'Foo.Bar1(System.Object[])'", - ".*/Test.cs(.*): error MT4136: The registrar cannot marshal the parameter type 'System.Object&' of the parameter 'arg' in the method 'Foo.Bar2(System.Object&)'", - ".*/Test.cs(.*): error MT4136: The registrar cannot marshal the parameter type 'System.Object&' of the parameter 'arg' in the method 'Foo.Bar3(System.Object&)'", - ".*/Test.cs(.*): error MT4136: The registrar cannot marshal the parameter type 'System.Object' of the parameter 'arg' in the method 'Foo.Bar4(System.Object)'", - ".*/Test.cs(.*): error MT4104: The registrar cannot marshal the return value of type `System.Object` in the method `Foo.Bar5()`.", - ".*/Test.cs(.*): error MT4136: The registrar cannot marshal the parameter type 'System.Nullable`1' of the parameter 'arg' in the method 'Foo.Bar6(System.Nullable`1)'", - ".*/Test.cs(.*): error MT4104: The registrar cannot marshal the return value of type `System.Nullable`1` in the method `Foo.Bar7()`.", - ".*/Test.cs(.*): error MT4136: The registrar cannot marshal the parameter type 'System.Nullable`1[]&' of the parameter 'arg' in the method 'Foo.Bar8(System.Nullable`1[]&)'", - ".*/Test.cs(.*): error MT4136: The registrar cannot marshal the parameter type 'System.Attribute' of the parameter 'attribute' in the method 'Foo.Bar9(System.Attribute)'", - ".*/Test.cs(.*): error MT4104: The registrar cannot marshal the return value of type `System.Object[]` in the method `Foo.get_Bar11()`.", - ".*/Test.cs(.*): error MT4136: The registrar cannot marshal the parameter type 'System.Object[]' of the parameter 'value' in the method 'Foo.set_Bar11(System.Object[])'"); + + using (var mtouch = new MTouchTool ()) { + mtouch.Linker = MTouchLinker.DontLink; // faster + mtouch.Registrar = MTouchRegistrar.Static; + mtouch.CreateTemporaryApp (code: code, extraArg: "-debug"); + mtouch.AssertExecuteFailure (MTouchAction.BuildSim, "build"); + mtouch.AssertError (4138, "The registrar cannot marshal the property type 'System.Object' of the property 'Foo.Bar10'.", "testApp.cs", 54); + mtouch.AssertError (4136, "The registrar cannot marshal the parameter type 'System.Object[]' of the parameter 'arg' in the method 'Foo.Bar1(System.Object[])'", "testApp.cs", 7); + mtouch.AssertError (4136, "The registrar cannot marshal the parameter type 'System.Object&' of the parameter 'arg' in the method 'Foo.Bar2(System.Object&)'", "testApp.cs", 12); + mtouch.AssertError (4136, "The registrar cannot marshal the parameter type 'System.Object&' of the parameter 'arg' in the method 'Foo.Bar3(System.Object&)'", "testApp.cs", 17); + mtouch.AssertError (4136, "The registrar cannot marshal the parameter type 'System.Object' of the parameter 'arg' in the method 'Foo.Bar4(System.Object)'", "testApp.cs", 23); + mtouch.AssertError (4104, "The registrar cannot marshal the return value of type `System.Object` in the method `Foo.Bar5()`.", "testApp.cs", 28); + mtouch.AssertError (4136, "The registrar cannot marshal the parameter type 'System.Nullable`1' of the parameter 'arg' in the method 'Foo.Bar6(System.Nullable`1)'", "testApp.cs", 34); + mtouch.AssertError (4104, "The registrar cannot marshal the return value of type `System.Nullable`1` in the method `Foo.Bar7()`.", "testApp.cs", 39); + mtouch.AssertError (4136, "The registrar cannot marshal the parameter type 'System.Nullable`1[]&' of the parameter 'arg' in the method 'Foo.Bar8(System.Nullable`1[]&)'", "testApp.cs", 45); + mtouch.AssertError (4136, "The registrar cannot marshal the parameter type 'System.Attribute' of the parameter 'attribute' in the method 'Foo.Bar9(System.Attribute)'", "testApp.cs", 50); + mtouch.AssertError (4104, "The registrar cannot marshal the return value of type `System.Object[]` in the method `Foo.get_Bar11()`.", "testApp.cs", 58); + mtouch.AssertError (4136, "The registrar cannot marshal the parameter type 'System.Object[]' of the parameter 'value' in the method 'Foo.set_Bar11(System.Object[])'", "testApp.cs", 60); + mtouch.AssertErrorCount (12); + } } [Test] From a24fa2f8588f6e5f6eef8ffc72c721e8310576cb Mon Sep 17 00:00:00 2001 From: Rolf Bjarne Kvinge Date: Wed, 5 Jul 2017 09:03:25 +0200 Subject: [PATCH 17/46] [ObjCRuntime] Add a null check. --- src/ObjCRuntime/Runtime.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/ObjCRuntime/Runtime.cs b/src/ObjCRuntime/Runtime.cs index 97876b838ad5..ceaaffae7c43 100644 --- a/src/ObjCRuntime/Runtime.cs +++ b/src/ObjCRuntime/Runtime.cs @@ -318,6 +318,8 @@ static IntPtr ConvertSmartEnumToNSString (IntPtr value_handle) if (!Registrar.IsSmartEnum (smart_type, out getConstantMethod, out getValueMethod)) throw ErrorHelper.CreateError (8024, $"Could not find a valid extension type for the smart enum '{smart_type.FullName}'. Please file a bug at https://bugzilla.xamarin.com."); var rv = (NSString) ((MethodInfo) getConstantMethod).Invoke (null, new object [] { value }); + if (rv == null) + return IntPtr.Zero; rv.DangerousRetain ().DangerousAutorelease (); return rv.Handle; } From c6cd2335459de30e68196f8515822634ac0c6f45 Mon Sep 17 00:00:00 2001 From: Rolf Bjarne Kvinge Date: Wed, 5 Jul 2017 09:10:53 +0200 Subject: [PATCH 18/46] [registrar] Simplify code a bit and fix a FIXME. --- src/ObjCRuntime/DynamicRegistrar.cs | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/src/ObjCRuntime/DynamicRegistrar.cs b/src/ObjCRuntime/DynamicRegistrar.cs index ee63bef9c17f..5ccd3fcbc7c0 100644 --- a/src/ObjCRuntime/DynamicRegistrar.cs +++ b/src/ObjCRuntime/DynamicRegistrar.cs @@ -56,14 +56,21 @@ public static Dictionary> PrepareInterfaceMethodMap return rv; } - public static T GetOneAttribute (MemberInfo provider) where T : Attribute + public static T GetOneAttribute (ICustomAttributeProvider provider) where T : Attribute { var attribs = provider.GetCustomAttributes (typeof (T), false); if (attribs.Length == 0) return null; else if (attribs.Length == 1) return (T) attribs [0]; - throw new AmbiguousMatchException (string.Format ("The member '{0}' contains more than one '{1}'", provider.Name, typeof (T).FullName)); + var member = provider as MemberInfo; + if (member != null) + throw new AmbiguousMatchException (string.Format ("The member '{0}' contains more than one '{1}'", member.Name, typeof (T).FullName)); + var parameter = provider as ParameterInfo; + if (parameter != null) + throw new AmbiguousMatchException (string.Format ("The parameter '{0}' contains more than one '{1}'", parameter.Name, typeof (T).FullName)); + + throw new AmbiguousMatchException (string.Format ("The member '{0}' contains more than one '{1}'", provider, typeof (T).FullName)); } } @@ -259,14 +266,7 @@ protected override BindAsAttribute GetBindAsAttribute (MethodBase method, int pa } } - var attribs = provider.GetCustomAttributes (typeof (BindAsAttribute), false); - if (attribs.Length == 0) - return null; - - if (attribs.Length != 1) - throw new AmbiguousMatchException (/* FIXME */); - - return (BindAsAttribute) attribs [0]; + return SharedDynamic.GetOneAttribute (provider); } public override Type GetNullableType (Type type) From f6e6e7e14bb02d38e3fa85809ee3ed81caba61b1 Mon Sep 17 00:00:00 2001 From: Rolf Bjarne Kvinge Date: Wed, 5 Jul 2017 09:13:54 +0200 Subject: [PATCH 19/46] [registrar] Improve an error message. --- src/ObjCRuntime/DynamicRegistrar.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ObjCRuntime/DynamicRegistrar.cs b/src/ObjCRuntime/DynamicRegistrar.cs index 5ccd3fcbc7c0..2189a7e38a5e 100644 --- a/src/ObjCRuntime/DynamicRegistrar.cs +++ b/src/ObjCRuntime/DynamicRegistrar.cs @@ -260,7 +260,7 @@ protected override BindAsAttribute GetBindAsAttribute (MethodBase method, int pa } else { var cinfo = method as ConstructorInfo; if (parameter_index == -1) { - throw new Exception (); + throw ErrorHelper.CreateError (99, $"Internal error: can't get the BindAs attribute for the return value of a constructor ({GetDescriptiveMethodName (method)}). Please file a bug report with a test case (https://bugzilla.xamarin.com)."); } else { provider = cinfo.GetParameters () [parameter_index]; } From d44b9cb8244e4eedf8958009979eb35eb6a31579 Mon Sep 17 00:00:00 2001 From: Rolf Bjarne Kvinge Date: Wed, 5 Jul 2017 10:40:44 +0200 Subject: [PATCH 20/46] [generator] Print nicer typenames in the BindAs attribute. Diff (empty): https://gist.github.com/rolfbjarne/8604c61ce33f8352e7ee75cb5c7a318e --- src/generator.cs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/generator.cs b/src/generator.cs index 33f1f797a2f2..5f8559d4d581 100644 --- a/src/generator.cs +++ b/src/generator.cs @@ -5558,11 +5558,10 @@ public void PrintBindAsAttribute (ICustomAttributeProvider mi, StringBuilder sb var originalType = method?.ReturnType ?? property?.PropertyType; originalType = originalType ?? param?.ParameterType; - var type = TypeManager.GetUnderlyingNullableType (p.Type) ?? p.Type; + var declaringType = (mi as MemberInfo)?.DeclaringType ?? param.Member.DeclaringType; var pReturn = method != null ? "return: " : string.Empty; - var pBoolean = p.IsNullable ? "?" : string.Empty; - var attribstr = $"[{pReturn}BindAs (typeof ({type.FullName}{pBoolean}), OriginalType = typeof ({originalType.FullName}))]"; + var attribstr = $"[{pReturn}BindAs (typeof ({FormatType (declaringType, p.Type)}), OriginalType = typeof ({FormatType (declaringType, originalType)}))]"; if (sb != null) sb.Append ($"{attribstr} "); From 8b8ff9a33940e42848271d53dee8c437e3399299 Mon Sep 17 00:00:00 2001 From: Rolf Bjarne Kvinge Date: Wed, 5 Jul 2017 10:42:28 +0200 Subject: [PATCH 21/46] [generator] Don't generate a 'GetConstant (T?)' and 'GetNullableValue' for smart enums. Instead inline the corresponding code in the generator, so that we don't add a lot of metadata for little benefit. Generator diff (compared with the commit before starting to generate GetConstant/GetNullableValue (32fb0b65ddb52c2bbc6962bdf40dab6ef41bc5c2)): https://gist.github.com/rolfbjarne/440f3a78089098881145dfb231d86d66 (empty). --- src/generator-enums.cs | 24 ------------------- src/generator.cs | 52 ++++++++++++++++++++++++++++++------------ 2 files changed, 37 insertions(+), 39 deletions(-) diff --git a/src/generator-enums.cs b/src/generator-enums.cs index 22cadb74b38b..3a98c6929310 100644 --- a/src/generator-enums.cs +++ b/src/generator-enums.cs @@ -164,18 +164,6 @@ void GenerateEnum (Type type) print ("}"); print (""); } - - print ("public static NSString GetConstant (this {0}? self)", type.Name); - print ("{"); - indent++; - print ("if (self == null)"); - indent++; - print ("return null;"); - indent--; - print ("return GetConstant (self.Value);"); - indent--; - print ("}"); - print (""); print ("public static NSString GetConstant (this {0} self)", type.Name); print ("{"); @@ -200,18 +188,6 @@ void GenerateEnum (Type type) print ("}"); print (""); - - print ("public static {0}? GetNullableValue (NSString constant)", type.Name); - print ("{"); - indent++; - print ("if (constant == null)"); - indent++; - print ("return null;"); - indent--; - print ("return GetValue (constant);"); - indent--; - print ("}"); - print (""); print ("public static {0} GetValue (NSString constant)", type.Name); print ("{"); diff --git a/src/generator.cs b/src/generator.cs index 5f8559d4d581..c2c78c7dc8ae 100644 --- a/src/generator.cs +++ b/src/generator.cs @@ -1284,15 +1284,17 @@ string GetToBindAsWrapper (MemberInformation minfo = null, ParameterInfo pi = nu } temp = string.Format ("{3}NSValue.From{0} ({2}{1});", typeStr, denullify, parameterName, nullCheck); } else if (originalType == TypeManager.NSString && IsSmartEnum (retType)) { - temp = $"{FormatType (retType.DeclaringType, retType)}Extensions.GetConstant ({parameterName});"; + temp = isNullable ? $"{parameterName} == null ? null : " : string.Empty; + temp += $"{FormatType (retType.DeclaringType, retType)}Extensions.GetConstant ({parameterName}{denullify});"; } else if (originalType.IsArray) { var arrType = originalType.GetElementType (); var arrRetType = TypeManager.GetUnderlyingNullableType (retType.GetElementType ()) ?? retType.GetElementType (); var valueConverter = string.Empty; - if (arrType == TypeManager.NSString) - valueConverter = $"{FormatType (retType.DeclaringType, arrRetType)}Extensions.GetConstant (o), {parameterName});"; - else if (arrType == TypeManager.NSNumber) { + if (arrType == TypeManager.NSString) { + valueConverter = isNullable ? "o == null ? null : " : string.Empty; + valueConverter += $"{FormatType (retType.DeclaringType, arrRetType)}Extensions.GetConstant (o), {parameterName});"; + } else if (arrType == TypeManager.NSNumber) { var cast = arrRetType.IsEnum ? "(int)" : string.Empty; valueConverter = $"new NSNumber ({cast}o{denullify}), {parameterName});"; } else if (arrType == TypeManager.NSValue) { @@ -1382,12 +1384,14 @@ static Dictionary NSValueReturnMap { } } - string GetFromBindAsWrapper (MemberInformation minfo) + string GetFromBindAsWrapper (MemberInformation minfo, out string suffix) { var declaringType = minfo.mi.DeclaringType; if (IsMemberInsideProtocol (declaringType)) throw new BindingException (1050, true, "[BindAs] cannot be used inside Protocol or Model types. Type: {0}", declaringType.Name); + suffix = string.Empty; + var attrib = GetBindAsAttribute (minfo.mi); var nullableRetType = TypeManager.GetUnderlyingNullableType (attrib.Type); var isNullable = nullableRetType != null; @@ -1421,7 +1425,8 @@ string GetFromBindAsWrapper (MemberInformation minfo) if (isNullable) append = $"?{append}"; } else if (originalReturnType == TypeManager.NSString && IsSmartEnum (retType)) { - append = $"{FormatType (retType.DeclaringType, retType)}Extensions.{(isNullable ? "GetNullableValue" : "GetValue")} ("; + append = $"{FormatType (retType.DeclaringType, retType)}Extensions.GetValue ("; + suffix = ")"; } else if (originalReturnType.IsArray) { var arrType = originalReturnType.GetElementType (); var nullableElementType = TypeManager.GetUnderlyingNullableType (retType.GetElementType ()); @@ -3467,15 +3472,28 @@ void GetReturnsWrappers (MethodInfo mi, MemberInformation minfo, Type declaringT cast_a = " Runtime.GetINativeObject<" + FormatType (declaringType, GetCorrectGenericType (mi.ReturnType)) + "> ("; cast_b = $", {minfo.is_forced_owns})"; } else if (minfo != null && minfo.is_bindAs) { + var bindAs = GetBindAsAttribute (minfo.mi); + var nullableBindAsType = TypeManager.GetUnderlyingNullableType (bindAs.Type); + var isNullable = nullableBindAsType != null; + var bindAsType = TypeManager.GetUnderlyingNullableType (bindAs.Type) ?? bindAs.Type; + var formattedBindAsType = FormatType (declaringType, GetCorrectGenericType (bindAs.Type)); + string suffix; + var wrapper = GetFromBindAsWrapper (minfo, out suffix); + var formattedReturnType = FormatType (declaringType, GetCorrectGenericType (mi.ReturnType)); if (mi.ReturnType == TypeManager.NSString) { - cast_a = $" {GetFromBindAsWrapper (minfo)}Runtime.GetNSObject<{FormatType (declaringType, GetCorrectGenericType (mi.ReturnType))}> ("; - cast_b = "))"; + if (isNullable) { + print ("IntPtr retvaltmp;"); + cast_a = "((retvaltmp = "; + cast_b = $") == IntPtr.Zero ? default ({formattedBindAsType}) : ({wrapper}Runtime.GetNSObject<{formattedReturnType}> (retvaltmp)){suffix})"; + } else { + cast_a = $"{wrapper}Runtime.GetNSObject<{formattedReturnType}> ("; + cast_b = $"){suffix}"; + } } else { - var bindAs = GetBindAsAttribute (minfo.mi); - var bindAsType = TypeManager.GetUnderlyingNullableType (bindAs.Type) ?? bindAs.Type; - var enumCast = (bindAsType.IsEnum && !minfo.type.IsArray) ? $"({FormatType (bindAsType.DeclaringType, GetCorrectGenericType (bindAsType))})" : string.Empty; - cast_a = $" {enumCast}Runtime.GetNSObject<{FormatType (declaringType, GetCorrectGenericType (mi.ReturnType))}> ("; - cast_b = ")" + GetFromBindAsWrapper (minfo); + var enumCast = (bindAsType.IsEnum && !minfo.type.IsArray) ? $"({formattedBindAsType}) " : string.Empty; + print ("IntPtr retvaltmp;"); + cast_a = "((retvaltmp = "; + cast_b = $") == IntPtr.Zero ? default ({formattedBindAsType}) : ({enumCast}Runtime.GetNSObject<{formattedReturnType}> (retvaltmp){wrapper})){suffix}"; } } else { cast_a = " Runtime.GetNSObject<" + FormatType (declaringType, GetCorrectGenericType (mi.ReturnType)) + "> ("; @@ -3494,8 +3512,12 @@ void GetReturnsWrappers (MethodInfo mi, MemberInformation minfo, Type declaringT Type etype = mai.Type.GetElementType (); if (minfo != null && minfo.is_bindAs) { var bindAsT = GetBindAsAttribute (minfo.mi).Type.GetElementType (); - cast_a = $"NSArray.ArrayFromHandleFunc <{FormatType (bindAsT.DeclaringType, bindAsT)}> ("; - cast_b = $", {GetFromBindAsWrapper (minfo)})"; + var suffix = string.Empty; + print ("IntPtr retvalarrtmp;"); + cast_a = "((retvalarrtmp = "; + cast_b = ") == IntPtr.Zero ? null : ("; + cast_b += $"NSArray.ArrayFromHandleFunc <{FormatType (bindAsT.DeclaringType, bindAsT)}> (retvalarrtmp, {GetFromBindAsWrapper (minfo, out suffix)})" + suffix; + cast_b += "))"; } else if (etype == TypeManager.System_String) { cast_a = "NSArray.StringArrayFromHandle ("; cast_b = ")"; From ef682e44630472255e917fa6fac78e18e3cc26e7 Mon Sep 17 00:00:00 2001 From: Rolf Bjarne Kvinge Date: Wed, 5 Jul 2017 10:54:45 +0200 Subject: [PATCH 22/46] [runtime] Use the native equivalent of Type.GetTypeCode to do fewer string comparisons. --- runtime/exports.t4 | 4 +++ runtime/mono-runtime.h.t4 | 46 +++++++++++++++++++++++++++++++ runtime/trampolines.m | 57 +++++++++++++++++++++++++-------------- 3 files changed, 87 insertions(+), 20 deletions(-) diff --git a/runtime/exports.t4 b/runtime/exports.t4 index 7d29a3f1d658..005a030fd7fb 100644 --- a/runtime/exports.t4 +++ b/runtime/exports.t4 @@ -383,6 +383,10 @@ "MonoMethodSignature *", "sig" ), + new Export ("int", "mono_type_get_type", + "MonoType *", "type" + ), + #endregion #region metadata/mono-debug.h diff --git a/runtime/mono-runtime.h.t4 b/runtime/mono-runtime.h.t4 index a5921a7c057e..ff3e9d8309f2 100644 --- a/runtime/mono-runtime.h.t4 +++ b/runtime/mono-runtime.h.t4 @@ -170,6 +170,52 @@ typedef void (*MonoUnhandledExceptionFunc) (MonoObject *exc, gpointer u typedef unsigned char* (*MonoLoadAotDataFunc) (MonoAssembly *assembly, int size, gpointer user_data, void **out_handle); typedef void (*MonoFreeAotDataFunc) (MonoAssembly *assembly, int size, gpointer user_data, void *handle); +/* metadata/blob.h */ + +/* + * Encoding for type signatures used in the Metadata + */ +typedef enum { + MONO_TYPE_END = 0x00, /* End of List */ + MONO_TYPE_VOID = 0x01, + MONO_TYPE_BOOLEAN = 0x02, + MONO_TYPE_CHAR = 0x03, + MONO_TYPE_I1 = 0x04, + MONO_TYPE_U1 = 0x05, + MONO_TYPE_I2 = 0x06, + MONO_TYPE_U2 = 0x07, + MONO_TYPE_I4 = 0x08, + MONO_TYPE_U4 = 0x09, + MONO_TYPE_I8 = 0x0a, + MONO_TYPE_U8 = 0x0b, + MONO_TYPE_R4 = 0x0c, + MONO_TYPE_R8 = 0x0d, + MONO_TYPE_STRING = 0x0e, + MONO_TYPE_PTR = 0x0f, /* arg: token */ + MONO_TYPE_BYREF = 0x10, /* arg: token */ + MONO_TYPE_VALUETYPE = 0x11, /* arg: token */ + MONO_TYPE_CLASS = 0x12, /* arg: token */ + MONO_TYPE_VAR = 0x13, /* number */ + MONO_TYPE_ARRAY = 0x14, /* type, rank, boundsCount, bound1, loCount, lo1 */ + MONO_TYPE_GENERICINST= 0x15, /* \x{2026} */ + MONO_TYPE_TYPEDBYREF = 0x16, + MONO_TYPE_I = 0x18, + MONO_TYPE_U = 0x19, + MONO_TYPE_FNPTR = 0x1b, /* arg: full method signature */ + MONO_TYPE_OBJECT = 0x1c, + MONO_TYPE_SZARRAY = 0x1d, /* 0-based one-dim-array */ + MONO_TYPE_MVAR = 0x1e, /* number */ + MONO_TYPE_CMOD_REQD = 0x1f, /* arg: typedef or typeref token */ + MONO_TYPE_CMOD_OPT = 0x20, /* optional arg: typedef or typref token */ + MONO_TYPE_INTERNAL = 0x21, /* CLR internal type */ + + MONO_TYPE_MODIFIER = 0x40, /* Or with the following types */ + MONO_TYPE_SENTINEL = 0x41, /* Sentinel for varargs method signature */ + MONO_TYPE_PINNED = 0x45, /* Local var that points to pinned object */ + + MONO_TYPE_ENUM = 0x55 /* an enumeration */ +} MonoTypeEnum; + /* * From internal headers */ diff --git a/runtime/trampolines.m b/runtime/trampolines.m index e5111923c1ed..52dd448da393 100644 --- a/runtime/trampolines.m +++ b/runtime/trampolines.m @@ -908,42 +908,59 @@ static void * xamarin_get_nsnumber_converter (MonoClass *managedType, MonoMethod *method, bool to_managed, guint32 *exception_gchandle) { + int type; void * func = NULL; char *fullname = xamarin_class_get_full_name (managedType, exception_gchandle); if (*exception_gchandle != 0) goto exception_handling; - if (!strcmp (fullname, "System.SByte")) { + type = mono_type_get_type (mono_class_get_type (managedType)); + + switch (type) { + case MONO_TYPE_I1: func = to_managed ? (void *) xamarin_nsnumber_to_sbyte : (void *) xamarin_sbyte_to_nsnumber; - } else if (!strcmp (fullname, "System.Byte")) { + break; + case MONO_TYPE_U1: func = to_managed ? (void *) xamarin_nsnumber_to_byte : (void *) xamarin_byte_to_nsnumber; - } else if (!strcmp (fullname, "System.Int16")) { + break; + case MONO_TYPE_I2: func = to_managed ? (void *) xamarin_nsnumber_to_short : (void *) xamarin_short_to_nsnumber; - } else if (!strcmp (fullname, "System.UInt16")) { + break; + case MONO_TYPE_U2: func = to_managed ? (void *) xamarin_nsnumber_to_ushort : (void *) xamarin_ushort_to_nsnumber; - } else if (!strcmp (fullname, "System.Int32")) { + break; + case MONO_TYPE_I4: func = to_managed ? (void *) xamarin_nsnumber_to_int : (void *) xamarin_int_to_nsnumber; - } else if (!strcmp (fullname, "System.UInt32")) { + break; + case MONO_TYPE_U4: func = to_managed ? (void *) xamarin_nsnumber_to_uint : (void *) xamarin_uint_to_nsnumber; - } else if (!strcmp (fullname, "System.Int64")) { + break; + case MONO_TYPE_I8: func = to_managed ? (void *) xamarin_nsnumber_to_long : (void *) xamarin_long_to_nsnumber; - } else if (!strcmp (fullname, "System.UInt64")) { + break; + case MONO_TYPE_U8: func = to_managed ? (void *) xamarin_nsnumber_to_ulong : (void *) xamarin_ulong_to_nsnumber; - } else if (!strcmp (fullname, "System.nint")) { - func = to_managed ? (void *) xamarin_nsnumber_to_nint : (void *) xamarin_nint_to_nsnumber; - } else if (!strcmp (fullname, "System.nuint")) { - func = to_managed ? (void *) xamarin_nsnumber_to_nuint : (void *) xamarin_nuint_to_nsnumber; - } else if (!strcmp (fullname, "System.Single")) { + break; + case MONO_TYPE_R4: func = to_managed ? (void *) xamarin_nsnumber_to_float : (void *) xamarin_float_to_nsnumber; - } else if (!strcmp (fullname, "System.Double")) { + break; + case MONO_TYPE_R8: func = to_managed ? (void *) xamarin_nsnumber_to_double : (void *) xamarin_double_to_nsnumber; - } else if (!strcmp (fullname, "System.nfloat")) { - func = to_managed ? (void *) xamarin_nsnumber_to_nfloat : (void *) xamarin_nfloat_to_nsnumber; - } else if (!strcmp (fullname, "System.Boolean")) { + break; + case MONO_TYPE_BOOLEAN: func = to_managed ? (void *) xamarin_nsnumber_to_bool : (void *) xamarin_bool_to_nsnumber; - } else { - *exception_gchandle = xamarin_create_bindas_exception (mono_class_get_type (managedType), mono_class_get_type (xamarin_get_nsnumber_class ()), method); - goto exception_handling; + break; + default: + if (!strcmp (fullname, "System.nint")) { + func = to_managed ? (void *) xamarin_nsnumber_to_nint : (void *) xamarin_nint_to_nsnumber; + } else if (!strcmp (fullname, "System.nuint")) { + func = to_managed ? (void *) xamarin_nsnumber_to_nuint : (void *) xamarin_nuint_to_nsnumber; + } else if (!strcmp (fullname, "System.nfloat")) { + func = to_managed ? (void *) xamarin_nsnumber_to_nfloat : (void *) xamarin_nfloat_to_nsnumber; + } else { + *exception_gchandle = xamarin_create_bindas_exception (mono_class_get_type (managedType), mono_class_get_type (xamarin_get_nsnumber_class ()), method); + goto exception_handling; + } } exception_handling: From 90415031497143e6be128be9f587bf8fdf0045f1 Mon Sep 17 00:00:00 2001 From: Rolf Bjarne Kvinge Date: Thu, 6 Jul 2017 10:40:54 +0200 Subject: [PATCH 23/46] [runtime] Don't freak out at startup if we can't find the NSValue or NSNumber classes. They might have been linked away. --- runtime/runtime.m | 23 ++++++++++++++++++----- 1 file changed, 18 insertions(+), 5 deletions(-) diff --git a/runtime/runtime.m b/runtime/runtime.m index cb39d62e54c5..6bf1c9cd249e 100644 --- a/runtime/runtime.m +++ b/runtime/runtime.m @@ -357,12 +357,16 @@ void xamarin_framework_peer_unlock () MonoClass * xamarin_get_nsvalue_class () { + if (nsvalue_class == NULL) + xamarin_assertion_message ("Internal consistency error, please file a bug (https://bugzilla.xamarin.com). Additional data: can't get the NSValue class because it's been linked away.\n"); return nsvalue_class; } MonoClass * xamarin_get_nsnumber_class () { + if (nsnumber_class == NULL) + xamarin_assertion_message ("Internal consistency error, please file a bug (https://bugzilla.xamarin.com). Additional data: can't get the NSNumber class because it's been linked away.\n"); return nsnumber_class; } @@ -399,6 +403,9 @@ void xamarin_framework_peer_unlock () // COOP: Reading managed data, must be in UNSAFE mode MONO_ASSERT_GC_UNSAFE; + if (nsnumber_class == NULL) + return false; + return mono_class_is_subclass_of (cls, nsnumber_class, false); } @@ -408,6 +415,9 @@ void xamarin_framework_peer_unlock () // COOP: Reading managed data, must be in UNSAFE mode MONO_ASSERT_GC_UNSAFE; + if (nsvalue_class == NULL) + return false; + return mono_class_is_subclass_of (cls, nsvalue_class, false); } @@ -417,6 +427,9 @@ void xamarin_framework_peer_unlock () // COOP: Reading managed data, must be in UNSAFE mode MONO_ASSERT_GC_UNSAFE; + if (nsstring_class == NULL) + return false; + return mono_class_is_subclass_of (cls, nsstring_class, false); } @@ -874,11 +887,11 @@ -(void) xamarinSetGCHandle: (int) gc_handle; } static MonoClass * -get_class_from_name (MonoImage* image, const char *nmspace, const char *name) +get_class_from_name (MonoImage* image, const char *nmspace, const char *name, bool optional = false) { // COOP: this is a convenience function executed only at startup, I believe the mode here doesn't matter. MonoClass *rv = mono_class_from_name (image, nmspace, name); - if (!rv) + if (!rv && !optional) xamarin_assertion_message ("Fatal error: failed to load the class '%s.%s'\n.", nmspace, name); return rv; } @@ -1299,9 +1312,9 @@ -(void) xamarinSetGCHandle: (int) gc_handle; runtime_class = get_class_from_name (platform_image, objcruntime, "Runtime"); inativeobject_class = get_class_from_name (platform_image, objcruntime, "INativeObject"); nsobject_class = get_class_from_name (platform_image, foundation, "NSObject"); - nsnumber_class = get_class_from_name (platform_image, foundation, "NSNumber"); - nsvalue_class = get_class_from_name (platform_image, foundation, "NSValue"); - nsstring_class = get_class_from_name (platform_image, foundation, "NSString"); + nsnumber_class = get_class_from_name (platform_image, foundation, "NSNumber", true); + nsvalue_class = get_class_from_name (platform_image, foundation, "NSValue", true); + nsstring_class = get_class_from_name (platform_image, foundation, "NSString", true); mono_add_internal_call (xamarin_use_new_assemblies ? "Foundation.NSObject::xamarin_release_managed_ref" : PRODUCT_COMPAT_NAMESPACE ".Foundation.NSObject::xamarin_release_managed_ref", (const void *) xamarin_release_managed_ref); mono_add_internal_call (xamarin_use_new_assemblies ? "Foundation.NSObject::xamarin_create_managed_ref" : PRODUCT_COMPAT_NAMESPACE ".Foundation.NSObject::xamarin_create_managed_ref", (const void *) xamarin_create_managed_ref); From 34f31b5b34258aa3457aa4411aecb9d031ec37a0 Mon Sep 17 00:00:00 2001 From: Rolf Bjarne Kvinge Date: Thu, 6 Jul 2017 10:42:27 +0200 Subject: [PATCH 24/46] [linker] Preserve smart enum conversion methods when needed. --- docs/website/mtouch-errors.md | 4 + tests/linker-ios/link all/PreserveTest.cs | 55 +++++++ .../PreserveSmartEnumConversionsSubStep.cs | 146 ++++++++++++++++++ tools/mmp/Makefile | 1 + tools/mmp/mmp.csproj | 3 + tools/mtouch/Makefile | 1 + tools/mtouch/Tuning.cs | 1 + tools/mtouch/mtouch.csproj | 3 + 8 files changed, 214 insertions(+) create mode 100644 tools/linker/MonoTouch.Tuner/PreserveSmartEnumConversionsSubStep.cs diff --git a/docs/website/mtouch-errors.md b/docs/website/mtouch-errors.md index dcd98fe18320..a95f0b595433 100644 --- a/docs/website/mtouch-errors.md +++ b/docs/website/mtouch-errors.md @@ -1201,6 +1201,10 @@ Something unexpected occured when trying to mark `NSObject` subclasses from the Something unexpected occured when trying to inline code from the application. The assembly causing the issue is named in the error message. In order to fix this issue the assembly will need to be provided in a [bug report](https://bugzilla.xamarin.com) along with a complete build log with verbosity enabled (i.e. `-v -v -v -v` in the **Additional mtouch arguments**). +### MT2100: Smart Enum Conversion Preserver failed processing `...`. + +Something unexpected occured when trying to mark the conversion methods for smart enums from the application. The assembly causing the issue is named in the error message. In order to fix this issue the assembly will need to be provided in a [bug report](https://bugzilla.xamarin.com) along with a complete build log with verbosity enabled (i.e. `-v -v -v -v` in the **Additional mtouch arguments**). + diff --git a/tests/linker-ios/link all/PreserveTest.cs b/tests/linker-ios/link all/PreserveTest.cs index f83addf0961f..2eac93a15ad2 100644 --- a/tests/linker-ios/link all/PreserveTest.cs +++ b/tests/linker-ios/link all/PreserveTest.cs @@ -164,5 +164,60 @@ public void Selector_Unconditional () var method = klass.GetMethod ("GetHandle", BindingFlags.Public | BindingFlags.Static); Assert.NotNull (method, "GetHandle"); } + + [Test] + public void SmartEnumTest () + { + var consumer = GetType ().Assembly.GetType ("LinkAll.Attributes.SmartConsumer"); + Assert.NotNull (consumer, "SmartConsumer"); + Assert.NotNull (consumer.GetMethod ("GetSmartEnumValue"), "GetSmartEnumValue"); + Assert.NotNull (consumer.GetMethod ("SetSmartEnumValue"), "SetSmartEnumValue"); + var smartEnum = GetType ().Assembly.GetType ("LinkAll.Attributes.SmartEnum"); + Assert.NotNull (smartEnum, "SmartEnum"); + var smartExtensions = GetType ().Assembly.GetType ("LinkAll.Attributes.SmartEnumExtensions"); + Assert.NotNull (smartExtensions, "SmartEnumExtensions"); + Assert.NotNull (smartExtensions.GetMethod ("GetConstant"), "GetConstant"); + Assert.NotNull (smartExtensions.GetMethod ("GetValue"), "GetValue"); + + // Unused smart enums and their extensions should be linked away + Assert.IsNull (typeof (NSObject).Assembly.GetType ("AVFoundation.AVMediaTypes"), "AVMediaTypes"); + Assert.IsNull (typeof (NSObject).Assembly.GetType ("AVFoundation.AVMediaTypesExtensions"), "AVMediaTypesExtensions"); + } + } + + [Preserve (AllMembers = true)] + class SmartConsumer : NSObject + { + // The Smart Get/Set methods should not be linked away, and neither should the Smart enums + extensions + [Export ("getSmartEnumValue")] + [return: BindAs (typeof (SmartEnum), OriginalType = typeof (NSString))] + public SmartEnum GetSmartEnumValue () + { + return SmartEnum.Smart; + } + + [Export ("setSmartEnumValue:")] + public void SetSmartEnumValue ([BindAs (typeof (SmartEnum), OriginalType = typeof (NSString))] SmartEnum value) + { + } + } + + public enum SmartEnum : int + { + Smart = 0, } + + public static class SmartEnumExtensions + { + public static NSString GetConstant (this SmartEnum self) + { + return (NSString) "Smart"; + } + + public static SmartEnum GetValue (NSString constant) + { + return SmartEnum.Smart; + } + } + } \ No newline at end of file diff --git a/tools/linker/MonoTouch.Tuner/PreserveSmartEnumConversionsSubStep.cs b/tools/linker/MonoTouch.Tuner/PreserveSmartEnumConversionsSubStep.cs new file mode 100644 index 000000000000..aef98062bf56 --- /dev/null +++ b/tools/linker/MonoTouch.Tuner/PreserveSmartEnumConversionsSubStep.cs @@ -0,0 +1,146 @@ +// Copyright 2017 Xamarin Inc. + +using System; +using System.Collections.Generic; + +using Mono.Cecil; +using Mono.Cecil.Cil; +using Mono.Linker; +using Mono.Tuner; +using MonoTouch.Tuner; + +namespace Xamarin.Linker.Steps +{ + public class PreserveSmartEnumConversionsSubStep : ExceptionalSubStep + { + Dictionary> cache; + protected override string Name { get; } = "Smart Enum Conversion Preserver"; + protected override int ErrorCode { get; } = 2100; + + public override SubStepTargets Targets { + get { + return SubStepTargets.Method | SubStepTargets.Property; + } + } + + public override bool IsActiveFor (AssemblyDefinition assembly) + { + // we need to process all assemblies, because the functions we want to + // preserve are not necessarily in the assembly we're processing. + return true; + } + + void Preserve (Tuple pair, MethodDefinition conditionA, MethodDefinition conditionB = null) + { + if (conditionA != null) { + context.Annotations.AddPreservedMethod (conditionA, pair.Item1); + context.Annotations.AddPreservedMethod (conditionA, pair.Item2); + } + if (conditionB != null) { + context.Annotations.AddPreservedMethod (conditionB, pair.Item1); + context.Annotations.AddPreservedMethod (conditionB, pair.Item2); + } + } + + void ProcessAttributeProvider (ICustomAttributeProvider provider, MethodDefinition conditionA, MethodDefinition conditionB = null) + { + if (provider?.HasCustomAttributes != true) + return; + + foreach (var ca in provider.CustomAttributes) { + var tr = ca.Constructor.DeclaringType; + + if (!tr.IsPlatformType ("ObjCRuntime", "BindAsAttribute")) + continue; + + if (ca.ConstructorArguments.Count != 1) { + Console.WriteLine ("WARNING"); + continue; + } + + var managedType = ca.ConstructorArguments [0].Value as TypeReference; + var managedEnumType = managedType?.GetElementType ().Resolve (); + if (managedEnumType == null) { + Console.WriteLine ("WARNING"); + continue; + } + + Tuple pair; + if (cache != null && cache.TryGetValue (managedEnumType, out pair)) { + Preserve (pair, conditionA, conditionB); + continue; + } + + Console.WriteLine (managedEnumType); + // Find the Extension type + TypeDefinition extensionType = null; + var extensionName = managedEnumType.Name + "Extensions"; + foreach (var type in managedEnumType.Module.Types) { + if (type.Namespace != managedEnumType.Namespace) + continue; + if (type.Name != extensionName) + continue; + extensionType = type; + break; + } + if (extensionType == null) { + Console.WriteLine ("WARNING"); + continue; + } + + // Find the GetConstant/GetValue methods + MethodDefinition getConstant = null; + MethodDefinition getValue = null; + + foreach (var method in extensionType.Methods) { + if (!method.IsStatic) + continue; + if (!method.HasParameters || method.Parameters.Count != 1) + continue; + if (method.Name == "GetConstant") { + if (!method.ReturnType.IsPlatformType ("Foundation", "NSString")) + continue; + if (method.Parameters [0].ParameterType != managedEnumType) + continue; + getConstant = method; + } else if (method.Name == "GetValue") { + if (!method.Parameters [0].ParameterType.IsPlatformType ("Foundation", "NSString")) + continue; + if (method.ReturnType != managedEnumType) + continue; + getValue = method; + } + } + if (getConstant == null || getValue == null) { + Console.WriteLine ("WARNING"); + continue; + } + + pair = new Tuple (getConstant, getValue); + if (cache == null) + cache = new Dictionary> (); + cache.Add (managedEnumType, pair); + Preserve (pair, conditionA, conditionB); + } + } + + protected override void Process (MethodDefinition method) + { + ProcessAttributeProvider (method, method); + ProcessAttributeProvider (method.MethodReturnType, method); + if (method.HasParameters) { + foreach (var p in method.Parameters) + ProcessAttributeProvider (p, method); + } + } + + protected override void Process (PropertyDefinition property) + { + ProcessAttributeProvider (property, property.GetMethod, property.SetMethod); + if (property.GetMethod != null) + Process (property.GetMethod); + if (property.SetMethod != null) + Process (property.SetMethod); + } + } +} diff --git a/tools/mmp/Makefile b/tools/mmp/Makefile index ffe6ac80935d..594f1f970924 100644 --- a/tools/mmp/Makefile +++ b/tools/mmp/Makefile @@ -94,6 +94,7 @@ tuner_sources = \ ../linker/MonoTouch.Tuner/Extensions.cs \ ../linker/MonoTouch.Tuner/ListExportedSymbols.cs \ ../linker/MonoTouch.Tuner/ProcessExportedFields.cs \ + ../linker/MonoTouch.Tuner/PreserveSmartEnumConversionsSubStep.cs \ ../../src/build/mac/Constants.cs linker_resources = \ diff --git a/tools/mmp/mmp.csproj b/tools/mmp/mmp.csproj index 5638b23f9c0a..e8e0239ca928 100644 --- a/tools/mmp/mmp.csproj +++ b/tools/mmp/mmp.csproj @@ -265,6 +265,9 @@ MonoTouch.Tuner\ProcessExportedFields.cs + + MonoTouch.Tuner\PreserveSmartEnumConversionsSubStep.cs + external\Constants.cs diff --git a/tools/mtouch/Makefile b/tools/mtouch/Makefile index 5f7565d54b2b..86472ff2c330 100644 --- a/tools/mtouch/Makefile +++ b/tools/mtouch/Makefile @@ -71,6 +71,7 @@ LINKER_SOURCES = \ $(LINKER_DIR)/MonoTouch.Tuner/RemoveAttributes.cs \ $(LINKER_DIR)/MonoTouch.Tuner/RemoveCode.cs \ $(LINKER_DIR)/MonoTouch.Tuner/SealerSubStep.cs \ + $(LINKER_DIR)/MonoTouch.Tuner/PreserveSmartEnumConversionsSubStep.cs \ $(TOP)/tools/linker/ApplyPreserveAttribute.cs \ $(TOP)/tools/linker/BaseProfile.cs \ $(TOP)/tools/linker/CoreHttpMessageHandler.cs \ diff --git a/tools/mtouch/Tuning.cs b/tools/mtouch/Tuning.cs index 0686fd1c6793..72b78729d9f1 100644 --- a/tools/mtouch/Tuning.cs +++ b/tools/mtouch/Tuning.cs @@ -157,6 +157,7 @@ static SubStepDispatcher GetSubSteps (LinkerOptions options) sub.Add (new PreserveSoapHttpClients ()); sub.Add (new CoreHttpMessageHandler (options)); sub.Add (new InlinerSubStep ()); + sub.Add (new PreserveSmartEnumConversionsSubStep ()); return sub; } diff --git a/tools/mtouch/mtouch.csproj b/tools/mtouch/mtouch.csproj index 62d0b69f75f8..312f775fecea 100644 --- a/tools/mtouch/mtouch.csproj +++ b/tools/mtouch/mtouch.csproj @@ -335,6 +335,9 @@ MonoTouch.Tuner\InlinerSubStep.cs + + MonoTouch.Tuner\PreserveSmartEnumConversionsSubStep.cs + common\BuildTasks.cs From fcd760c73fa828784fe322c1fe9c543c969e9532 Mon Sep 17 00:00:00 2001 From: Rolf Bjarne Kvinge Date: Thu, 6 Jul 2017 12:56:32 +0200 Subject: [PATCH 25/46] [registrar] Make sure to include the required headers for the generated BindAs glue code. Since the native signature doesn't match the managed signature, we need to manually check the type of each conversion and make sure the required headers are included. --- tools/common/StaticRegistrar.cs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/tools/common/StaticRegistrar.cs b/tools/common/StaticRegistrar.cs index e7415ab1acd3..c308dd0ceb7b 100644 --- a/tools/common/StaticRegistrar.cs +++ b/tools/common/StaticRegistrar.cs @@ -3872,6 +3872,9 @@ void GenerateConversionToManaged (TypeReference inputType, TypeReference outputT sb.AppendLine ($"{classVariableName} = {managedClassExpression};"); } + CheckNamespace (underlyingNativeType.Resolve (), exceptions); + CheckNamespace (underlyingManagedType.Resolve (), exceptions); + if (isManagedNullable || isManagedArray) sb.AppendLine ($"if ({inputName}) {{"); @@ -3945,6 +3948,9 @@ void GenerateConversionToNative (TypeReference inputType, TypeReference outputTy sb.AppendLine ($"{classVariableName} = {managedClassExpression};"); } + CheckNamespace (underlyingNativeType.Resolve (), exceptions); + CheckNamespace (underlyingManagedType.Resolve (), exceptions); + if (isManagedNullable || isManagedArray) sb.AppendLine ($"if ({inputName}) {{"); From 2d12c1864531631a8c3b54600b4b3b870897bed0 Mon Sep 17 00:00:00 2001 From: Rolf Bjarne Kvinge Date: Thu, 6 Jul 2017 13:51:21 +0200 Subject: [PATCH 26/46] [tests] Restrict some tests to the required Xcode version. --- tests/test-libraries/testgenerator.cs | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/tests/test-libraries/testgenerator.cs b/tests/test-libraries/testgenerator.cs index 0b61979f587f..c6a2e4c3b78c 100644 --- a/tests/test-libraries/testgenerator.cs +++ b/tests/test-libraries/testgenerator.cs @@ -57,6 +57,7 @@ class BindAsData public string ManagedNewExpression; public string Map; public string MapFrom; + public Version MinXcodeVersion; } static BindAsData [] bindas_nsnumber = new [] { @@ -96,7 +97,7 @@ class BindAsData }; static BindAsData [] bindas_nsstring = new [] { - new BindAsData { Managed = "SecKeyAlgorithm", ManagedNewExpression = "SecKeyAlgorithm.RsaSignatureRaw" }, + new BindAsData { Managed = "SecKeyAlgorithm", ManagedNewExpression = "SecKeyAlgorithm.RsaSignatureRaw", MinXcodeVersion = new Version (8, 0) }, }; static string GetNativeName (char t) @@ -1238,6 +1239,10 @@ public class RegistrarTestGenerated {"); w.AppendLine ("\t\t[Test]"); w.AppendLine ($"\t\tpublic void NSStringBindAs_{v.Managed}_Bindings ()"); w.AppendLine ("\t\t{"); + if (v.MinXcodeVersion != null) { + w.AppendLine ($"\t\t\tTestRuntime.AssertXcodeVersion ({v.MinXcodeVersion.Major}, {v.MinXcodeVersion.Minor});"); + w.AppendLine (); + } w.AppendLine ($"\t\t\tusing (var obj = new ObjCRegistrarTest ()) {{"); w.AppendLine ($"\t\t\t\tAssert.Throws (() => {{ Console.WriteLine (obj.PSmart{v.Managed}Property); }}, \"initial zero property\");"); w.AppendLine ($"\t\t\t\tAssert.Throws (() => {{ Console.WriteLine (obj.GetSmart{v.Managed}Value ()); }}, \"initial zero method\");"); @@ -1271,6 +1276,10 @@ public class RegistrarTestGenerated {"); w.AppendLine ("\t\t[Test]"); w.AppendLine ($"\t\tpublic void NSStringBindAs_{v.Managed}_Overrides ()"); w.AppendLine ("\t\t{"); + if (v.MinXcodeVersion != null) { + w.AppendLine ($"\t\t\tTestRuntime.AssertXcodeVersion ({v.MinXcodeVersion.Major}, {v.MinXcodeVersion.Minor});"); + w.AppendLine (); + } w.AppendLine ($"\t\t\tusing (var obj = new BindAsTestClassGenerated ()) {{"); w.AppendLine ($"\t\t\t\tAssert.Throws (() => {{ Console.WriteLine (obj.PSmart{v.Managed}Property); }}, \"initial null\");"); w.AppendLine ($"\t\t\t\tMessaging.void_objc_msgSend_IntPtr (obj.Handle, Selector.GetHandle (\"setSmartNullable{v.Managed}Value:\"), IntPtr.Zero);"); @@ -1316,6 +1325,10 @@ public class RegistrarTestGenerated {"); w.AppendLine ("\t\t[Test]"); w.AppendLine ($"\t\tpublic void NSStringBindAs_{v.Managed}Array_Bindings ()"); w.AppendLine ("\t\t{"); + if (v.MinXcodeVersion != null) { + w.AppendLine ($"\t\t\tTestRuntime.AssertXcodeVersion ({v.MinXcodeVersion.Major}, {v.MinXcodeVersion.Minor});"); + w.AppendLine (); + } w.AppendLine ($"\t\t\tusing (var obj = new ObjCRegistrarTest ()) {{"); w.AppendLine ($"\t\t\t\tAssert.IsNull (obj.PSmart{v.Managed}Properties, \"initial null property\");"); w.AppendLine ($"\t\t\t\tAssert.IsNull (obj.GetSmart{v.Managed}Values (), \"initial null method\");"); @@ -1347,6 +1360,10 @@ public class RegistrarTestGenerated {"); w.AppendLine ("\t\t[Test]"); w.AppendLine ($"\t\tpublic void NSStringBindAs_{v.Managed}_Array_Overrides ()"); w.AppendLine ("\t\t{"); + if (v.MinXcodeVersion != null) { + w.AppendLine ($"\t\t\tTestRuntime.AssertXcodeVersion ({v.MinXcodeVersion.Major}, {v.MinXcodeVersion.Minor});"); + w.AppendLine (); + } w.AppendLine ($"\t\t\tusing (var obj = new BindAsTestClassGenerated ()) {{"); w.AppendLine ($"\t\t\t\tAssert.IsNull (obj.PSmart{v.Managed}Properties, \"initial null\");"); w.AppendLine ($"\t\t\t\tMessaging.void_objc_msgSend_IntPtr (obj.Handle, Selector.GetHandle (\"setSmart{v.Managed}Values:\"), IntPtr.Zero);"); From 7816a686e8efff891b1a9e7a09366a759d93761e Mon Sep 17 00:00:00 2001 From: Rolf Bjarne Kvinge Date: Thu, 6 Jul 2017 17:25:01 +0200 Subject: [PATCH 27/46] [registrar] Add support for BindAs conversion between NSNumber and enums. --- runtime/exports.t4 | 4 ++++ runtime/trampolines.m | 2 ++ src/ObjCRuntime/Registrar.cs | 2 +- tests/test-libraries/testgenerator.cs | 17 ++++++++++------- tools/common/StaticRegistrar.cs | 4 ++++ 5 files changed, 21 insertions(+), 8 deletions(-) diff --git a/runtime/exports.t4 b/runtime/exports.t4 index 005a030fd7fb..43d15a7d340e 100644 --- a/runtime/exports.t4 +++ b/runtime/exports.t4 @@ -68,6 +68,10 @@ "MonoClass *", "klass" ), + new Export ("MonoType *", "mono_class_enum_basetype", + "MonoClass *", "klass" + ), + new Export ("int32_t", "mono_class_value_size", "MonoClass *", "klass", "uint32_t *", "align" diff --git a/runtime/trampolines.m b/runtime/trampolines.m index 52dd448da393..917e85566f15 100644 --- a/runtime/trampolines.m +++ b/runtime/trampolines.m @@ -957,6 +957,8 @@ func = to_managed ? (void *) xamarin_nsnumber_to_nuint : (void *) xamarin_nuint_to_nsnumber; } else if (!strcmp (fullname, "System.nfloat")) { func = to_managed ? (void *) xamarin_nsnumber_to_nfloat : (void *) xamarin_nfloat_to_nsnumber; + } else if (mono_class_is_enum (managedType)) { + func = xamarin_get_nsnumber_converter (mono_class_from_mono_type (mono_class_enum_basetype (managedType)), method, to_managed, exception_gchandle); } else { *exception_gchandle = xamarin_create_bindas_exception (mono_class_get_type (managedType), mono_class_get_type (xamarin_get_nsnumber_class ()), method); goto exception_handling; diff --git a/src/ObjCRuntime/Registrar.cs b/src/ObjCRuntime/Registrar.cs index dcb107b5f6ad..eb58f614f9bb 100644 --- a/src/ObjCRuntime/Registrar.cs +++ b/src/ObjCRuntime/Registrar.cs @@ -657,7 +657,7 @@ bool IsValidToManagedTypeConversion (TType inputType, TType outputType) case "System.Boolean": return true; default: - return false; + return Registrar.IsEnum (underlyingOutputType); } } else if (Registrar.Is (underlyingInputType, Foundation, "NSValue")) { // Remove 'MonoMac.' namespace prefix to make switch smaller diff --git a/tests/test-libraries/testgenerator.cs b/tests/test-libraries/testgenerator.cs index c6a2e4c3b78c..47c6a2740261 100644 --- a/tests/test-libraries/testgenerator.cs +++ b/tests/test-libraries/testgenerator.cs @@ -58,6 +58,8 @@ class BindAsData public string Map; public string MapFrom; public Version MinXcodeVersion; + public string ToNSNumberCastExpression; + public string FromNSNumberCastExpression; } static BindAsData [] bindas_nsnumber = new [] { @@ -75,6 +77,7 @@ class BindAsData new BindAsData { Managed = "nuint", Native = "NSUInteger", ManagedNewExpression = "((nuint) 1)", Map = ".NUIntValue" }, new BindAsData { Managed = "nfloat", Native = "NSFloat", ManagedNewExpression = "((nfloat) 1)", Map = ".NFloatValue" }, new BindAsData { Managed = "Boolean", Native = "BOOL", ManagedNewExpression = "true", Map = ".BoolValue" }, + new BindAsData { Managed = "NSStreamStatus", Native = "NSStreamStatus", ManagedNewExpression = "NSStreamStatus.Closed", Map = ".UInt64Value", ToNSNumberCastExpression = "(ulong) ", FromNSNumberCastExpression = "(NSStreamStatus) " }, }; static BindAsData[] bindas_nsvalue = new [] { new BindAsData { Managed = "CGAffineTransform", Native = "CGAffineTransform", ManagedNewExpression = "new CGAffineTransform (1, 2, 3, 4, 5, 6)", Map = ".CGAffineTransformValue", MapFrom = "FromCGAffineTransform" }, @@ -956,13 +959,13 @@ public class RegistrarTestGenerated {"); w.AppendLine (); w.AppendLine ($"\t\t\t\tvar value = {v.ManagedNewExpression};"); - w.AppendLine ($"\t\t\t\tusing (var input = new NSNumber (value))"); + w.AppendLine ($"\t\t\t\tusing (var input = new NSNumber ({v.ToNSNumberCastExpression}value))"); w.AppendLine ($"\t\t\t\t\tMessaging.void_objc_msgSend_IntPtr (obj.Handle, Selector.GetHandle (\"set{v.Managed}NumberNullable:\"), input.Handle);"); w.AppendLine ($"\t\t\t\tAssert.AreEqual (value, obj.{v.Managed}Number, \"after setting A\");"); w.AppendLine (); w.AppendLine ($"\t\t\t\tobj.{v.Managed}Number = null;"); - w.AppendLine ($"\t\t\t\tusing (var input = new NSNumber (value))"); + w.AppendLine ($"\t\t\t\tusing (var input = new NSNumber ({v.ToNSNumberCastExpression}value))"); w.AppendLine ($"\t\t\t\t\tMessaging.void_objc_msgSend_IntPtr (obj.Handle, Selector.GetHandle (\"set{v.Managed}NumberNonNullable:\"), input.Handle);"); w.AppendLine ($"\t\t\t\tAssert.AreEqual (value, obj.{v.Managed}Number.Value, \"after setting B\");"); w.AppendLine (); @@ -975,13 +978,13 @@ public class RegistrarTestGenerated {"); w.AppendLine ($"\t\t\t\tvalue = {v.ManagedNewExpression};"); w.AppendLine ($"\t\t\t\tobj.{v.Managed}Number = value;"); w.AppendLine ($"\t\t\t\tnumber = Runtime.GetNSObject (Messaging.IntPtr_objc_msgSend (obj.Handle, Selector.GetHandle (\"get{v.Managed}NumberNullable\")));"); - w.AppendLine ($"\t\t\t\tAssert.AreEqual (value, number{v.Map}, \"getter B\");"); + w.AppendLine ($"\t\t\t\tAssert.AreEqual (value, {v.FromNSNumberCastExpression}number{v.Map}, \"getter B\");"); w.AppendLine (); w.AppendLine ($"\t\t\t\tvalue = {v.ManagedNewExpression};"); w.AppendLine ($"\t\t\t\tobj.{v.Managed}Number = value;"); w.AppendLine ($"\t\t\t\tnumber = Runtime.GetNSObject (Messaging.IntPtr_objc_msgSend (obj.Handle, Selector.GetHandle (\"get{v.Managed}NumberNonNullable\")));"); - w.AppendLine ($"\t\t\t\tAssert.AreEqual (value, number{v.Map}, \"getter C\");"); + w.AppendLine ($"\t\t\t\tAssert.AreEqual (value, {v.FromNSNumberCastExpression}number{v.Map}, \"getter C\");"); w.AppendLine ($"\t\t\t}}"); w.AppendLine ("\t\t}"); @@ -1032,14 +1035,14 @@ public class RegistrarTestGenerated {"); w.AppendLine (); w.AppendLine ($"\t\t\t\tvar value = new {v.Managed} [] {{ {v.ManagedNewExpression} }};"); - w.AppendLine ($"\t\t\t\tusing (var input = NSArray.FromNSObjects<{v.Managed}> ((v) => new NSNumber (v), value))"); + w.AppendLine ($"\t\t\t\tusing (var input = NSArray.FromNSObjects<{v.Managed}> ((v) => new NSNumber ({v.ToNSNumberCastExpression}v), value))"); w.AppendLine ($"\t\t\t\t\tMessaging.void_objc_msgSend_IntPtr (obj.Handle, Selector.GetHandle (\"set{v.Managed}Array:\"), input.Handle);"); w.AppendLine ($"\t\t\t\tAssert.AreEqual (value.Length, obj.{v.Managed}Array.Length, \"after setting A\");"); w.AppendLine ($"\t\t\t\tAssert.AreEqual (value [0], obj.{v.Managed}Array [0], \"after setting A element\");"); w.AppendLine (); w.AppendLine ($"\t\t\t\tobj.{v.Managed}Array = null;"); - w.AppendLine ($"\t\t\t\tusing (var input = NSArray.FromNSObjects<{v.Managed}> ((v) => new NSNumber (v), value))"); + w.AppendLine ($"\t\t\t\tusing (var input = NSArray.FromNSObjects<{v.Managed}> ((v) => new NSNumber ({v.ToNSNumberCastExpression}v), value))"); w.AppendLine ($"\t\t\t\t\tMessaging.void_objc_msgSend_IntPtr (obj.Handle, Selector.GetHandle (\"set{v.Managed}Array:\"), input.Handle);"); w.AppendLine ($"\t\t\t\tAssert.AreEqual (value.Length, obj.{v.Managed}Array.Length, \"after setting B\");"); w.AppendLine ($"\t\t\t\tAssert.AreEqual (value [0], obj.{v.Managed}Array [0], \"after setting B element\");"); @@ -1054,7 +1057,7 @@ public class RegistrarTestGenerated {"); w.AppendLine ($"\t\t\t\tobj.{v.Managed}Array = value;"); w.AppendLine ($"\t\t\t\tarray = Runtime.GetNSObject (Messaging.IntPtr_objc_msgSend (obj.Handle, Selector.GetHandle (\"get{v.Managed}Array\")));"); w.AppendLine ($"\t\t\t\tAssert.AreEqual (value.Length, array.Count, \"getter B\");"); - w.AppendLine ($"\t\t\t\tAssert.AreEqual (value [0], array.GetItem (0){v.Map}, \"getter B element\");"); + w.AppendLine ($"\t\t\t\tAssert.AreEqual (value [0], {v.FromNSNumberCastExpression}array.GetItem (0){v.Map}, \"getter B element\");"); w.AppendLine (); w.AppendLine ($"\t\t\t}}"); diff --git a/tools/common/StaticRegistrar.cs b/tools/common/StaticRegistrar.cs index c308dd0ceb7b..d97cea876d5f 100644 --- a/tools/common/StaticRegistrar.cs +++ b/tools/common/StaticRegistrar.cs @@ -3734,6 +3734,8 @@ string GetManagedToNSNumberFunc (TypeReference managedType, TypeReference inputT case "System.nfloat": return "xamarin_nfloat_to_nsnumber"; case "System.Boolean": return "xamarin_bool_to_nsnumber"; default: + if (IsEnum (managedType)) + return GetManagedToNSNumberFunc (GetEnumUnderlyingType (managedType), inputType, outputType, descriptiveMethodName); throw ErrorHelper.CreateError (99, $"Internal error: can't convert from '{inputType.FullName}' to '{outputType.FullName}' in {descriptiveMethodName}. Please file a bug report with a test case (https://bugzilla.xamarin.com)."); } } @@ -3757,6 +3759,8 @@ string GetNSNumberToManagedFunc (TypeReference managedType, TypeReference inputT case "System.nfloat": nativeType = "CGFloat"; return "xamarin_nsnumber_to_nfloat"; case "System.Boolean": nativeType = "BOOL"; return "xamarin_nsnumber_to_bool"; default: + if (IsEnum (managedType)) + return GetNSNumberToManagedFunc (GetEnumUnderlyingType (managedType), inputType, outputType, descriptiveMethodName, out nativeType); throw ErrorHelper.CreateError (99, $"Internal error: can't convert from '{inputType.FullName}' to '{outputType.FullName}' in {descriptiveMethodName}. Please file a bug report with a test case (https://bugzilla.xamarin.com)."); } } From 1702359b21e239e5ab9357ff01b991b37d5ed050 Mon Sep 17 00:00:00 2001 From: Rolf Bjarne Kvinge Date: Fri, 7 Jul 2017 12:27:13 +0200 Subject: [PATCH 28/46] [registrar] Verify if the BindAs type matches the type in the method signature. --- src/ObjCRuntime/Registrar.cs | 25 +++++++++++++++-- tests/mtouch/RegistrarTest.cs | 48 +++++++++++++++++++++++++++++++++ tools/common/StaticRegistrar.cs | 6 ++--- 3 files changed, 74 insertions(+), 5 deletions(-) diff --git a/src/ObjCRuntime/Registrar.cs b/src/ObjCRuntime/Registrar.cs index eb58f614f9bb..188027a3f54e 100644 --- a/src/ObjCRuntime/Registrar.cs +++ b/src/ObjCRuntime/Registrar.cs @@ -1077,13 +1077,34 @@ protected bool IsEnum (TType type) public BindAsAttribute GetBindAsAttribute (ObjCMethod method, int parameter_index) { var attrib = GetBindAsAttribute (method.Method, parameter_index); - if (attrib != null) + if (attrib != null) { + var type = parameter_index == -1 ? GetReturnType (method.Method) : GetParameters (method.Method) [parameter_index]; + if (parameter_index == -1) { + var returnType = GetReturnType (method.Method); + if (!AreEqual (returnType, attrib.Type)) + throw CreateException (4171, method.Method, $"The BindAs attribute on the return value of the method {method.DescriptiveMethodName} is invalid: the BindAs type {GetTypeFullName (attrib.Type)} is different from the return type {GetTypeFullName (returnType)}."); + } else { + var parameterType = GetParameters (method.Method) [parameter_index]; + if (IsByRef (parameterType)) + parameterType = GetElementType (parameterType); + if (!AreEqual (parameterType, attrib.Type)) + throw CreateException (4171, method.Method, $"The BindAs attribute on the parameter #{parameter_index + 1} is invalid: the BindAs type {GetTypeFullName (attrib.Type)} is different from the parameter type {GetTypeFullName (parameterType)}."); + } + return attrib; + } if (!method.IsPropertyAccessor) return null; - return GetBindAsAttribute (FindProperty (method.DeclaringType.Type, method.MethodName.Substring (4))); + var property = FindProperty (method.DeclaringType.Type, method.MethodName.Substring (4)); + attrib = GetBindAsAttribute (property); + if (attrib != null) { + var propertyType = GetPropertyType (property); + if (!AreEqual (propertyType, attrib.Type)) + throw CreateException (4171, property, $"The BindAs attribute on the property {GetTypeFullName (method.DeclaringType.Type)}.{GetPropertyName (property)} is invalid: the BindAs type {GetTypeFullName (attrib.Type)} is different from the property type {GetTypeFullName (propertyType)}."); + } + return attrib; } bool IsSmartEnum (TType type) diff --git a/tests/mtouch/RegistrarTest.cs b/tests/mtouch/RegistrarTest.cs index 16e30ac71076..d7dfc91ef351 100644 --- a/tests/mtouch/RegistrarTest.cs +++ b/tests/mtouch/RegistrarTest.cs @@ -758,6 +758,54 @@ class X : NSObject { } } + [Test] + public void MT4171 () + { + using (var mtouch = new MTouchTool ()) { + var code = @" + namespace NS { + using System; + using Foundation; + using ObjCRuntime; + class X : NSObject { + [Export (""a:"")] + void A ([BindAs (typeof (DateTime), OriginalType = typeof (NSNumber))] ConsoleColor value) {} + + [Export (""b:"")] + void B ([BindAs (typeof (DateTime?), OriginalType = typeof (NSNumber))] ConsoleColor? value) {} + + [Export (""C"")] + [return: BindAs (typeof (DateTime), OriginalType = typeof (NSNumber))] + ConsoleColor C () { throw new NotImplementedException (); } + + [Export (""d"")] + [return: BindAs (typeof (DateTime?), OriginalType = typeof (NSNumber))] + ConsoleColor? D () { throw new NotImplementedException (); } + + [Export (""E"")] + [BindAs (typeof (DateTime), OriginalType = typeof (NSNumber))] + ConsoleColor E { get; set; } + + [Export (""F"")] + [BindAs (typeof (DateTime?), OriginalType = typeof (NSNumber))] + ConsoleColor? F { get; set; } + } + }"; + mtouch.Linker = MTouchLinker.DontLink; // faster + mtouch.Registrar = MTouchRegistrar.Static; + mtouch.CreateTemporaryApp (extraCode: code, extraArg: "-debug"); + mtouch.CreateTemporaryCacheDirectory (); + mtouch.AssertExecuteFailure (MTouchAction.BuildSim, "build"); + mtouch.AssertError (4138, "The registrar cannot marshal the property type 'System.ConsoleColor' of the property 'NS.X.E'.", "testApp.cs", 23); + mtouch.AssertError (4138, "The registrar cannot marshal the property type 'System.Nullable`1' of the property 'NS.X.F'.", "testApp.cs", 27); + mtouch.AssertError (4171, "The BindAs attribute on the parameter #1 is invalid: the BindAs type System.DateTime is different from the parameter type System.ConsoleColor.", "testApp.cs", 8); + mtouch.AssertError (4171, "The BindAs attribute on the parameter #1 is invalid: the BindAs type System.Nullable`1 is different from the parameter type System.Nullable`1.", "testApp.cs", 11); + mtouch.AssertError (4171, "The BindAs attribute on the return value of the method NS.X.C is invalid: the BindAs type System.DateTime is different from the return type System.ConsoleColor.", "testApp.cs", 15); + mtouch.AssertError (4171, "The BindAs attribute on the return value of the method NS.X.D is invalid: the BindAs type System.Nullable`1 is different from the return type System.Nullable`1.", "testApp.cs", 19); + mtouch.AssertErrorCount (8 /* 2 errors are duplicated */); + } + } + [Test] public void MT4172 () { diff --git a/tools/common/StaticRegistrar.cs b/tools/common/StaticRegistrar.cs index d97cea876d5f..9abd7d5894b0 100644 --- a/tools/common/StaticRegistrar.cs +++ b/tools/common/StaticRegistrar.cs @@ -1590,7 +1590,7 @@ static BindAsAttribute CreateBindAsAttribute (CustomAttribute attrib, IMemberDef switch (attrib.ConstructorArguments.Count) { case 1: var t1 = (TypeReference) attrib.ConstructorArguments [0].Value; - return new BindAsAttribute (t1 != null ? t1.Resolve () : null) { OriginalType = originalType }; + return new BindAsAttribute (t1) { OriginalType = originalType }; default: throw ErrorHelper.CreateError (4124, "Invalid BindAsAttribute found on '{0}.{1}'. Please file a bug report at https://bugzilla.xamarin.com", member.DeclaringType.FullName, member.Name); } @@ -4291,12 +4291,12 @@ class ProtocolAttribute : Attribute { class BindAsAttribute : Attribute { - public BindAsAttribute (TypeDefinition type) + public BindAsAttribute (TypeReference type) { this.Type = type; } - public TypeDefinition Type { get; set; } + public TypeReference Type { get; set; } public TypeReference OriginalType { get; set; } } From e540eb7cac570d4b9dbfd56a60ea0ee73b28b567 Mon Sep 17 00:00:00 2001 From: Rolf Bjarne Kvinge Date: Fri, 7 Jul 2017 12:28:53 +0200 Subject: [PATCH 29/46] [tests] Update tests since BindAs supports conversions between enums and NSNumber. --- tests/mtouch/RegistrarTest.cs | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/tests/mtouch/RegistrarTest.cs b/tests/mtouch/RegistrarTest.cs index d7dfc91ef351..4dc7f44af9b7 100644 --- a/tests/mtouch/RegistrarTest.cs +++ b/tests/mtouch/RegistrarTest.cs @@ -741,19 +741,20 @@ namespace NS { using ObjCRuntime; class X : NSObject { [Export (""a"")] - [return: BindAs (typeof (ConsoleColor), OriginalType = typeof (NSNumber))] - ConsoleColor A () { throw new NotImplementedException (); } + [return: BindAs (typeof (DateTime), OriginalType = typeof (NSNumber))] + DateTime A () { throw new NotImplementedException (); } [Export (""b"")] - [return: BindAs (typeof (ConsoleColor?), OriginalType = typeof (NSNumber))] - ConsoleColor? B () { throw new NotImplementedException (); } + [return: BindAs (typeof (DateTime?), OriginalType = typeof (NSNumber))] + DateTime? B () { throw new NotImplementedException (); } } }"; mtouch.Linker = MTouchLinker.DontLink; // faster mtouch.Registrar = MTouchRegistrar.Static; mtouch.CreateTemporaryApp (extraCode: code, extraArg: "-debug"); + mtouch.CreateTemporaryCacheDirectory (); mtouch.AssertExecuteFailure (MTouchAction.BuildSim, "build"); - mtouch.AssertError (4170, "The registrar can't convert from 'System.ConsoleColor' to 'Foundation.NSNumber' for the return value in the method NS.X.A.", "testApp.cs", 9); - mtouch.AssertError (4170, "The registrar can't convert from 'System.Nullable`1' to 'Foundation.NSNumber' for the return value in the method NS.X.B.", "testApp.cs", 12); + mtouch.AssertError (4170, "The registrar can't convert from 'System.DateTime' to 'Foundation.NSNumber' for the return value in the method NS.X.A.", "testApp.cs", 9); + mtouch.AssertError (4170, "The registrar can't convert from 'System.Nullable`1' to 'Foundation.NSNumber' for the return value in the method NS.X.B.", "testApp.cs", 12); mtouch.AssertErrorCount (4 /* errors are duplicated */); } } @@ -817,9 +818,9 @@ namespace NS { using ObjCRuntime; class X : NSObject { [Export (""a:"")] - void A ([BindAs (typeof (ConsoleColor), OriginalType = typeof (NSNumber))] ConsoleColor value) {} + void A ([BindAs (typeof (DateTime), OriginalType = typeof (NSNumber))] DateTime value) {} [Export (""b:"")] - void B ([BindAs (typeof (ConsoleColor?), OriginalType = typeof (NSNumber))] ConsoleColor? value) {} + void B ([BindAs (typeof (DateTime?), OriginalType = typeof (NSNumber))] DateTime? value) {} [Export (""d:"")] void D ([BindAs (typeof (int?[]), OriginalType = typeof (NSNumber[]))] int?[] value) {} [Export (""e:"")] @@ -845,8 +846,8 @@ void D ([BindAs (typeof (E?[]), OriginalType = typeof (NSString[]))] E?[] value) mtouch.Registrar = MTouchRegistrar.Static; mtouch.CreateTemporaryApp (extraCode: code, extraArg: "-debug"); mtouch.AssertExecuteFailure (MTouchAction.BuildSim, "build"); - mtouch.AssertError (4172, "The registrar can't convert from 'System.ConsoleColor' to 'Foundation.NSNumber' for the parameter 'value' in the method NS.X.A.", "testApp.cs", 8); - mtouch.AssertError (4172, "The registrar can't convert from 'System.Nullable`1' to 'Foundation.NSNumber' for the parameter 'value' in the method NS.X.B.", "testApp.cs", 10); + mtouch.AssertError (4172, "The registrar can't convert from 'System.DateTime' to 'Foundation.NSNumber' for the parameter 'value' in the method NS.X.A.", "testApp.cs", 8); + mtouch.AssertError (4172, "The registrar can't convert from 'System.Nullable`1' to 'Foundation.NSNumber' for the parameter 'value' in the method NS.X.B.", "testApp.cs", 10); mtouch.AssertError (4172, "The registrar can't convert from 'System.Nullable`1[]' to 'Foundation.NSNumber[]' for the parameter 'value' in the method NS.X.D.", "testApp.cs", 12); mtouch.AssertError (4172, "The registrar can't convert from 'System.Int32&' to 'Foundation.NSNumber' for the parameter 'value' in the method NS.X.E.", "testApp.cs", 14); mtouch.AssertError (4172, "The registrar can't convert from 'System.Int32&' to 'Foundation.NSNumber' for the parameter 'value' in the method NS.X.F.", "testApp.cs", 16); From f795fc9b4a3cee847768806395f626eedb828b11 Mon Sep 17 00:00:00 2001 From: Rolf Bjarne Kvinge Date: Fri, 7 Jul 2017 17:13:14 +0200 Subject: [PATCH 30/46] [docs] Add MT4171, and describe MT4172/4170. --- docs/website/mtouch-errors.md | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/docs/website/mtouch-errors.md b/docs/website/mtouch-errors.md index a95f0b595433..d4012e3590d7 100644 --- a/docs/website/mtouch-errors.md +++ b/docs/website/mtouch-errors.md @@ -1550,8 +1550,27 @@ Please check the reported error message for the underlying cause. ### MT4170: The registrar can't convert from '{managed type}' to '{native type}' for the return value in the method {method}. +See the description of error MT4172. + +### MT4171: The BindAs attribute on the member {member} is invalid: the BindAs type {type} is different from the property type {type}. + +Please make sure the type in the BindAs attribute matches the type of the member it's attached to. + ### MT4172: The registrar can't convert from '{native type}' to '{managed type}' for the parameter '{parameter name}' in the method {method}. +The registrar does not support converting between the mentioned types. + +This is a bug in Xamarin.iOS if the API in question is provided by Xamarin.iOS; +please file a bug at [http://bugzilla.xamarin.com][1]. + +If you run into this while developing a binding project for a native library, +we're open to adding support for new combinations of types. If this is the +case, please file an enhancement request ([http://bugzilla.xamarin.com][2]) +with a test case and we'll evaluate it. + +[1]: https://bugzilla.xamarin.com/enter_bug.cgi?product=iOS +[2]: https://bugzilla.xamarin.com/enter_bug.cgi?product=iOS&component=General&bug_severity=enhancement + # MT5xxx: GCC and toolchain error messages ### MT51xx: Compilation From 516232c79c1b25092c7d3ae05e8f80a9f751f531 Mon Sep 17 00:00:00 2001 From: Rolf Bjarne Kvinge Date: Tue, 11 Jul 2017 09:48:56 +0200 Subject: [PATCH 31/46] [runtime] Optimize code that doesn't need an alloc/free pair. --- runtime/trampolines.m | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/runtime/trampolines.m b/runtime/trampolines.m index 917e85566f15..3bfdfe068fc9 100644 --- a/runtime/trampolines.m +++ b/runtime/trampolines.m @@ -980,11 +980,8 @@ goto exception_handling; #if MONOMAC - if (xamarin_use_new_assemblies && !strncmp (fullname, "MonoMac.", 8)) { - char *tmp_to_name = xamarin_strdup_printf ("%s", fullname + 8); - xamarin_free (fullname); - fullname = tmp_to_name; - } + if (xamarin_use_new_assemblies && !strncmp (fullname, "MonoMac.", 8)) + memmove (fullname, fullname + 8, strlen (fullname) - 7 /* also copy the null char */); #endif if (!strcmp (fullname, "Foundation.NSRange")) { From e040f0603af5e1227da62b116980076ca494b95f Mon Sep 17 00:00:00 2001 From: Rolf Bjarne Kvinge Date: Tue, 11 Jul 2017 09:49:26 +0200 Subject: [PATCH 32/46] [linker] Fix debug spew to be proper warnings. --- src/ObjCRuntime/ErrorHelper.cs | 89 ++++++++++++++++--- .../PreserveSmartEnumConversionsSubStep.cs | 26 ++++-- 2 files changed, 96 insertions(+), 19 deletions(-) diff --git a/src/ObjCRuntime/ErrorHelper.cs b/src/ObjCRuntime/ErrorHelper.cs index f6703f5aa1cd..b9ea1ebfd152 100644 --- a/src/ObjCRuntime/ErrorHelper.cs +++ b/src/ObjCRuntime/ErrorHelper.cs @@ -118,6 +118,77 @@ public static void SetLocation (Application app, ProductException ex, Mono.Cecil } public static ProductException CreateError (Application app, int code, Mono.Cecil.MemberReference member, string message, params object[] args) + { + return Create (app, code, true, null, member, message, args); + } + + public static ProductException CreateError (Application app, int code, Mono.Cecil.MethodDefinition location, string message, params object[] args) + { + return Create (app, code, true, null, location, message, args); + } + + public static ProductException CreateError (Application app, int code, Mono.Cecil.ICustomAttributeProvider provider, string message, params object [] args) + { + return Create (app, code, true, null, provider, message, args); + } + + public static ProductException CreateError (Application app, int code, Exception innerException, Mono.Cecil.MethodDefinition location, string message, params object[] args) + { + return Create (app, code, true, innerException, location, message, args); + } + + public static ProductException CreateError (Application app, int code, Exception innerException, Mono.Cecil.TypeReference location, string message, params object[] args) + { + return Create (app, code, true, innerException, location, message, args); + } + + public static ProductException CreateError (Application app, int code, Exception innerException, Mono.Cecil.ICustomAttributeProvider provider, string message, params object [] args) + { + return Create (app, code, true, innerException, provider, message, args); + } + + public static ProductException CreateWarning (Application app, int code, Mono.Cecil.MemberReference member, string message, params object [] args) + { + return Create (app, code, false, null, member, message, args); + } + + public static ProductException CreateWarning (Application app, int code, Mono.Cecil.MethodDefinition location, string message, params object[] args) + { + return Create (app, code, false, null, location, message, args); + } + + public static ProductException CreateWarning (Application app, int code, Mono.Cecil.ICustomAttributeProvider provider, string message, params object [] args) + { + return Create (app, code, false, null, provider, message, args); + } + + public static ProductException CreateWarning (Application app, int code, Exception innerException, Mono.Cecil.MethodDefinition location, string message, params object[] args) + { + return Create (app, code, false, innerException, location, message, args); + } + + public static ProductException CreateWarning (Application app, int code, Exception innerException, Mono.Cecil.TypeReference location, string message, params object[] args) + { + return Create (app, code, false, innerException, location, message, args); + } + + public static ProductException CreateWarning (Application app, int code, Exception innerException, Mono.Cecil.ICustomAttributeProvider provider, string message, params object [] args) + { + return Create (app, code, false, innerException, provider, message, args); + } + + public static ProductException Create (Application app, int code, bool error, Exception innerException, Mono.Cecil.ICustomAttributeProvider provider, string message, params object [] args) + { + if (provider is Mono.Cecil.MemberReference member) + return Create (app, code, error, innerException, member, message, args); + + if (provider is Mono.Cecil.TypeReference type) + return Create (app, code, error, innerException, type, message, args); + + return new ProductException (code, error, innerException, message, args); + } + + public static ProductException Create (Application app, int code, bool error, Exception innerException, Mono.Cecil.MemberReference member, string message, params object [] args) { Mono.Cecil.MethodReference method = member as Mono.Cecil.MethodReference; if (method == null) { @@ -128,28 +199,20 @@ public static ProductException CreateError (Application app, int code, Mono.Ceci method = property.SetMethod; } } - return CreateError (app, code, method == null ? null : method.Resolve (), message, args); + return Create (app, code, error, innerException, method == null ? null : method.Resolve (), message, args); } - public static ProductException CreateError (Application app, int code, Mono.Cecil.MethodDefinition location, string message, params object[] args) + public static ProductException Create (Application app, int code, bool error, Exception innerException, Mono.Cecil.MethodDefinition location, string message, params object [] args) { - var e = new ProductException (code, true, message, args); + var e = new ProductException (code, error, innerException, message, args); if (location != null) SetLocation (app, e, location); return e; } - public static ProductException CreateError (Application app, int code, Exception innerException, Mono.Cecil.MethodDefinition location, string message, params object[] args) - { - var e = new ProductException (code, true, innerException, message, args); - if (location != null) - SetLocation (app, e, location); - return e; - } - - public static ProductException CreateError (Application app, int code, Exception innerException, Mono.Cecil.TypeReference location, string message, params object[] args) + public static ProductException Create (Application app, int code, bool error, Exception innerException, Mono.Cecil.TypeReference location, string message, params object [] args) { - var e = new ProductException (code, true, innerException, message, args); + var e = new ProductException (code, error, innerException, message, args); if (location != null) { var td = location.Resolve (); diff --git a/tools/linker/MonoTouch.Tuner/PreserveSmartEnumConversionsSubStep.cs b/tools/linker/MonoTouch.Tuner/PreserveSmartEnumConversionsSubStep.cs index aef98062bf56..328c7cbb25a5 100644 --- a/tools/linker/MonoTouch.Tuner/PreserveSmartEnumConversionsSubStep.cs +++ b/tools/linker/MonoTouch.Tuner/PreserveSmartEnumConversionsSubStep.cs @@ -9,6 +9,8 @@ using Mono.Tuner; using MonoTouch.Tuner; +using Xamarin.Bundler; + namespace Xamarin.Linker.Steps { public class PreserveSmartEnumConversionsSubStep : ExceptionalSubStep @@ -42,6 +44,13 @@ void Preserve (Tuple pair, MethodDefinition } } + static string ProviderToString (ICustomAttributeProvider provider) + { + if (provider is MemberReference member) + return member.DeclaringType.FullName + "." + member.Name; + return provider.ToString (); + } + void ProcessAttributeProvider (ICustomAttributeProvider provider, MethodDefinition conditionA, MethodDefinition conditionB = null) { if (provider?.HasCustomAttributes != true) @@ -54,14 +63,14 @@ void ProcessAttributeProvider (ICustomAttributeProvider provider, MethodDefiniti continue; if (ca.ConstructorArguments.Count != 1) { - Console.WriteLine ("WARNING"); + ErrorHelper.Show (ErrorHelper.CreateWarning (LinkContext.Target.App, 4124, provider, "Invalid BindAsAttribute found on '{0}': should have 1 constructor arguments, found {1}. Please file a bug report at https://bugzilla.xamarin.com", ProviderToString (provider), ca.ConstructorArguments.Count)); continue; } var managedType = ca.ConstructorArguments [0].Value as TypeReference; var managedEnumType = managedType?.GetElementType ().Resolve (); if (managedEnumType == null) { - Console.WriteLine ("WARNING"); + ErrorHelper.Show (ErrorHelper.CreateWarning (LinkContext.Target.App, 4124, provider, "Invalid BindAsAttribute found on '{0}': could not find the underlying enum type of {1}. Please file a bug report at https://bugzilla.xamarin.com", ProviderToString (provider), managedType?.FullName)); continue; } @@ -71,7 +80,6 @@ void ProcessAttributeProvider (ICustomAttributeProvider provider, MethodDefiniti continue; } - Console.WriteLine (managedEnumType); // Find the Extension type TypeDefinition extensionType = null; var extensionName = managedEnumType.Name + "Extensions"; @@ -84,7 +92,7 @@ void ProcessAttributeProvider (ICustomAttributeProvider provider, MethodDefiniti break; } if (extensionType == null) { - Console.WriteLine ("WARNING"); + ErrorHelper.Show (ErrorHelper.CreateWarning (LinkContext.Target.App, 4124, provider, "Invalid BindAsAttribute found on '{0}': could not find the smart extension type {1}.{2}. Please file a bug report at https://bugzilla.xamarin.com", ProviderToString (provider), managedEnumType.Namespace, extensionName)); continue; } @@ -111,8 +119,14 @@ void ProcessAttributeProvider (ICustomAttributeProvider provider, MethodDefiniti getValue = method; } } - if (getConstant == null || getValue == null) { - Console.WriteLine ("WARNING"); + + if (getConstant == null) { + ErrorHelper.Show (ErrorHelper.CreateWarning (LinkContext.Target.App, 4124, provider, "Invalid BindAsAttribute found on '{0}': could not find the GetConstant method in the type {1}. Please file a bug report at https://bugzilla.xamarin.com", ProviderToString (provider), extensionType.FullName)); + continue; + } + + if (getValue == null) { + ErrorHelper.Show (ErrorHelper.CreateWarning (LinkContext.Target.App, 4124, provider, "Invalid BindAsAttribute found on '{0}': could not find the GetValue method in the type {1}. Please file a bug report at https://bugzilla.xamarin.com", ProviderToString (provider), extensionType.FullName)); continue; } From 9859e04ee2847346b63581d19a7503cdc9e783f1 Mon Sep 17 00:00:00 2001 From: Rolf Bjarne Kvinge Date: Tue, 11 Jul 2017 09:49:37 +0200 Subject: [PATCH 33/46] [registrar] Limit XM-only code to XM. --- src/ObjCRuntime/Registrar.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/ObjCRuntime/Registrar.cs b/src/ObjCRuntime/Registrar.cs index 188027a3f54e..ffee3751762d 100644 --- a/src/ObjCRuntime/Registrar.cs +++ b/src/ObjCRuntime/Registrar.cs @@ -660,9 +660,11 @@ bool IsValidToManagedTypeConversion (TType inputType, TType outputType) return Registrar.IsEnum (underlyingOutputType); } } else if (Registrar.Is (underlyingInputType, Foundation, "NSValue")) { +#if MMP || MONOMAC // Remove 'MonoMac.' namespace prefix to make switch smaller if (!Registrar.IsDualBuild && outputTypeName.StartsWith ("MonoMac.", StringComparison.Ordinal)) outputTypeName = outputTypeName.Substring ("MonoMac.".Length); +#endif switch (outputTypeName) { case "CoreAnimation.CATransform3D": From 99cc47e99b3c493b8d08745f0d3174c3fd7c39d5 Mon Sep 17 00:00:00 2001 From: Rolf Bjarne Kvinge Date: Tue, 11 Jul 2017 09:57:18 +0200 Subject: [PATCH 34/46] [linker] Only look for BindAs attributes in assemblies that reference the BindAsAttribute type. --- .../PreserveSmartEnumConversionsSubStep.cs | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/tools/linker/MonoTouch.Tuner/PreserveSmartEnumConversionsSubStep.cs b/tools/linker/MonoTouch.Tuner/PreserveSmartEnumConversionsSubStep.cs index 328c7cbb25a5..4dde346037db 100644 --- a/tools/linker/MonoTouch.Tuner/PreserveSmartEnumConversionsSubStep.cs +++ b/tools/linker/MonoTouch.Tuner/PreserveSmartEnumConversionsSubStep.cs @@ -27,9 +27,16 @@ public override SubStepTargets Targets { public override bool IsActiveFor (AssemblyDefinition assembly) { - // we need to process all assemblies, because the functions we want to - // preserve are not necessarily in the assembly we're processing. - return true; + if (Profile.IsProductAssembly (assembly)) + return true; + + // We don't need to process assemblies that don't reference ObjCRuntime.BindAsAttribute. + foreach (var tr in assembly.MainModule.GetTypeReferences ()) { + if (tr.IsPlatformType ("ObjCRuntime", "BindAsAttribute")) + return true; + } + + return false; } void Preserve (Tuple pair, MethodDefinition conditionA, MethodDefinition conditionB = null) From 4d393a683de9bc1f401245c61c239ba79269e6eb Mon Sep 17 00:00:00 2001 From: Rolf Bjarne Kvinge Date: Tue, 11 Jul 2017 12:29:15 +0200 Subject: [PATCH 35/46] [runtime] Use composable strings to save space. --- runtime/runtime.m | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/runtime/runtime.m b/runtime/runtime.m index 6bf1c9cd249e..82b164ac8bc4 100644 --- a/runtime/runtime.m +++ b/runtime/runtime.m @@ -358,7 +358,7 @@ void xamarin_framework_peer_unlock () xamarin_get_nsvalue_class () { if (nsvalue_class == NULL) - xamarin_assertion_message ("Internal consistency error, please file a bug (https://bugzilla.xamarin.com). Additional data: can't get the NSValue class because it's been linked away.\n"); + xamarin_assertion_message ("Internal consistency error, please file a bug (https://bugzilla.xamarin.com). Additional data: can't get the %s class because it's been linked away.\n", "NSValue"); return nsvalue_class; } @@ -366,7 +366,7 @@ void xamarin_framework_peer_unlock () xamarin_get_nsnumber_class () { if (nsnumber_class == NULL) - xamarin_assertion_message ("Internal consistency error, please file a bug (https://bugzilla.xamarin.com). Additional data: can't get the NSNumber class because it's been linked away.\n"); + xamarin_assertion_message ("Internal consistency error, please file a bug (https://bugzilla.xamarin.com). Additional data: can't get the %s class because it's been linked away.\n", "NSNumber"); return nsnumber_class; } From f1dd9985f41e4c890e4cff207a6990cdf00b6b07 Mon Sep 17 00:00:00 2001 From: Rolf Bjarne Kvinge Date: Tue, 11 Jul 2017 13:28:36 +0200 Subject: [PATCH 36/46] [generator] Refactor to extract common code (property fetch) into a variable. --- src/generator.cs | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/src/generator.cs b/src/generator.cs index c2c78c7dc8ae..3d5e5d6ea05d 100644 --- a/src/generator.cs +++ b/src/generator.cs @@ -1977,12 +1977,13 @@ void DeclareInvoker (MethodInfo mi) try { if (Compat) { - bool arm_stret = Stret.ArmNeedStret (mi.ReturnType); + var returnType = mi.ReturnType; + bool arm_stret = Stret.ArmNeedStret (returnType); bool is_aligned = AttributeManager.HasAttribute (mi); RegisterMethod (arm_stret, mi, MakeSig (mi, arm_stret, arm_stret && is_aligned), arm_stret && is_aligned); RegisterMethod (arm_stret, mi, MakeSuperSig (mi, arm_stret, arm_stret && is_aligned), arm_stret && is_aligned); - bool x86_stret = Stret.X86NeedStret (mi.ReturnType); + bool x86_stret = Stret.X86NeedStret (returnType); if (x86_stret != arm_stret){ RegisterMethod (x86_stret, mi, MakeSig (mi, x86_stret, x86_stret && is_aligned), x86_stret && is_aligned); RegisterMethod (x86_stret, mi, MakeSuperSig (mi, x86_stret, x86_stret && is_aligned), x86_stret && is_aligned); @@ -3624,8 +3625,9 @@ void GenerateInvoke (bool supercall, MethodInfo mi, MemberInformation minfo, str return; } - bool arm_stret = Stret.ArmNeedStret (mi.ReturnType); - bool x86_stret = Stret.X86NeedStret (mi.ReturnType); + var returnType = mi.ReturnType; + bool arm_stret = Stret.ArmNeedStret (returnType); + bool x86_stret = Stret.X86NeedStret (returnType); bool aligned = AttributeManager.HasAttribute (mi); if (CurrentPlatform == PlatformName.MacOSX) { @@ -3651,9 +3653,10 @@ void GenerateInvoke (bool supercall, MethodInfo mi, MemberInformation minfo, str void GenerateNewStyleInvoke (bool supercall, MethodInfo mi, MemberInformation minfo, string selector, string[] args, bool assign_to_temp, Type category_type) { - bool arm_stret = Stret.ArmNeedStret (mi.ReturnType); - bool x86_stret = Stret.X86NeedStret (mi.ReturnType); - bool x64_stret = Stret.X86_64NeedStret (mi.ReturnType); + var returnType = mi.ReturnType; + bool arm_stret = Stret.ArmNeedStret (returnType); + bool x86_stret = Stret.X86NeedStret (returnType); + bool x64_stret = Stret.X86_64NeedStret (returnType); bool dual_enum = HasNativeEnumInSignature (mi); bool is_stret_multi = arm_stret || x86_stret || x64_stret; bool need_multi_path = is_stret_multi || dual_enum; From e97f2f3244917f0d017318995e3cd49b08a89b63 Mon Sep 17 00:00:00 2001 From: Rolf Bjarne Kvinge Date: Tue, 11 Jul 2017 13:35:25 +0200 Subject: [PATCH 37/46] [src] Revert accidental whitespace change. --- src/generator-enums.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/generator-enums.cs b/src/generator-enums.cs index 3a98c6929310..8eba548a1cbe 100644 --- a/src/generator-enums.cs +++ b/src/generator-enums.cs @@ -164,7 +164,7 @@ void GenerateEnum (Type type) print ("}"); print (""); } - + print ("public static NSString GetConstant (this {0} self)", type.Name); print ("{"); indent++; @@ -188,7 +188,7 @@ void GenerateEnum (Type type) print ("}"); print (""); - + print ("public static {0} GetValue (NSString constant)", type.Name); print ("{"); indent++; From 607f947831d589d24d009852edbebb03f1aad737 Mon Sep 17 00:00:00 2001 From: Rolf Bjarne Kvinge Date: Tue, 11 Jul 2017 13:38:05 +0200 Subject: [PATCH 38/46] [src] Simplify conditional expression that would always be false. --- src/generator.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/generator.cs b/src/generator.cs index 3d5e5d6ea05d..924e35977320 100644 --- a/src/generator.cs +++ b/src/generator.cs @@ -1434,7 +1434,7 @@ string GetFromBindAsWrapper (MemberInformation minfo, out string suffix) var arrRetType = arrIsNullable ? nullableElementType : retType.GetElementType (); var valueFetcher = string.Empty; if (arrType == TypeManager.NSString) - append = $"ptr => {{\n\tusing (var str = Runtime.GetNSObject (ptr)) {{\n\t\treturn {FormatType (arrRetType.DeclaringType, arrRetType)}Extensions.{(isNullable ? "GetNullableValue" : "GetValue")} (str);\n\t}}\n}}"; + append = $"ptr => {{\n\tusing (var str = Runtime.GetNSObject (ptr)) {{\n\t\treturn {FormatType (arrRetType.DeclaringType, arrRetType)}Extensions.GetValue (str);\n\t}}\n}}"; else if (arrType == TypeManager.NSNumber) { if (NSNumberReturnMap.TryGetValue (arrRetType, out valueFetcher) || arrRetType.IsEnum) { var getterStr = string.Format ("{0}{1}", arrIsNullable ? "?" : string.Empty, arrRetType.IsEnum ? ".Int32Value" : valueFetcher); From 2dd10e7cb410b0687f891d4cf5312dbcf12c57cb Mon Sep 17 00:00:00 2001 From: Rolf Bjarne Kvinge Date: Tue, 11 Jul 2017 18:27:28 +0200 Subject: [PATCH 39/46] [registrar] Improve error message. --- tools/common/StaticRegistrar.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/common/StaticRegistrar.cs b/tools/common/StaticRegistrar.cs index 9abd7d5894b0..582c3ebe1b3e 100644 --- a/tools/common/StaticRegistrar.cs +++ b/tools/common/StaticRegistrar.cs @@ -3050,7 +3050,7 @@ void Specialize (AutoIndentStringBuilder sb, ObjCMethod method, List if (type != nativetype) { GenerateConversionToManaged (nativetype, type, setup_call_stack, descriptiveMethodName, ref exceptions, method, $"p{i}", $"arg_ptrs [{i}]", $"mono_class_from_mono_type (xamarin_get_parameter_type (managed_method, {i}))"); if (isRef || isOut) - throw new Exception (); + throw ErrorHelper.CreateError (4163, $"Internal error in the registrar (BindAs parameters can't be ref/out: {descriptiveMethodName}). Please file a bug report at https://bugzilla.xamarin.com"); continue; } else if (isRef) { type = type.GetElementType (); From ee36719d8a95334193b1b6dc938eb24bf069dd4c Mon Sep 17 00:00:00 2001 From: Rolf Bjarne Kvinge Date: Fri, 22 Sep 2017 11:58:37 +0200 Subject: [PATCH 40/46] [runtime] Ask clang to be quiet about using new API for our conversion methods. --- runtime/trampolines.m | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/runtime/trampolines.m b/runtime/trampolines.m index 3bfdfe068fc9..d0ca72ae7bc9 100644 --- a/runtime/trampolines.m +++ b/runtime/trampolines.m @@ -761,6 +761,9 @@ return convertedValue; } +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wunguarded-availability-new" + // Returns a pointer to the value type, which must be freed using xamarin_free. // If called multiple times in succession, the returned pointer can be passed as the second ptr argument, and it need only be freed once done iterating. void *xamarin_nsnumber_to_bool (NSNumber *number, void *ptr, MonoClass *managedType, guint32 *exception_gchandle) { BOOL *valueptr = (BOOL *) (ptr ? ptr : xamarin_calloc (sizeof (BOOL))); *valueptr = [number boolValue]; return valueptr; } @@ -905,6 +908,8 @@ id xamarin_uioffset_to_nsvalue (MonoObject *value, guint32 *exception_gchandle) { return [NSValue valueWithUIOffset: *(UIOffset *) mono_object_unbox (value)]; } #endif +#pragma clang diagnostic pop + static void * xamarin_get_nsnumber_converter (MonoClass *managedType, MonoMethod *method, bool to_managed, guint32 *exception_gchandle) { From 9114a12800654c2ed9abb92e2557c8958db59914 Mon Sep 17 00:00:00 2001 From: Rolf Bjarne Kvinge Date: Mon, 25 Sep 2017 16:06:39 +0200 Subject: [PATCH 41/46] [tests] Make more tests Xcode-version-aware. --- tests/test-libraries/testgenerator.cs | 32 +++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/tests/test-libraries/testgenerator.cs b/tests/test-libraries/testgenerator.cs index 47c6a2740261..efd007082e28 100644 --- a/tests/test-libraries/testgenerator.cs +++ b/tests/test-libraries/testgenerator.cs @@ -912,6 +912,10 @@ public class RegistrarTestGenerated {"); w.AppendLine ("\t\t[Test]"); w.AppendLine ($"\t\tpublic void NSNumberBindAs_{v.Managed}_Bindings ()"); w.AppendLine ("\t\t{"); + if (v.MinXcodeVersion != null) { + w.AppendLine ($"\t\t\tTestRuntime.AssertXcodeVersion ({v.MinXcodeVersion.Major}, {v.MinXcodeVersion.Minor});"); + w.AppendLine (); + } w.AppendLine ($"\t\t\tusing (var obj = new ObjCRegistrarTest ()) {{"); w.AppendLine ($"\t\t\t\tAssert.IsNull (obj.P{v.Managed}Number, \"initial null property\");"); w.AppendLine ($"\t\t\t\tAssert.IsNull (obj.P{v.Managed}NumberNullable, \"initial nullable null property\");"); @@ -947,6 +951,10 @@ public class RegistrarTestGenerated {"); w.AppendLine ("\t\t[Test]"); w.AppendLine ($"\t\tpublic void NSNumberBindAs_{v.Managed}_Overrides ()"); w.AppendLine ("\t\t{"); + if (v.MinXcodeVersion != null) { + w.AppendLine ($"\t\t\tTestRuntime.AssertXcodeVersion ({v.MinXcodeVersion.Major}, {v.MinXcodeVersion.Minor});"); + w.AppendLine (); + } w.AppendLine ($"\t\t\tusing (var obj = new BindAsTestClassGenerated ()) {{"); w.AppendLine ($"\t\t\t\tAssert.IsNull (obj.{v.Managed}Number, \"initial null\");"); w.AppendLine ($"\t\t\t\tMessaging.void_objc_msgSend_IntPtr (obj.Handle, Selector.GetHandle (\"set{v.Managed}NumberNullable:\"), IntPtr.Zero);"); @@ -992,6 +1000,10 @@ public class RegistrarTestGenerated {"); w.AppendLine ("\t\t[Test]"); w.AppendLine ($"\t\tpublic void NSNumberBindAs_{v.Managed}Array_Bindings ()"); w.AppendLine ("\t\t{"); + if (v.MinXcodeVersion != null) { + w.AppendLine ($"\t\t\tTestRuntime.AssertXcodeVersion ({v.MinXcodeVersion.Major}, {v.MinXcodeVersion.Minor});"); + w.AppendLine (); + } w.AppendLine ($"\t\t\tusing (var obj = new ObjCRegistrarTest ()) {{"); w.AppendLine ($"\t\t\t\tAssert.IsNull (obj.P{v.Managed}Array, \"initial null property\");"); w.AppendLine ($"\t\t\t\tAssert.IsNull (obj.Get{v.Managed}Array (), \"initial null method\");"); @@ -1023,6 +1035,10 @@ public class RegistrarTestGenerated {"); w.AppendLine ("\t\t[Test]"); w.AppendLine ($"\t\tpublic void NSNumberBindAs_{v.Managed}_Array_Overrides ()"); w.AppendLine ("\t\t{"); + if (v.MinXcodeVersion != null) { + w.AppendLine ($"\t\t\tTestRuntime.AssertXcodeVersion ({v.MinXcodeVersion.Major}, {v.MinXcodeVersion.Minor});"); + w.AppendLine (); + } w.AppendLine ($"\t\t\tusing (var obj = new BindAsTestClassGenerated ()) {{"); w.AppendLine ($"\t\t\t\tAssert.IsNull (obj.{v.Managed}Array, \"initial null\");"); w.AppendLine ($"\t\t\t\tMessaging.void_objc_msgSend_IntPtr (obj.Handle, Selector.GetHandle (\"set{v.Managed}Array:\"), IntPtr.Zero);"); @@ -1078,6 +1094,10 @@ public class RegistrarTestGenerated {"); w.AppendLine ("\t\t[Test]"); w.AppendLine ($"\t\tpublic void NSValueBindAs_{v.Managed}_Bindings ()"); w.AppendLine ("\t\t{"); + if (v.MinXcodeVersion != null) { + w.AppendLine ($"\t\t\tTestRuntime.AssertXcodeVersion ({v.MinXcodeVersion.Major}, {v.MinXcodeVersion.Minor});"); + w.AppendLine (); + } w.AppendLine ($"\t\t\tusing (var obj = new ObjCRegistrarTest ()) {{"); w.AppendLine ($"\t\t\t\tAssert.IsNull (obj.P{v.Managed}Value, \"initial null property\");"); w.AppendLine ($"\t\t\t\tAssert.IsNull (obj.P{v.Managed}ValueNullable, \"initial nullable null property\");"); @@ -1113,6 +1133,10 @@ public class RegistrarTestGenerated {"); w.AppendLine ("\t\t[Test]"); w.AppendLine ($"\t\tpublic void NSValueBindAs_{v.Managed}_Overrides ()"); w.AppendLine ("\t\t{"); + if (v.MinXcodeVersion != null) { + w.AppendLine ($"\t\t\tTestRuntime.AssertXcodeVersion ({v.MinXcodeVersion.Major}, {v.MinXcodeVersion.Minor});"); + w.AppendLine (); + } w.AppendLine ($"\t\t\tusing (var obj = new BindAsTestClassGenerated ()) {{"); w.AppendLine ($"\t\t\t\tAssert.IsNull (obj.{v.Managed}Value, \"initial null\");"); w.AppendLine ($"\t\t\t\tMessaging.void_objc_msgSend_IntPtr (obj.Handle, Selector.GetHandle (\"set{v.Managed}ValueNullable:\"), IntPtr.Zero);"); @@ -1158,6 +1182,10 @@ public class RegistrarTestGenerated {"); w.AppendLine ("\t\t[Test]"); w.AppendLine ($"\t\tpublic void NSValueBindAs_{v.Managed}Array_Bindings ()"); w.AppendLine ("\t\t{"); + if (v.MinXcodeVersion != null) { + w.AppendLine ($"\t\t\tTestRuntime.AssertXcodeVersion ({v.MinXcodeVersion.Major}, {v.MinXcodeVersion.Minor});"); + w.AppendLine (); + } w.AppendLine ($"\t\t\tusing (var obj = new ObjCRegistrarTest ()) {{"); w.AppendLine ($"\t\t\t\tAssert.IsNull (obj.P{v.Managed}Array, \"initial null property\");"); w.AppendLine ($"\t\t\t\tAssert.IsNull (obj.Get{v.Managed}Array (), \"initial null method\");"); @@ -1189,6 +1217,10 @@ public class RegistrarTestGenerated {"); w.AppendLine ("\t\t[Test]"); w.AppendLine ($"\t\tpublic void NSValueBindAs_{v.Managed}_Array_Overrides ()"); w.AppendLine ("\t\t{"); + if (v.MinXcodeVersion != null) { + w.AppendLine ($"\t\t\tTestRuntime.AssertXcodeVersion ({v.MinXcodeVersion.Major}, {v.MinXcodeVersion.Minor});"); + w.AppendLine (); + } w.AppendLine ($"\t\t\tusing (var obj = new BindAsTestClassGenerated ()) {{"); w.AppendLine ($"\t\t\t\tAssert.IsNull (obj.{v.Managed}Array, \"initial null\");"); w.AppendLine ($"\t\t\t\tMessaging.void_objc_msgSend_IntPtr (obj.Handle, Selector.GetHandle (\"set{v.Managed}Array:\"), IntPtr.Zero);"); From 49223162bebe43852eaa4c20c1f409ccc1808427 Mon Sep 17 00:00:00 2001 From: Rolf Bjarne Kvinge Date: Fri, 22 Sep 2017 12:00:08 +0200 Subject: [PATCH 42/46] Add support for NSDirectionalEdgeInsets NSValues to the BindAs attribute. --- runtime/trampolines.m | 4 ++++ runtime/xamarin/trampolines.h | 2 ++ src/ObjCRuntime/Registrar.cs | 1 + src/generator.cs | 1 + tests/test-libraries/testgenerator.cs | 1 + tools/common/StaticRegistrar.cs | 2 ++ 6 files changed, 11 insertions(+) diff --git a/runtime/trampolines.m b/runtime/trampolines.m index d0ca72ae7bc9..1a0373429337 100644 --- a/runtime/trampolines.m +++ b/runtime/trampolines.m @@ -796,6 +796,7 @@ void *xamarin_nsvalue_to_cgrect (NSValue *value, void *ptr, MonoClass *managedType, guint32 *exception_gchandle) { CGRect *valueptr = (CGRect *) (ptr ? ptr : xamarin_calloc (sizeof (CGRect))); *valueptr = [value CGRectValue]; return valueptr; } void *xamarin_nsvalue_to_cgsize (NSValue *value, void *ptr, MonoClass *managedType, guint32 *exception_gchandle) { CGSize *valueptr = (CGSize *) (ptr ? ptr : xamarin_calloc (sizeof (CGSize))); *valueptr = [value CGSizeValue]; return valueptr; } void *xamarin_nsvalue_to_cgvector (NSValue *value, void *ptr, MonoClass *managedType, guint32 *exception_gchandle) { CGVector *valueptr = (CGVector *) (ptr ? ptr : xamarin_calloc (sizeof (CGVector))); *valueptr = [value CGVectorValue]; return valueptr; } +void *xamarin_nsvalue_to_nsdirectionaledgeinsets(NSValue *value, void *ptr, MonoClass *managedType, guint32 *exception_gchandle) {NSDirectionalEdgeInsets *valueptr =(NSDirectionalEdgeInsets *) (ptr ? ptr : xamarin_calloc (sizeof (NSDirectionalEdgeInsets)));*valueptr = [value directionalEdgeInsetsValue];return valueptr; } #endif #if HAVE_COREANIMATION void *xamarin_nsvalue_to_catransform3d (NSValue *value, void *ptr, MonoClass *managedType, guint32 *exception_gchandle) { CATransform3D *valueptr = (CATransform3D *) (ptr ? ptr : xamarin_calloc (sizeof (CATransform3D))); *valueptr = [value CATransform3DValue]; return valueptr; } @@ -885,6 +886,7 @@ id xamarin_cgrect_to_nsvalue (MonoObject *value, guint32 *exception_gchandle) { return [NSValue valueWithCGRect: *(CGRect *) mono_object_unbox (value)]; } id xamarin_cgsize_to_nsvalue (MonoObject *value, guint32 *exception_gchandle) { return [NSValue valueWithCGSize: *(CGSize *) mono_object_unbox (value)]; } id xamarin_cgvector_to_nsvalue (MonoObject *value, guint32 *exception_gchandle) { return [NSValue valueWithCGVector: *(CGVector *) mono_object_unbox (value)]; } +id xamarin_nsdirectionaledgeinsets_to_nsvalue(MonoObject *value, guint32 *exception_gchandle) { return [NSValue valueWithDirectionalEdgeInsets:*(NSDirectionalEdgeInsets *)mono_object_unbox (value)]; } #endif #if HAVE_COREANIMATION id xamarin_catransform3d_to_nsvalue (MonoObject *value, guint32 *exception_gchandle) { return [NSValue valueWithCATransform3D: *(CATransform3D *) mono_object_unbox (value)]; } @@ -1002,6 +1004,8 @@ func = to_managed ? (void *) xamarin_nsvalue_to_cgsize : (void *) xamarin_cgsize_to_nsvalue; } else if (!strcmp (fullname, "CoreGraphics.CGVector")) { func = to_managed ? (void *) xamarin_nsvalue_to_cgvector : (void *) xamarin_cgvector_to_nsvalue; + } else if (!strcmp (fullname, "UIKit.NSDirectionalEdgeInsets")) { + func = to_managed ? (void *) xamarin_nsvalue_to_nsdirectionaledgeinsets : (void *) xamarin_nsdirectionaledgeinsets_to_nsvalue; #endif #if HAVE_COREANIMATION } else if (!strcmp (fullname, "CoreAnimation.CATransform3D")) { diff --git a/runtime/xamarin/trampolines.h b/runtime/xamarin/trampolines.h index 19c6a680009b..599da7524075 100644 --- a/runtime/xamarin/trampolines.h +++ b/runtime/xamarin/trampolines.h @@ -113,6 +113,7 @@ void *xamarin_nsvalue_to_scnvector3 (NSValue *value, void *ptr, Mono void *xamarin_nsvalue_to_scnvector4 (NSValue *value, void *ptr, MonoClass *managedType, guint32 *exception_ghandle); void *xamarin_nsvalue_to_uiedgeinsets (NSValue *value, void *ptr, MonoClass *managedType, guint32 *exception_ghandle); void *xamarin_nsvalue_to_uioffset (NSValue *value, void *ptr, MonoClass *managedType, guint32 *exception_ghandle); +void *xamarin_nsvalue_to_nsdirectionaledgeinsets(NSValue *value, void *ptr, MonoClass *managedType, guint32 *exception_ghandle); id xamarin_bool_to_nsnumber (MonoObject *value, guint32 *exception_gchandle); id xamarin_sbyte_to_nsnumber (MonoObject *value, guint32 *exception_gchandle); @@ -147,6 +148,7 @@ id xamarin_scnvector3_to_nsvalue (MonoObject *value, guint32 *except id xamarin_scnvector4_to_nsvalue (MonoObject *value, guint32 *exception_gchandle); id xamarin_uiedgeinsets_to_nsvalue (MonoObject *value, guint32 *exception_gchandle); id xamarin_uioffset_to_nsvalue (MonoObject *value, guint32 *exception_gchandle); +id xamarin_nsdirectionaledgeinsets_to_nsvalue(MonoObject *value, guint32 *exception_gchandle); /* Copied from SGen */ diff --git a/src/ObjCRuntime/Registrar.cs b/src/ObjCRuntime/Registrar.cs index ffee3751762d..f6e1d37081a9 100644 --- a/src/ObjCRuntime/Registrar.cs +++ b/src/ObjCRuntime/Registrar.cs @@ -684,6 +684,7 @@ bool IsValidToManagedTypeConversion (TType inputType, TType outputType) case "SceneKit.SCNVector4": case "UIKit.UIEdgeInsets": case "UIKit.UIOffset": + case "UIKit.NSDirectionalEdgeInsets": return true; default: return false; diff --git a/src/generator.cs b/src/generator.cs index 924e35977320..bee1309a402b 100644 --- a/src/generator.cs +++ b/src/generator.cs @@ -1220,6 +1220,7 @@ static Dictionary NSValueCreateMap { if (Frameworks.HaveUIKit) { nsvalue_create_map [TypeManager.UIEdgeInsets] = "UIEdgeInsets"; nsvalue_create_map [TypeManager.UIOffset] = "UIOffset"; + nsvalue_create_map [TypeManager.NSDirectionalEdgeInsets] = "DirectionalEdgeInsets"; } if (TypeManager.MKCoordinateSpan != null) diff --git a/tests/test-libraries/testgenerator.cs b/tests/test-libraries/testgenerator.cs index efd007082e28..6d29d0288d62 100644 --- a/tests/test-libraries/testgenerator.cs +++ b/tests/test-libraries/testgenerator.cs @@ -97,6 +97,7 @@ class BindAsData new BindAsData { Managed = "CMTime", Native = "CMTime", ManagedCondition = "HAVE_COREMEDIA", ManagedNewExpression = "new CMTime (35, 36)", Map = ".CMTimeValue", MapFrom = "FromCMTime" }, new BindAsData { Managed = "CMTimeMapping", Native = "CMTimeMapping", ManagedCondition = "HAVE_COREMEDIA", ManagedNewExpression = "new CMTimeMapping { Source = new CMTimeRange { Duration = new CMTime (42, 43), Start = new CMTime (44, 45) } }", Map = ".CMTimeMappingValue", MapFrom = "FromCMTimeMapping" }, new BindAsData { Managed = "CATransform3D", Native = "CATransform3D", ManagedCondition = "HAVE_COREANIMATION", ManagedNewExpression = "new CATransform3D { m11 = 41 }", Map = ".CATransform3DValue", MapFrom = "FromCATransform3D" }, + new BindAsData { Managed = "NSDirectionalEdgeInsets", Native = "NSDirectionalEdgeInsets", ManagedCondition = "HAVE_UIKIT", ManagedNewExpression = "new NSDirectionalEdgeInsets (42, 43, 44, 45)", Map = ".DirectionalEdgeInsetsValue", MapFrom = "FromDirectionalEdgeInsets", MinXcodeVersion = new Version (9, 0) }, }; static BindAsData [] bindas_nsstring = new [] { diff --git a/tools/common/StaticRegistrar.cs b/tools/common/StaticRegistrar.cs index 582c3ebe1b3e..caf36eaf8016 100644 --- a/tools/common/StaticRegistrar.cs +++ b/tools/common/StaticRegistrar.cs @@ -3793,6 +3793,7 @@ string GetNSValueToManagedFunc (TypeReference managedType, TypeReference inputTy case "SceneKit.SCNVector4": nativeType = "SCNVector4"; return "xamarin_nsvalue_to_scnvector4"; case "UIKit.UIEdgeInsets": nativeType = "UIEdgeInsets"; return "xamarin_nsvalue_to_uiedgeinsets"; case "UIKit.UIOffset": nativeType = "UIOffset"; return "xamarin_nsvalue_to_uioffset"; + case "UIKit.NSDirectionalEdgeInsets": nativeType = "NSDirectionalEdgeInsets"; return "xamarin_nsvalue_to_nsdirectionaledgeinsets"; default: throw ErrorHelper.CreateError (99, $"Internal error: can't convert from '{inputType.FullName}' to '{outputType.FullName}' in {descriptiveMethodName}. Please file a bug report with a test case (https://bugzilla.xamarin.com)."); } @@ -3826,6 +3827,7 @@ string GetManagedToNSValueFunc (TypeReference managedType, TypeReference inputTy case "SceneKit.SCNVector4": return "xamarin_scnvector4_to_nsvalue"; case "UIKit.UIEdgeInsets": return "xamarin_uiedgeinsets_to_nsvalue"; case "UIKit.UIOffset": return "xamarin_uioffset_to_nsvalue"; + case "UIKit.NSDirectionalEdgeInsets": return "xamarin_nsdirectionaledgeinsets_to_nsvalue"; default: throw ErrorHelper.CreateError (99, $"Internal error: can't convert from '{inputType.FullName}' to '{outputType.FullName}' in {descriptiveMethodName}. Please file a bug report with a test case (https://bugzilla.xamarin.com)."); } From 8bc81cc68a46bc8f6d2197d3e7e63df2e9826bf8 Mon Sep 17 00:00:00 2001 From: Rolf Bjarne Kvinge Date: Fri, 22 Sep 2017 12:28:37 +0200 Subject: [PATCH 43/46] [registrar] Don't store stuff in instance variables before knowing if the stuff is correct. --- src/ObjCRuntime/Registrar.cs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/ObjCRuntime/Registrar.cs b/src/ObjCRuntime/Registrar.cs index f6e1d37081a9..4599886de64e 100644 --- a/src/ObjCRuntime/Registrar.cs +++ b/src/ObjCRuntime/Registrar.cs @@ -602,7 +602,9 @@ public TType [] Parameters { public TType [] NativeParameters { get { if (native_parameters == null && Parameters != null) { - native_parameters = new TType [parameters.Length]; + // Put the parameters in a temporary variable, and only store them in the instance field once done, + // so that if an exception occurs, the same exception will be raised the next time too. + var native_parameters = new TType [parameters.Length]; for (int i = 0; i < parameters.Length; i++) { var originalType = Registrar.GetBindAsAttribute (this, i)?.OriginalType; if (originalType != null) { @@ -613,6 +615,7 @@ public TType [] NativeParameters { native_parameters [i] = parameters [i]; } } + this.native_parameters = native_parameters; } return native_parameters; } From b216e74e89d07282ea10b7a570544f7db4442eb2 Mon Sep 17 00:00:00 2001 From: Rolf Bjarne Kvinge Date: Mon, 25 Sep 2017 15:54:04 +0200 Subject: [PATCH 44/46] [linker] Teach 'provider to string' logic about method return types. Prevents these little gems: > warning MT4124: Invalid BindAsAttribute found on 'Mono.Cecil.MethodReturnType': could not find the smart extension type ... --- .../MonoTouch.Tuner/PreserveSmartEnumConversionsSubStep.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tools/linker/MonoTouch.Tuner/PreserveSmartEnumConversionsSubStep.cs b/tools/linker/MonoTouch.Tuner/PreserveSmartEnumConversionsSubStep.cs index 4dde346037db..4bb3f9062db6 100644 --- a/tools/linker/MonoTouch.Tuner/PreserveSmartEnumConversionsSubStep.cs +++ b/tools/linker/MonoTouch.Tuner/PreserveSmartEnumConversionsSubStep.cs @@ -55,6 +55,8 @@ static string ProviderToString (ICustomAttributeProvider provider) { if (provider is MemberReference member) return member.DeclaringType.FullName + "." + member.Name; + if (provider is MethodReturnType returnType) + return ProviderToString ((ICustomAttributeProvider) returnType.Method); return provider.ToString (); } From 0355baa57b3418bf96f0a81e608c7829f9510688 Mon Sep 17 00:00:00 2001 From: Rolf Bjarne Kvinge Date: Mon, 25 Sep 2017 15:55:03 +0200 Subject: [PATCH 45/46] [linker] Filter smart enum conversion to enums, and downgrade warnings to log messages. Only apply smart enum conversion to enums, and downgrade related warnings to log messages. This avoids spurious warnings like this: > warning MT4124: Invalid BindAsAttribute found on 'Bindings.Test.ObjCRegistrarTest.GetBooleanArray': could not find the smart extension type System.BooleanExtensions. Please file a bug report at https://bugzilla.xamarin.com when the BindAs attribute is valid, but just not about a smart enum in the first place. --- .../PreserveSmartEnumConversionsSubStep.cs | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/tools/linker/MonoTouch.Tuner/PreserveSmartEnumConversionsSubStep.cs b/tools/linker/MonoTouch.Tuner/PreserveSmartEnumConversionsSubStep.cs index 4bb3f9062db6..00077ac76c31 100644 --- a/tools/linker/MonoTouch.Tuner/PreserveSmartEnumConversionsSubStep.cs +++ b/tools/linker/MonoTouch.Tuner/PreserveSmartEnumConversionsSubStep.cs @@ -83,6 +83,10 @@ void ProcessAttributeProvider (ICustomAttributeProvider provider, MethodDefiniti continue; } + // We only care about enums, BindAs attributes can be used for other types too. + if (!managedEnumType.IsEnum) + continue; + Tuple pair; if (cache != null && cache.TryGetValue (managedEnumType, out pair)) { Preserve (pair, conditionA, conditionB); @@ -101,7 +105,7 @@ void ProcessAttributeProvider (ICustomAttributeProvider provider, MethodDefiniti break; } if (extensionType == null) { - ErrorHelper.Show (ErrorHelper.CreateWarning (LinkContext.Target.App, 4124, provider, "Invalid BindAsAttribute found on '{0}': could not find the smart extension type {1}.{2}. Please file a bug report at https://bugzilla.xamarin.com", ProviderToString (provider), managedEnumType.Namespace, extensionName)); + Driver.Log (1, $"Could not find a smart extension type for the enum {managedEnumType.FullName} (due to BindAs attribute on {ProviderToString (provider)}): most likely this is because the enum isn't a smart enum."); continue; } @@ -130,12 +134,12 @@ void ProcessAttributeProvider (ICustomAttributeProvider provider, MethodDefiniti } if (getConstant == null) { - ErrorHelper.Show (ErrorHelper.CreateWarning (LinkContext.Target.App, 4124, provider, "Invalid BindAsAttribute found on '{0}': could not find the GetConstant method in the type {1}. Please file a bug report at https://bugzilla.xamarin.com", ProviderToString (provider), extensionType.FullName)); + Driver.Log (1, $"Could not find the GetConstant method on the supposedly smart extension type {extensionType.FullName} for the enum {managedEnumType.FullName} (due to BindAs attribute on {ProviderToString (provider)}): most likely this is because the enum isn't a smart enum."); continue; } if (getValue == null) { - ErrorHelper.Show (ErrorHelper.CreateWarning (LinkContext.Target.App, 4124, provider, "Invalid BindAsAttribute found on '{0}': could not find the GetValue method in the type {1}. Please file a bug report at https://bugzilla.xamarin.com", ProviderToString (provider), extensionType.FullName)); + Driver.Log (1, $"Could not find the GetValue method on the supposedly smart extension type {extensionType.FullName} for the enum {managedEnumType.FullName} (due to BindAs attribute on {ProviderToString (provider)}): most likely this is because the enum isn't a smart enum."); continue; } From 78e2a483b0b4051857622dce31e59639a3811c00 Mon Sep 17 00:00:00 2001 From: Rolf Bjarne Kvinge Date: Mon, 25 Sep 2017 16:38:21 +0200 Subject: [PATCH 46/46] [docs] Document how to add support for new BindAs types. --- docs/bindas.md | 54 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 54 insertions(+) create mode 100644 docs/bindas.md diff --git a/docs/bindas.md b/docs/bindas.md new file mode 100644 index 000000000000..44e6b187b046 --- /dev/null +++ b/docs/bindas.md @@ -0,0 +1,54 @@ +# How to add BindAs support for new types + +Currently the BindAs code only supports converting between a certain set of +types and NSValue, NSNumber and smart enums. + +If your BindAs support does not involve NSValue or NSNumber, then the required +changes are a bit more extensive than I explain here (but this is still a good +starting point). + +[Sample code][8] + +The sample code is to support a new type for NSValue, the exact code locations will differ slightly for NSNumber (different switches, etc). + +1. Add a test (or three) + + * Add an entry to [tests/test-libraries/testgenerator.cs][1] for the new type. + testgenerator.cs will generate the code required to test your new BindAs + support for all known scenarios. + + * Any other manual tests should go in monotouch-test. + +2. Add native conversions functions to runtime/trampolines.m|h. In the sample + code this is the two functions to convert between NSValue and + NSDirectionalEdgeInsets: + + `xamarin_nsdirectionaledgeinsets_to_nsvalue`: [trampolines.h#151][2], [trampolines.m#889][3] + `xamarin_nsvalue_to_nsdirectionaledgeinsets`: [trampolines.h#116][4], [trampolines.m#799][5] + +3. Add a switch entry to [trampolines.m#1007][6] to use the two new conversion functions. + +4. The registrar also needs to know ([Registrar.cs#687][7]). + +5. And the static registrar needs to know too, so that it can call the right native conversion function ([StaticRegistrar.cs#3796][9], [StaticRegistrar.cs#3830][10]). + +6. Now there's just the generator support left ([generator.cs#1223][11], [generator.cs#1369][12]). + +7. Finally run the following tests (at least) + +* All variations of monotouch-test (iOS/watchOS/tvOS) on both simulator and device. +* link all on both simulator and device. + + +[1]: https://github.com/rolfbjarne/xamarin-macios/blob/b38c114fbe8c9d229ec41a312dc36802cb4f027e/tests/test-libraries/testgenerator.cs#L100 +[2]: https://github.com/rolfbjarne/xamarin-macios/blob/b38c114fbe8c9d229ec41a312dc36802cb4f027e/runtime/xamarin/trampolines.h#L151 +[3]: https://github.com/rolfbjarne/xamarin-macios/blob/b38c114fbe8c9d229ec41a312dc36802cb4f027e/runtime/trampolines.m#L889 +[4]: https://github.com/rolfbjarne/xamarin-macios/blob/b38c114fbe8c9d229ec41a312dc36802cb4f027e/runtime/xamarin/trampolines.h#L116 +[5]: https://github.com/rolfbjarne/xamarin-macios/blob/b38c114fbe8c9d229ec41a312dc36802cb4f027e/runtime/trampolines.m#L799 +[6]: https://github.com/rolfbjarne/xamarin-macios/blob/b38c114fbe8c9d229ec41a312dc36802cb4f027e/runtime/trampolines.m#L1007-L1008 +[7]: https://github.com/rolfbjarne/xamarin-macios/blob/b38c114fbe8c9d229ec41a312dc36802cb4f027e/src/ObjCRuntime/Registrar.cs#L687 +[8]: https://github.com/xamarin/xamarin-macios/pull/2288/commits/b38c114fbe8c9d229ec41a312dc36802cb4f027e +[9]: https://github.com/rolfbjarne/xamarin-macios/blob/b38c114fbe8c9d229ec41a312dc36802cb4f027e/tools/common/StaticRegistrar.cs#L3796 +[10]: https://github.com/rolfbjarne/xamarin-macios/blob/b38c114fbe8c9d229ec41a312dc36802cb4f027e/tools/common/StaticRegistrar.cs#L3830 +[11]: https://github.com/rolfbjarne/xamarin-macios/blob/b38c114fbe8c9d229ec41a312dc36802cb4f027e/src/generator.cs#L1223 +[12]: https://github.com/rolfbjarne/xamarin-macios/blob/b38c114fbe8c9d229ec41a312dc36802cb4f027e/src/generator.cs#L1369