From c6943139c43192a735e70ead9a72adb06b35fba8 Mon Sep 17 00:00:00 2001 From: Jonathan Pobst Date: Tue, 28 Feb 2023 10:54:34 -1000 Subject: [PATCH 1/4] [generator] Allow 'managedOverride' metadata to support 'abstract' methods. --- .../Models/MethodWriter.cs | 3 ++- .../Unit-Tests/CodeGeneratorTests.cs | 24 +++++++++++++++++++ .../BoundMethodAbstractDeclaration.cs | 3 +++ 3 files changed, 29 insertions(+), 1 deletion(-) 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 6e8e68c08..782857152 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 () { 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); } From 1dd91b5189e17a69c76bdceca7899735d08dfca7 Mon Sep 17 00:00:00 2001 From: Jonathan Pobst Date: Thu, 2 Mar 2023 10:32:29 -1000 Subject: [PATCH 2/4] [generator] Add support for 'skipInvokerMethods' metadata. --- .../Unit-Tests/CodeGeneratorTests.cs | 30 +++++++++++++++++++ .../XmlApiImporter.cs | 4 +++ .../ClassGen.cs | 3 ++ .../SourceWriters/ClassInvokerClass.cs | 15 ++++++---- 4 files changed, 46 insertions(+), 6 deletions(-) diff --git a/tests/generator-Tests/Unit-Tests/CodeGeneratorTests.cs b/tests/generator-Tests/Unit-Tests/CodeGeneratorTests.cs index 782857152..ac238514a 100644 --- a/tests/generator-Tests/Unit-Tests/CodeGeneratorTests.cs +++ b/tests/generator-Tests/Unit-Tests/CodeGeneratorTests.cs @@ -322,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 e3f2e3850..949ddf1c4 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 [] { ',' })) + 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 6d0e96705..ffb419a6d 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 (); @@ -355,6 +356,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/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)) From 9fbfbef09ae1675289843c5f06df73ea4473700e Mon Sep 17 00:00:00 2001 From: Jonathan Pobst Date: Thu, 2 Mar 2023 13:44:06 -1000 Subject: [PATCH 3/4] Allow space as a separator for 'skipInvokerMethods'. --- .../Java.Interop.Tools.Generator.Importers/XmlApiImporter.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/generator/Java.Interop.Tools.Generator.Importers/XmlApiImporter.cs b/tools/generator/Java.Interop.Tools.Generator.Importers/XmlApiImporter.cs index 949ddf1c4..e23632242 100644 --- a/tools/generator/Java.Interop.Tools.Generator.Importers/XmlApiImporter.cs +++ b/tools/generator/Java.Interop.Tools.Generator.Importers/XmlApiImporter.cs @@ -117,7 +117,7 @@ public static ClassGen CreateClass (XElement pkg, XElement elem, CodeGenerationO }; if (elem.Attribute ("skipInvokerMethods")?.Value is string skip) - foreach (var m in skip.Split (new char [] { ',' })) + foreach (var m in skip.Split (new char [] { ',', ' ' })) klass.SkippedInvokerMethods.Add (m); FillApiSince (klass, pkg, elem); From dc7a7655853367e56812f8b6d7ce18688e80284e Mon Sep 17 00:00:00 2001 From: Jonathan Pobst Date: Mon, 6 Mar 2023 10:15:41 -1000 Subject: [PATCH 4/4] Allow newline delimited attribute. --- .../Java.Interop.Tools.Generator.Importers/XmlApiImporter.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/generator/Java.Interop.Tools.Generator.Importers/XmlApiImporter.cs b/tools/generator/Java.Interop.Tools.Generator.Importers/XmlApiImporter.cs index e23632242..5a3b0563b 100644 --- a/tools/generator/Java.Interop.Tools.Generator.Importers/XmlApiImporter.cs +++ b/tools/generator/Java.Interop.Tools.Generator.Importers/XmlApiImporter.cs @@ -117,7 +117,7 @@ public static ClassGen CreateClass (XElement pkg, XElement elem, CodeGenerationO }; if (elem.Attribute ("skipInvokerMethods")?.Value is string skip) - foreach (var m in skip.Split (new char [] { ',', ' ' })) + foreach (var m in skip.Split (new char [] { ',', ' ', '\n', '\r' }, StringSplitOptions.RemoveEmptyEntries)) klass.SkippedInvokerMethods.Add (m); FillApiSince (klass, pkg, elem);