diff --git a/src/Xamarin.SourceWriter/Models/MethodWriter.cs b/src/Xamarin.SourceWriter/Models/MethodWriter.cs index 61e4587a4..f826b919b 100644 --- a/src/Xamarin.SourceWriter/Models/MethodWriter.cs +++ b/src/Xamarin.SourceWriter/Models/MethodWriter.cs @@ -88,7 +88,8 @@ public virtual void WriteSignature (CodeWriter writer) if (IsOverride) writer.Write ("override "); - else if (IsVirtual) + + if (IsVirtual) writer.Write ("virtual "); else if (IsAbstract) writer.Write ("abstract "); diff --git a/tests/generator-Tests/Unit-Tests/CodeGeneratorTests.cs b/tests/generator-Tests/Unit-Tests/CodeGeneratorTests.cs index bbb9724c2..b85c4d4fe 100644 --- a/tests/generator-Tests/Unit-Tests/CodeGeneratorTests.cs +++ b/tests/generator-Tests/Unit-Tests/CodeGeneratorTests.cs @@ -150,6 +150,30 @@ public void ManagedOverrideMethod_Override () Assert.True (writer.ToString ().Contains ("public override unsafe int DoStuff ()"), $"was: `{writer.ToString ()}`"); } + [Test] + public void ManagedOverrideAbstractMethod_Override () + { + var xml = @" + + + + + + + + + "; + + var gens = ParseApiDefinition (xml); + var klass = gens.Single (g => g.Name == "MyClass"); + + generator.Context.ContextTypes.Push (klass); + generator.WriteType (klass, string.Empty, new GenerationInfo ("", "", "MyAssembly")); + generator.Context.ContextTypes.Pop (); + + Assert.True (writer.ToString ().Contains ("public override abstract int DoStuff ();"), $"was: `{writer}`"); + } + [Test] public void ManagedOverrideMethod_None () { @@ -298,6 +322,36 @@ public void ManagedOverrideInterfaceProperty_Reabstract () Assert.True (writer.ToString ().Contains ("abstract int Name {"), $"was: `{writer}`"); } + [Test] + public void SkipInvokerMethodsMetadata () + { + var xml = @" + + + + + + + + + + + + "; + + var gens = ParseApiDefinition (xml); + var klass = gens.Single (g => g.Name == "MyClass"); + + generator.Context.ContextTypes.Push (klass); + generator.WriteType (klass, string.Empty, new GenerationInfo ("", "", "MyAssembly")); + generator.Context.ContextTypes.Pop (); + + // `override abstract` causes both invoker methods to get generated. We use metadata + // to suppress the base class's method to prevent a conflict. + Assert.True (writer.ToString ().Contains ("public override abstract Com.Xamarin.Android.MyClass DoStuff ();"), $"was: `{writer}`"); + Assert.False (writer.ToString ().Contains ("public abstract Com.Xamarin.Android.MyBaseClass DoStuff ();"), $"was: `{writer}`"); + } + [Test] public void WriteDuplicateInterfaceEventArgs () { diff --git a/tools/generator/Java.Interop.Tools.Generator.Importers/XmlApiImporter.cs b/tools/generator/Java.Interop.Tools.Generator.Importers/XmlApiImporter.cs index 2e773df60..39b882d03 100644 --- a/tools/generator/Java.Interop.Tools.Generator.Importers/XmlApiImporter.cs +++ b/tools/generator/Java.Interop.Tools.Generator.Importers/XmlApiImporter.cs @@ -116,6 +116,10 @@ public static ClassGen CreateClass (XElement pkg, XElement elem, CodeGenerationO !options.SupportNestedInterfaceTypes }; + if (elem.Attribute ("skipInvokerMethods")?.Value is string skip) + foreach (var m in skip.Split (new char [] { ',', ' ', '\n', '\r' }, StringSplitOptions.RemoveEmptyEntries)) + klass.SkippedInvokerMethods.Add (m); + FillApiSince (klass, pkg, elem); SetLineInfo (klass, elem, options); diff --git a/tools/generator/Java.Interop.Tools.Generator.ObjectModel/ClassGen.cs b/tools/generator/Java.Interop.Tools.Generator.ObjectModel/ClassGen.cs index 30ce30b9e..b1da186e6 100644 --- a/tools/generator/Java.Interop.Tools.Generator.ObjectModel/ClassGen.cs +++ b/tools/generator/Java.Interop.Tools.Generator.ObjectModel/ClassGen.cs @@ -11,6 +11,7 @@ namespace MonoDroid.Generation public class ClassGen : GenBase { bool fill_explicit_implementation_started; + HashSet skipped_invoker_methods; public List Ctors { get; private set; } = new List (); @@ -339,6 +340,8 @@ public override void ResetValidation () base.ResetValidation (); } + public HashSet SkippedInvokerMethods => skipped_invoker_methods ??= new HashSet (); + public override string ToNative (CodeGenerationOptions opt, string varname, Dictionary mappings = null) { if (opt.CodeGenerationTarget == CodeGenerationTarget.JavaInterop1) { diff --git a/tools/generator/SourceWriters/BoundMethodAbstractDeclaration.cs b/tools/generator/SourceWriters/BoundMethodAbstractDeclaration.cs index d76b4e90d..13ae7cb04 100644 --- a/tools/generator/SourceWriters/BoundMethodAbstractDeclaration.cs +++ b/tools/generator/SourceWriters/BoundMethodAbstractDeclaration.cs @@ -44,6 +44,9 @@ public BoundMethodAbstractDeclaration (GenBase gen, Method method, CodeGeneratio NewFirst = true; + if (method.ManagedOverride?.ToLowerInvariant () == "override") + IsOverride = true; + if (opt.CodeGenerationTarget != CodeGenerationTarget.JavaInterop1) { method_callback = new MethodCallback (impl, method, opt, null, method.IsReturnCharSequence); } diff --git a/tools/generator/SourceWriters/ClassInvokerClass.cs b/tools/generator/SourceWriters/ClassInvokerClass.cs index 84fc75964..508730701 100644 --- a/tools/generator/SourceWriters/ClassInvokerClass.cs +++ b/tools/generator/SourceWriters/ClassInvokerClass.cs @@ -64,21 +64,21 @@ public ClassInvokerClass (ClassGen klass, CodeGenerationOptions opt) Properties.Add (new ThresholdTypeGetter ()); } - AddMemberInvokers (klass, opt, new HashSet ()); + AddMemberInvokers (klass, opt, new HashSet (), klass.SkippedInvokerMethods); } - void AddMemberInvokers (ClassGen klass, CodeGenerationOptions opt, HashSet members) + void AddMemberInvokers (ClassGen klass, CodeGenerationOptions opt, HashSet members, HashSet skipInvokers) { AddPropertyInvokers (klass, klass.Properties, members, opt); - AddMethodInvokers (klass, klass.Methods, members, null, opt); + AddMethodInvokers (klass, klass.Methods, members, skipInvokers, null, opt); foreach (var iface in klass.GetAllDerivedInterfaces ()) { AddPropertyInvokers (klass, iface.Properties.Where (p => !klass.ContainsProperty (p.Name, false, false)), members, opt); - AddMethodInvokers (klass, iface.Methods.Where (m => (opt.SupportDefaultInterfaceMethods || !m.IsInterfaceDefaultMethod) && !klass.ContainsMethod (m, false, false) && !klass.IsCovariantMethod (m) && !klass.ExplicitlyImplementedInterfaceMethods.Contains (m.GetSignature ())), members, iface, opt); + AddMethodInvokers (klass, iface.Methods.Where (m => (opt.SupportDefaultInterfaceMethods || !m.IsInterfaceDefaultMethod) && !klass.ContainsMethod (m, false, false) && !klass.IsCovariantMethod (m) && !klass.ExplicitlyImplementedInterfaceMethods.Contains (m.GetSignature ())), members, skipInvokers, iface, opt); } if (klass.BaseGen != null && klass.BaseGen.FullName != "Java.Lang.Object") - AddMemberInvokers (klass.BaseGen, opt, members); + AddMemberInvokers (klass.BaseGen, opt, members, skipInvokers); } void AddPropertyInvokers (ClassGen klass, IEnumerable properties, HashSet members, CodeGenerationOptions opt) @@ -100,9 +100,12 @@ void AddPropertyInvokers (ClassGen klass, IEnumerable properties, Hash } } - void AddMethodInvokers (ClassGen klass, IEnumerable methods, HashSet members, InterfaceGen gen, CodeGenerationOptions opt) + void AddMethodInvokers (ClassGen klass, IEnumerable methods, HashSet members, HashSet skipInvokers, InterfaceGen gen, CodeGenerationOptions opt) { foreach (var m in methods) { + if (skipInvokers.Contains ($"{m.DeclaringType.RawJniName}.{m.JavaName}{m.JniSignature}")) + continue; + var sig = m.GetSignature (); if (members.Contains (sig))