diff --git a/docs/website/binding_types_reference_guide.md b/docs/website/binding_types_reference_guide.md
index 28bc5eecc1..91b72f6612 100644
--- a/docs/website/binding_types_reference_guide.md
+++ b/docs/website/binding_types_reference_guide.md
@@ -1900,8 +1900,6 @@ var strongDemo = new Demo ();
demo.Delegate = new MyDelegate ();
```
-
-
Another use of the `Wrap` attribute is to support strongly typed version
of methods. For example:
@@ -1937,6 +1935,42 @@ interface FooExplorer {
}
```
+`[Wrap]` can also be used directly in property getters and setters,
+this allows to have full control on them and adjust the code the way you need,
+for example using smart enums, consider the following API definition:
+
+```csharp
+// Smart enum.
+enum PersonRelationship {
+ [Field (null)]
+ None,
+
+ [Field ("FMFather", "__Internal")]
+ Father,
+
+ [Field ("FMMother", "__Internal")]
+ Mother
+}
+```
+
+Interface definition:
+
+```
+// Property definition.
+
+ [Export ("presenceType")]
+ NSString _PresenceType { get; set; }
+
+ PersonRelationship PresenceType {
+ [Wrap ("PersonRelationshipExtensions.GetValue (_PresenceType)")]
+ get;
+ [Wrap ("_PresenceType = value.GetConstant ()")]
+ set;
+ }
+```
+
+
+
# Parameter Attributes
This section describes the attributes that you can apply to the parameters in
diff --git a/docs/website/generator-errors.md b/docs/website/generator-errors.md
index 0b95a9f08e..1fa378b47e 100644
--- a/docs/website/generator-errors.md
+++ b/docs/website/generator-errors.md
@@ -176,6 +176,8 @@ This usually indicates a bug in Xamarin.iOS/Xamarin.Mac; please [file a bug repo
### BI1062: The member '*' contains ref/out parameters and must not be decorated with [Async].
+### BI1063: The 'WrapAttribute' can only be used at the property or at getter/setter level at a given time. Property: '*'.
+
# BI11xx: warnings
diff --git a/src/generator-attribute-manager.cs b/src/generator-attribute-manager.cs
index 383943ac1d..602ec4b763 100644
--- a/src/generator-attribute-manager.cs
+++ b/src/generator-attribute-manager.cs
@@ -283,6 +283,8 @@ public static class AttributeManager
public static T GetCustomAttribute (ICustomAttributeProvider provider) where T : System.Attribute
{
+ if (provider is null)
+ return null;
var rv = GetCustomAttributes (provider);
if (rv == null || rv.Length == 0)
return null;
diff --git a/src/generator.cs b/src/generator.cs
index 6a6bb84b8e..44804af460 100644
--- a/src/generator.cs
+++ b/src/generator.cs
@@ -426,13 +426,30 @@ public interface IMemberGatherer {
IEnumerable GetTypeContractMethods (Type source);
}
+class WrapPropMemberInformation
+{
+ public bool HasWrapOnGetter { get => WrapGetter != null; }
+ public bool HasWrapOnSetter { get => WrapSetter != null; }
+ public string WrapGetter { get; private set; }
+ public string WrapSetter { get; private set; }
+
+ public WrapPropMemberInformation (PropertyInfo pi)
+ {
+ WrapGetter = AttributeManager.GetCustomAttribute (pi?.GetMethod)?.MethodName;
+ WrapSetter = AttributeManager.GetCustomAttribute (pi?.SetMethod)?.MethodName;
+ }
+}
+
+
public class MemberInformation
{
public readonly MemberInfo mi;
public readonly Type type;
public readonly Type category_extension_type;
+ internal readonly WrapPropMemberInformation wpmi;
public readonly bool is_abstract, is_protected, is_internal, is_unified_internal, is_override, is_new, is_sealed, is_static, is_thread_static, is_autorelease, is_wrapper, is_forced;
public readonly bool is_type_sealed, ignore_category_static_warnings, is_basewrapper_protocol_method;
+ public readonly bool has_inner_wrap_attribute;
public readonly Generator.ThreadCheck threadCheck;
public bool is_unsafe, is_virtual_method, is_export, is_category_extension, is_variadic, is_interface_impl, is_extension_method, is_appearance, is_model, is_ctor;
public bool is_return_release;
@@ -572,6 +589,17 @@ public class MemberInformation
is_virtual_method = false;
else
is_virtual_method = !is_static;
+
+ // Properties can have WrapAttribute on getter/setter so we need to check for this
+ // but only if no Export is already found on property level.
+ if (export is null) {
+ wpmi = new WrapPropMemberInformation (pi);
+ has_inner_wrap_attribute = wpmi.HasWrapOnGetter || wpmi.HasWrapOnSetter;
+
+ // Wrap can only be used either at property level or getter/setter level at a given time.
+ if (wrap_method != null && has_inner_wrap_attribute)
+ throw new BindingException (1063, true, $"The 'WrapAttribute' can only be used at the property or at getter/setter level at a given time. Property: '{pi.DeclaringType}.{pi.Name}'");
+ }
}
public string GetVisibility ()
@@ -2264,7 +2292,10 @@ public partial class Generator : IMemberGatherer {
if (AttributeManager.HasAttribute (pi))
continue;
-
+
+ if (AttributeManager.HasAttribute (pi.GetGetMethod ()) || AttributeManager.HasAttribute (pi.GetSetMethod ()))
+ continue;
+
throw new BindingException (1018, true, "No [Export] attribute on property {0}.{1}", t.FullName, pi.Name);
}
if (AttributeManager.HasAttribute (pi))
@@ -4653,6 +4684,39 @@ public partial class Generator : IMemberGatherer {
indent--;
print ("}\n");
return;
+ } else if (minfo.has_inner_wrap_attribute) {
+ // If property getter or setter has its own WrapAttribute we let the user do whatever their heart desires
+ if (pi.CanRead) {
+ PrintAttributes (pi, platform: true);
+ PrintAttributes (pi.GetGetMethod (), platform: true, preserve: true, advice: true);
+ print ("get {");
+ indent++;
+
+ print ($"return {minfo.wpmi.WrapGetter};");
+
+ indent--;
+ print ("}");
+ }
+ if (pi.CanWrite) {
+ var setter = pi.GetSetMethod ();
+ var not_implemented_attr = AttributeManager.GetCustomAttribute (setter);
+
+ PrintAttributes (pi, platform: true);
+ PrintAttributes (setter, platform: true, preserve: true, advice: true, notImplemented: true);
+ print ("set {");
+ indent++;
+
+ if (not_implemented_attr != null)
+ print ("throw new NotImplementedException ({0});", not_implemented_attr.Message == null ? "" : $@"""{not_implemented_attr.Message}""");
+ else
+ print ($"{minfo.wpmi.WrapSetter};");
+
+ indent--;
+ print ("}");
+ }
+ indent--;
+ print ("}\n");
+ return;
}
if (pi.CanRead){
diff --git a/tests/generator/BGenTests.cs b/tests/generator/BGenTests.cs
index 593c7a1fbd..8fafe61357 100644
--- a/tests/generator/BGenTests.cs
+++ b/tests/generator/BGenTests.cs
@@ -522,15 +522,24 @@ namespace GeneratorTests
[Test]
public void Bug57531 () => BuildFile (Profile.iOS, "bug57531.cs");
+ [Test]
+ public void Bug57870 () => BuildFile (Profile.iOS, true, true, "bug57870.cs");
+
BGenTool BuildFile (Profile profile, params string [] filenames)
{
- return BuildFile (profile, true, filenames);
+ return BuildFile (profile, true, false, filenames);
}
BGenTool BuildFile (Profile profile, bool nowarnings, params string [] filenames)
+ {
+ return BuildFile (profile, nowarnings, false, filenames);
+ }
+
+ BGenTool BuildFile (Profile profile, bool nowarnings, bool processEnums, params string [] filenames)
{
var bgen = new BGenTool ();
bgen.Profile = profile;
+ bgen.ProcessEnums = processEnums;
bgen.Defines = BGenTool.GetDefaultDefines (bgen.Profile);
bgen.CreateTemporaryBinding (filenames.Select ((filename) => File.ReadAllText (Path.Combine (Configuration.SourceRoot, "tests", "generator", filename))).ToArray ());
bgen.AssertExecute ("build");
diff --git a/tests/generator/ErrorTests.cs b/tests/generator/ErrorTests.cs
index 091df65e3c..7daa1ddddd 100644
--- a/tests/generator/ErrorTests.cs
+++ b/tests/generator/ErrorTests.cs
@@ -535,6 +535,49 @@ namespace BI1062Tests {
bgen.AssertError (1062, "The member 'FooObject.FooMethod' contains ref/out parameters and must not be decorated with [Async].");
}
+ [Test]
+ public void BI1063_NoDoubleWrapTest ()
+ {
+ var bgen = new BGenTool {
+ Profile = Profile.iOS,
+ ProcessEnums = true
+ };
+ bgen.CreateTemporaryBinding (@"
+using System;
+using Foundation;
+
+namespace BI1063Tests {
+
+ enum PersonRelationship {
+ [Field (null)]
+ None,
+
+ [Field (""INPersonRelationshipFather"", ""__Internal"")]
+ Father,
+
+ [Field (""INPersonRelationshipMother"", ""__Internal"")]
+ Mother
+ }
+
+ [BaseType (typeof (NSObject))]
+ interface Wrappers {
+
+ // SmartEnum -- Normal Wrap getter Property
+
+ [Export (""presenceType"")]
+ NSString _PresenceType { get; }
+
+ [Wrap (""PersonRelationshipExtensions.GetValue (_PresenceType)"")]
+ PersonRelationship PresenceType {
+ [Wrap (""PersonRelationshipExtensions.GetValue (_PresenceType)"")]
+ get;
+ }
+ }
+}");
+ bgen.AssertExecuteError ("build");
+ bgen.AssertError (1063, "The 'WrapAttribute' can only be used at the property or at getter/setter level at a given time. Property: 'BI1063Tests.Wrappers.PresenceType'");
+ }
+
[Test]
[TestCase (Profile.iOS)]
[TestCase (Profile.macOSFull)]
diff --git a/tests/generator/bug57870.cs b/tests/generator/bug57870.cs
new file mode 100644
index 0000000000..191c8f61d7
--- /dev/null
+++ b/tests/generator/bug57870.cs
@@ -0,0 +1,62 @@
+using System;
+using Foundation;
+
+namespace Bug57870 {
+
+ enum PersonRelationship {
+ [Field (null)]
+ None,
+
+ [Field ("INPersonRelationshipFather", "__Internal")]
+ Father,
+
+ [Field ("INPersonRelationshipMother", "__Internal")]
+ Mother
+ }
+
+ [BaseType (typeof (NSObject))]
+ interface Wrappers {
+
+ // SmartEnum -- Normal Wrap getter Property
+
+ [Export ("presenceType")]
+ NSString _PresenceType { get; }
+
+ [Wrap ("PersonRelationshipExtensions.GetValue (_PresenceType)")]
+ PersonRelationship PresenceType { get; }
+
+ // SmartEnum -- getter Wrap + NotImplemented setter
+
+ [Export ("presenceType2")]
+ NSString _PresenceType2 { get; [NotImplemented] set; }
+
+ PersonRelationship PresenceType2 {
+ [Wrap ("PersonRelationshipExtensions.GetValue (_PresenceType2)")]
+ get;
+ [NotImplemented ("Nope nope nope")]
+ set;
+ }
+
+ // SmartEnum -- getter Wrap Only
+
+ [Export ("presenceType3")]
+ NSString _PresenceType3 { get; }
+
+ PersonRelationship PresenceType3 {
+ [Wrap ("PersonRelationshipExtensions.GetValue (_PresenceType3)")]
+ get;
+ }
+
+ // SmartEnum -- Wrap getter and setter
+
+ [Export ("presenceType4")]
+ NSString _PresenceType4 { get; set; }
+
+ PersonRelationship PresenceType4 {
+ [Wrap ("PersonRelationshipExtensions.GetValue (_PresenceType4)")]
+ get;
+ [Wrap ("_PresenceType4 = value.GetConstant ()")]
+ set;
+ }
+ }
+}