From 70bd904936ea0f3ddabe000ccab06eeb02826f6b Mon Sep 17 00:00:00 2001 From: Jonathan Pobst Date: Mon, 9 Dec 2019 13:48:12 -0600 Subject: [PATCH] [generator] Mark Kotlin-mangled methods as non-virtual (#534) --- .../Unit-Tests/KotlinFixupsTests.cs | 39 ++++++++++++++++ .../Unit-Tests/XmlApiImporterTests.cs | 18 -------- tools/generator/CodeGenerator.cs | 4 ++ .../XmlApiImporter.cs | 21 +-------- .../MethodBase.cs | 18 ++++++++ .../KotlinFixups.cs | 46 +++++++++++++++++++ tools/generator/generator.csproj | 1 + 7 files changed, 110 insertions(+), 37 deletions(-) create mode 100644 tests/generator-Tests/Unit-Tests/KotlinFixupsTests.cs create mode 100644 tools/generator/Java.Interop.Tools.Generator.Transformation/KotlinFixups.cs diff --git a/tests/generator-Tests/Unit-Tests/KotlinFixupsTests.cs b/tests/generator-Tests/Unit-Tests/KotlinFixupsTests.cs new file mode 100644 index 000000000..1e4c90081 --- /dev/null +++ b/tests/generator-Tests/Unit-Tests/KotlinFixupsTests.cs @@ -0,0 +1,39 @@ +using System; +using System.Linq; +using System.Xml.Linq; +using Java.Interop.Tools.Generator.Transformation; +using MonoDroid.Generation; +using NUnit.Framework; + +namespace generatortests +{ + [TestFixture] + public class KotlinFixupsTests + { + [Test] + public void CreateMethod_EnsureKotlinImplFix () + { + var xml = XDocument.Parse (""); + var klass = XmlApiImporter.CreateClass (xml.Root, xml.Root.Element ("class")); + + KotlinFixups.Fixup (new [] { (GenBase)klass }.ToList ()); + + Assert.AreEqual ("Add", klass.Methods [0].Name); + Assert.IsTrue (klass.Methods [0].IsFinal); + Assert.IsFalse (klass.Methods [0].IsVirtual); + } + + [Test] + public void CreateMethod_EnsureKotlinHashcodeFix () + { + var xml = XDocument.Parse (""); + var klass = XmlApiImporter.CreateClass (xml.Root, xml.Root.Element ("class")); + + KotlinFixups.Fixup (new [] { (GenBase) klass }.ToList ()); + + Assert.AreEqual ("Add", klass.Methods [0].Name); + Assert.IsTrue (klass.Methods [0].IsFinal); + Assert.IsFalse (klass.Methods [0].IsVirtual); + } + } +} diff --git a/tests/generator-Tests/Unit-Tests/XmlApiImporterTests.cs b/tests/generator-Tests/Unit-Tests/XmlApiImporterTests.cs index a7681be26..c8161224a 100644 --- a/tests/generator-Tests/Unit-Tests/XmlApiImporterTests.cs +++ b/tests/generator-Tests/Unit-Tests/XmlApiImporterTests.cs @@ -89,24 +89,6 @@ public void CreateMethod_EnsureValidNameHyphen () Assert.AreEqual ("_3", klass.Methods [0].Name); } - [Test] - public void CreateMethod_EnsureKotlinImplFix () - { - var xml = XDocument.Parse (""); - var klass = XmlApiImporter.CreateClass (xml.Root, xml.Root.Element ("class")); - - Assert.AreEqual ("Add", klass.Methods [0].Name); - } - - [Test] - public void CreateMethod_EnsureKotlinHashcodeFix () - { - var xml = XDocument.Parse (""); - var klass = XmlApiImporter.CreateClass (xml.Root, xml.Root.Element ("class")); - - Assert.AreEqual ("Add", klass.Methods [0].Name); - } - [Test] public void CreateParameter_EnsureValidName () { diff --git a/tools/generator/CodeGenerator.cs b/tools/generator/CodeGenerator.cs index 65b795f60..11d3f5409 100644 --- a/tools/generator/CodeGenerator.cs +++ b/tools/generator/CodeGenerator.cs @@ -12,6 +12,7 @@ using Java.Interop.Tools.Diagnostics; using Java.Interop.Tools.TypeNameMappings; using MonoDroid.Generation.Utilities; +using Java.Interop.Tools.Generator.Transformation; namespace Xamarin.Android.Binder { @@ -149,6 +150,9 @@ static void Run (CodeGeneratorOptions options, DirectoryAssemblyResolver resolve AddTypeToTable (opt, gen); } + // Apply fixups + KotlinFixups.Fixup (gens); + Validate (gens, opt, new CodeGeneratorContext ()); if (api_versions_xml != null) diff --git a/tools/generator/Java.Interop.Tools.Generator.Importers/XmlApiImporter.cs b/tools/generator/Java.Interop.Tools.Generator.Importers/XmlApiImporter.cs index cac352a55..7e4609645 100644 --- a/tools/generator/Java.Interop.Tools.Generator.Importers/XmlApiImporter.cs +++ b/tools/generator/Java.Interop.Tools.Generator.Importers/XmlApiImporter.cs @@ -245,25 +245,8 @@ public static Method CreateMethod (GenBase declaringType, XElement elem) if (elem.Attribute ("managedName") != null) method.Name = elem.XGetAttribute ("managedName"); - else { - var name = method.JavaName; - - // Kotlin generates methods that cannot be referenced in Java, - // like `add-impl` and `add-V5j3Lk8`. We mangle them back into - // something a user would expect by truncating anything after the hyphen. - var index = name.IndexOf ("-impl"); - - if (index >= 0) - name = name.Substring (0, index); - - index = name.IndexOf ('-'); - - // `add-V5j3Lk8` is always a 7 character hashcode - if (index >= 0 && name.Length - index == 8) - name = name.Substring (0, index); - - method.Name = StringRocks.MemberToPascalCase (name); - } + else + method.Name = StringRocks.MemberToPascalCase (method.JavaName); if (method.IsReturnEnumified) { method.ManagedReturn = elem.XGetAttribute ("enumReturn"); diff --git a/tools/generator/Java.Interop.Tools.Generator.ObjectModel/MethodBase.cs b/tools/generator/Java.Interop.Tools.Generator.ObjectModel/MethodBase.cs index d77cccc67..4a738d501 100644 --- a/tools/generator/Java.Interop.Tools.Generator.ObjectModel/MethodBase.cs +++ b/tools/generator/Java.Interop.Tools.Generator.ObjectModel/MethodBase.cs @@ -79,6 +79,24 @@ public string GetSignature (CodeGenerationOptions opt) public virtual bool IsGeneric => Parameters.HasGeneric; + public bool IsKotlinNameMangled { + get { + // Kotlin generates methods that cannot be referenced in Java, + // like `add-impl` and `add-V5j3Lk8`. We will need to fix those later. + if (this is Method method) { + if (method.JavaName.IndexOf ("-impl") >= 0) + return true; + + var index = method.JavaName.IndexOf ('-'); + + // `add-V5j3Lk8` is always a 7 character hashcode + return index >= 0 && method.JavaName.Length - index == 8; + } + + return false; + } + } + public virtual bool Matches (MethodBase other) { if (Name != other.Name) diff --git a/tools/generator/Java.Interop.Tools.Generator.Transformation/KotlinFixups.cs b/tools/generator/Java.Interop.Tools.Generator.Transformation/KotlinFixups.cs new file mode 100644 index 000000000..fc901fc5c --- /dev/null +++ b/tools/generator/Java.Interop.Tools.Generator.Transformation/KotlinFixups.cs @@ -0,0 +1,46 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using MonoDroid.Generation; +using MonoDroid.Utils; + +namespace Java.Interop.Tools.Generator.Transformation +{ + public static class KotlinFixups + { + public static void Fixup (List gens) + { + foreach (var c in gens.OfType ()) + FixupClass (c); + } + + private static void FixupClass (ClassGen c) + { + // Kotlin mangles the name of some methods to make them + // inaccessible from Java, like `add-impl` and `add-V5j3Lk8`. + // We need to generate C# compatible names as well as prevent overriding + // them as we cannot generate JCW for them. + var invalid_methods = c.Methods.Where (m => m.IsKotlinNameMangled).ToList (); + + foreach (var method in invalid_methods) { + + // If the method is virtual, mark it as !virtual as it can't be overridden in Java + if (!method.IsFinal) + method.IsFinal = true; + + if (method.IsVirtual) + method.IsVirtual = false; + + // Only run this if it's the default name (ie: not a user's "managedName") + if (method.Name == StringRocks.MemberToPascalCase (method.JavaName).Replace ('-', '_')) { + // We want to remove the hyphen and anything afterwards to fix mangled names, + // but a previous step converted it to an underscore. Remove the final + // underscore and anything after it. + var index = method.Name.LastIndexOf ('_'); + + method.Name = method.Name.Substring (0, index); + } + } + } + } +} diff --git a/tools/generator/generator.csproj b/tools/generator/generator.csproj index 30c8d16bd..9b3f96404 100644 --- a/tools/generator/generator.csproj +++ b/tools/generator/generator.csproj @@ -104,6 +104,7 @@ +