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))