From 24ba16b5346f8339b50cebb860da8ef55191ed61 Mon Sep 17 00:00:00 2001 From: Atsushi Eno Date: Fri, 13 Jan 2017 19:29:06 +0900 Subject: [PATCH] [api-xml-adjuster] further fix on generic nested type name parsing. We have fixed nested generic type name crashes[*1] at 769f9444, but that was only to fix the crash. The resulting type name was not parsed correctly - namely, the resulting type name dropped the remainings after the first generic arguments list i.e. in `A.C`, `.C` part had dropped. The cause of the problem is somewhat deep, as JavaTypeName and JavaTypeReference do not consider the possibility that both the nested generic parent and child have generic arguments. Anyhow in JavaTypeName now we store the expected information, by having (hacky) `GenericParent` property to store the expected information. Now it comes with a test so that it should prove the purpose of the change. [*1] https://bugzilla.xamarin.com/show_bug.cgi?id=46344 --- .../JavaApiTypeResolverExtensions.cs | 8 +-- .../JavaTypeName.cs | 70 ++++++++++++------- .../Tests/JavaApiTest.cs | 16 +++++ 3 files changed, 66 insertions(+), 28 deletions(-) diff --git a/src/Xamarin.Android.Tools.ApiXmlAdjuster/JavaApiTypeResolverExtensions.cs b/src/Xamarin.Android.Tools.ApiXmlAdjuster/JavaApiTypeResolverExtensions.cs index af991e967..65f1ea7c7 100644 --- a/src/Xamarin.Android.Tools.ApiXmlAdjuster/JavaApiTypeResolverExtensions.cs +++ b/src/Xamarin.Android.Tools.ApiXmlAdjuster/JavaApiTypeResolverExtensions.cs @@ -18,15 +18,15 @@ public static JavaTypeReference Parse (this JavaApi api, string name, params Jav var tn = JavaTypeName.Parse (name); return JavaTypeNameToReference (api, tn, contextTypeParameters); } - + static JavaTypeReference JavaTypeNameToReference (this JavaApi api, JavaTypeName tn, params JavaTypeParameters [] contextTypeParameters) { - var tp = contextTypeParameters.Where (tps => tps != null).SelectMany (tps => tps.TypeParameters).FirstOrDefault (_ => _.Name == tn.FullNameNonGeneric); + var tp = contextTypeParameters.Where (tps => tps != null).SelectMany (tps => tps.TypeParameters).FirstOrDefault (_ => _.Name == tn.DottedName); if (tp != null) return new JavaTypeReference (tp, tn.ArrayPart); - if (tn.FullNameNonGeneric == JavaTypeReference.GenericWildcard.SpecialName) + if (tn.DottedName == JavaTypeReference.GenericWildcard.SpecialName) return new JavaTypeReference (tn.BoundsType, tn.GenericConstraints?.Select (gc => JavaTypeNameToReference (api, gc, contextTypeParameters)), tn.ArrayPart); - var primitive = JavaTypeReference.GetSpecialType (tn.FullNameNonGeneric); + var primitive = JavaTypeReference.GetSpecialType (tn.DottedName); if (primitive != null) return tn.ArrayPart == null && tn.GenericConstraints == null ? primitive : new JavaTypeReference (primitive, tn.ArrayPart, tn.BoundsType, tn.GenericConstraints?.Select (gc => JavaTypeNameToReference (api, gc, contextTypeParameters))); var type = api.FindNonGenericType (tn.FullNameNonGeneric); diff --git a/src/Xamarin.Android.Tools.ApiXmlAdjuster/JavaTypeName.cs b/src/Xamarin.Android.Tools.ApiXmlAdjuster/JavaTypeName.cs index 4f7d1877e..adc9e6414 100644 --- a/src/Xamarin.Android.Tools.ApiXmlAdjuster/JavaTypeName.cs +++ b/src/Xamarin.Android.Tools.ApiXmlAdjuster/JavaTypeName.cs @@ -33,7 +33,7 @@ public class JavaTypeName public static JavaTypeName Parse (string dottedFullName) { var ret = new JavaTypeName (); - + foreach (var label in genericConstraintsLabels) { int gcidx = dottedFullName.IndexOf (label, StringComparison.Ordinal); int gcgidx = gcidx < 0 ? -1 : dottedFullName.IndexOf ('<', 0, gcidx); @@ -45,7 +45,7 @@ public static JavaTypeName Parse (string dottedFullName) dottedFullName = dottedFullName.Substring (0, gcidx).Trim (); } } - + if (dottedFullName.EndsWith ("...", StringComparison.Ordinal)) { ret.ArrayPart = "..."; dottedFullName = dottedFullName.Substring (0, dottedFullName.Length - 3); @@ -56,36 +56,47 @@ public static JavaTypeName Parse (string dottedFullName) dottedFullName = dottedFullName.Substring (0, aidx); } - Func getMatchingGenericCloser = (str, start) => { - int count = 0; - for (int i = start; i < str.Length; i++) { - switch (str [i]) { - case '<': - count++; - break; - case '>': - if (count-- == 0) - return i; - break; - } - } - return -1; - }; int idx = dottedFullName.IndexOf ('<'); + int nextIndex = dottedFullName.Length; if (idx > 0) { - int last = getMatchingGenericCloser (dottedFullName, idx + 1); + int last = GetMatchingGenericCloser (dottedFullName, idx + 1); ret.GenericArguments = ParseCommaSeparatedTypeNames (dottedFullName.Substring (idx + 1, last - idx - 1)) .Select (s => JavaTypeName.Parse (s.Trim ())) .ToArray (); - dottedFullName = dottedFullName.Substring (0, idx); + nextIndex = last + 1; } - // at this state, there is no way to distinguish package name from this name specification. - ret.FullNameNonGeneric = dottedFullName; - + ret.DottedName = idx < 0 ? dottedFullName : dottedFullName.Substring (0, idx); + + if (nextIndex < dottedFullName.Length) { + if (dottedFullName [nextIndex] != '.') + throw new ArgumentException (nameof (dottedFullName)); + // the generic parent is parsed, but the rest is still there. + var parent = ret; + ret = Parse (dottedFullName.Substring (nextIndex + 1)); + ret.GenericParent = parent; + } + return ret; } + static int GetMatchingGenericCloser (string str, int start) + { + int count = 0; + for (int i = start; i < str.Length; i++) { + switch (str [i]) { + case '<': + count++; + break; + case '>': + if (count-- == 0) + return i; + break; + } + } + return -1; + } + static IEnumerable ParseCommaSeparatedTypeNames (string args) { int comma = args.IndexOf (','); @@ -119,11 +130,22 @@ static IEnumerable ParseCommaSeparatedTypeNames (string args) } } } - - public string FullNameNonGeneric { get; set; } + + public JavaTypeName GenericParent { get; set; } + public string DottedName { get; set; } public string BoundsType { get; set; } // " extends " / " super " public IList GenericConstraints { get; private set; } public IList GenericArguments { get; private set; } public string ArrayPart { get; set; } + + public string FullNameNonGeneric { + get { + if (GenericParent != null) + return GenericParent.FullNameNonGeneric + "." + DottedName; + else + return DottedName; + } + } + } } diff --git a/src/Xamarin.Android.Tools.ApiXmlAdjuster/Tests/JavaApiTest.cs b/src/Xamarin.Android.Tools.ApiXmlAdjuster/Tests/JavaApiTest.cs index bc66871d3..edf26b42e 100644 --- a/src/Xamarin.Android.Tools.ApiXmlAdjuster/Tests/JavaApiTest.cs +++ b/src/Xamarin.Android.Tools.ApiXmlAdjuster/Tests/JavaApiTest.cs @@ -42,6 +42,22 @@ public void ParseName () Assert.AreEqual (1, ga2.GenericConstraints.Count, "genarg#1 incorrect number of parsed generic constraints"); Assert.AreEqual ("U", ga2.GenericConstraints [0].FullNameNonGeneric, "genarg#1.1 constraint name mismatch"); } + + [Test] + public void ParseName2 () + { + string name = "com.good.gd.ndkproxy.auth.GDFingerprintAuthenticationManager.a.b"; + var tn = JavaTypeName.Parse (name); + Assert.IsTrue (tn.GenericParent != null, "result has generic parent"); + Assert.AreEqual ("b", tn.DottedName, "result name mismatch"); + Assert.AreEqual ("com.good.gd.ndkproxy.auth.GDFingerprintAuthenticationManager.a.b", tn.FullNameNonGeneric, "failed to parse name"); + Assert.AreEqual (1, tn.GenericArguments.Count, "result genparams count mismatch"); + Assert.AreEqual ("com.good.gd.ndkproxy.auth.d.a", tn.GenericArguments [0].FullNameNonGeneric, "result genarg name mismatch"); + var p = tn.GenericParent; + Assert.AreEqual (1, p.GenericArguments.Count, "top genparams count"); + var ga1 = p.GenericArguments [0]; + Assert.AreEqual ("com.good.gd.ndkproxy.auth.c.a", ga1.FullNameNonGeneric, "top genarg name mismatch"); + } } }