Skip to content

Commit 8dac926

Browse files
atsushienojonpryor
authored andcommitted
[api-xml-adjuster] method overrides cannot expose non-public types. (#94)
Fixes: https://bugzilla.xamarin.com/show_bug.cgi?id=44531 When a Java method is overriden, its parameter type or return type can be variant, which possibly result in exposure of non-public types (returning private class object which is derived from public type is totally valid). It is because we blindly emit the parsed type from class-parse output as the API XML adjuster output. It needs to be examined and adjusted to be public (or protected) type. jar2xml didn't have this problem because Java reflection API is clever enough to cover that issue. The fixes are... not cosmetic. Basically now we use TypeReference.ToString() which used to not be precise and ready for API XML output. So there are couple of fixes for that. (We also want to track from which method base a parameter or return type is bound, so we pass that to the .ctor now). We hide extraneous public TypeParameter.ctor() now to reduce chances for bugs. Tests need to be modified to reflect the changes. Lastly, Mono.Android API XML will be updated to reflect this change. There will be some removal of method overrides from api-*.xml.in but there was no regression reported from api-diff.
1 parent 572b119 commit 8dac926

14 files changed

+779487
-370646
lines changed

src/Xamarin.Android.Tools.ApiXmlAdjuster/JavaApi.XmlModel.cs

+11-2
Original file line numberDiff line numberDiff line change
@@ -253,6 +253,12 @@ public override string ToString ()
253253

254254
public partial class JavaParameter
255255
{
256+
public JavaParameter (JavaMethodBase parent)
257+
{
258+
Parent = parent;
259+
}
260+
261+
public JavaMethodBase Parent { get; private set; }
256262
public string Name { get; set; }
257263
public string Type { get; set; }
258264

@@ -309,7 +315,7 @@ public JavaTypeParameter (JavaTypeParameters parent)
309315

310316
public override string ToString ()
311317
{
312-
return Name + (GenericConstraints == null ? null : GenericConstraints.ToString ());
318+
return Name;
313319
}
314320
}
315321

@@ -326,7 +332,10 @@ public JavaGenericConstraints ()
326332

327333
public override string ToString ()
328334
{
329-
return " " + (BoundsType ?? "extends") + " " + string.Join (" & ", GenericConstraints);
335+
string csts = string.Join (" & ", GenericConstraints);
336+
if (csts == "java.lang.Object")
337+
return string.Empty;
338+
return " " + (BoundsType ?? "extends") + " " + csts;
330339
}
331340
}
332341

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
using System;
2+
using System.Linq;
3+
4+
namespace Xamarin.Android.Tools.ApiXmlAdjuster
5+
{
6+
public static class JavaApiFixVisibilityExtensions
7+
{
8+
public static string GetVisibleTypeName (this JavaParameter parameter)
9+
{
10+
var r = GetVisibleNonSpecialType (parameter);
11+
return r != null ? r.ToString () : parameter.Type;
12+
}
13+
14+
public static string GetVisibleReturnTypeName (this JavaMethod method)
15+
{
16+
var r = GetVisibleNonSpecialReturnType (method);
17+
return r != null ? r.ToString () : method.Return;
18+
}
19+
20+
public static JavaTypeReference GetVisibleNonSpecialType (this JavaParameter parameter)
21+
{
22+
return GetVisibleNonSpecialType (parameter.Parent, parameter.ResolvedType);
23+
}
24+
25+
public static JavaTypeReference GetVisibleNonSpecialReturnType (this JavaMethod method)
26+
{
27+
return GetVisibleNonSpecialType (method, method.ResolvedReturnType);
28+
}
29+
30+
static JavaTypeReference GetVisibleNonSpecialType (this JavaMethodBase method, JavaTypeReference r)
31+
{
32+
if (r == null || r.SpecialName != null || r.ReferencedTypeParameter != null || r.ArrayPart != null)
33+
return null;
34+
var requiredVisibility = method.Visibility == "public" && method.Parent.Visibility == "public" ? "public" : method.Visibility;
35+
for (var t = r; t != null; t = (t.ReferencedType as JavaClass)?.ResolvedExtends) {
36+
if (t.ReferencedType == null)
37+
break;
38+
if (IsAcceptableVisibility (required: requiredVisibility, actual: t.ReferencedType.Visibility))
39+
return t;
40+
}
41+
return null;
42+
}
43+
44+
static bool IsAcceptableVisibility (string required, string actual)
45+
{
46+
if (required == "public")
47+
return actual == "public";
48+
else
49+
return true;
50+
}
51+
}
52+
}

src/Xamarin.Android.Tools.ApiXmlAdjuster/JavaApiTypeResolverExtensions.cs

+4-2
Original file line numberDiff line numberDiff line change
@@ -24,9 +24,11 @@ static JavaTypeReference JavaTypeNameToReference (this JavaApi api, JavaTypeName
2424
var tp = contextTypeParameters.Where (tps => tps != null).SelectMany (tps => tps.TypeParameters).FirstOrDefault (_ => _.Name == tn.FullNameNonGeneric);
2525
if (tp != null)
2626
return new JavaTypeReference (tp, tn.ArrayPart);
27+
if (tn.FullNameNonGeneric == JavaTypeReference.GenericWildcard.SpecialName)
28+
return new JavaTypeReference (tn.BoundsType, tn.GenericConstraints?.Select (gc => JavaTypeNameToReference (api, gc, contextTypeParameters)), tn.ArrayPart);
2729
var primitive = JavaTypeReference.GetSpecialType (tn.FullNameNonGeneric);
2830
if (primitive != null)
29-
return tn.ArrayPart == null ? primitive : new JavaTypeReference (primitive, tn.ArrayPart);
31+
return tn.ArrayPart == null && tn.GenericConstraints == null ? primitive : new JavaTypeReference (primitive, tn.ArrayPart, tn.BoundsType, tn.GenericConstraints?.Select (gc => JavaTypeNameToReference (api, gc, contextTypeParameters)));
3032
var type = api.FindNonGenericType (tn.FullNameNonGeneric);
3133
return new JavaTypeReference (type,
3234
tn.GenericArguments != null ? tn.GenericArguments.Select (_ => api.JavaTypeNameToReference (_, contextTypeParameters)).ToArray () : null,
@@ -111,7 +113,7 @@ static void ResolveMethodBase (this JavaMethodBase m, JavaTypeParameters methodT
111113
public static void Resolve (this JavaMethod m)
112114
{
113115
if (m.TypeParameters != null)
114-
m.TypeParameters.Resolve (m.GetApi (), m.Parent.TypeParameters);
116+
m.TypeParameters.Resolve (m.GetApi (), m.TypeParameters);
115117
m.ResolveMethodBase (m.TypeParameters);
116118
m.ResolvedReturnType = m.GetApi ().Parse (m.Return, m.Parent.TypeParameters, m.TypeParameters);
117119
}

src/Xamarin.Android.Tools.ApiXmlAdjuster/JavaApiXmlGeneratorExtensions.cs

+3-3
Original file line numberDiff line numberDiff line change
@@ -176,7 +176,7 @@ static void Save (this JavaMethod method, XmlWriter writer)
176176
SaveCommon (method, writer, "method",
177177
XmlConvert.ToString (method.Abstract),
178178
XmlConvert.ToString (method.Native),
179-
method.Return,
179+
method.GetVisibleReturnTypeName (),
180180
XmlConvert.ToString (method.Synchronized),
181181
null,
182182
null,
@@ -232,8 +232,8 @@ static void SaveCommon (this JavaMember m, XmlWriter writer, string elementName,
232232
foreach (var p in parameters) {
233233
writer.WriteStartElement ("parameter");
234234
writer.WriteAttributeString ("name", p.Name);
235-
writer.WriteAttributeString ("type", p.Type);
236-
writer.WriteString ("\n ");
235+
writer.WriteAttributeString ("type", p.GetVisibleTypeName ());
236+
writer.WriteString ("\n ");
237237
writer.WriteFullEndElement ();
238238
}
239239
}

src/Xamarin.Android.Tools.ApiXmlAdjuster/JavaApiXmlLoaderExtensions.cs

+1-1
Original file line numberDiff line numberDiff line change
@@ -242,7 +242,7 @@ static void LoadMethodBase (this JavaMethodBase methodBase, string elementName,
242242
tp.Load (reader);
243243
method.TypeParameters = tp;
244244
} else if (reader.LocalName == "parameter") {
245-
var p = new JavaParameter ();
245+
var p = new JavaParameter (methodBase);
246246
p.Load (reader);
247247
methodBase.Parameters.Add (p);
248248
} else if (reader.LocalName == "exception") {

src/Xamarin.Android.Tools.ApiXmlAdjuster/JavaTypeName.cs

+10-7
Original file line numberDiff line numberDiff line change
@@ -19,14 +19,14 @@ namespace Xamarin.Android.Tools.ApiXmlAdjuster
1919
//
2020
// A generic type parameter, or a primitive type, is also represented by this too.
2121
//
22-
internal class JavaTypeName
22+
public class JavaTypeName
2323
{
2424
const string extendsLabel = " extends ";
2525
const string superLabel = " super ";
2626

2727
static readonly string [] genericConstraintsLabels = { extendsLabel, superLabel };
2828

29-
private JavaTypeName ()
29+
JavaTypeName ()
3030
{
3131
}
3232

@@ -40,6 +40,7 @@ public static JavaTypeName Parse (string dottedFullName)
4040
int gccidx = gcidx < 0 ? -1 : dottedFullName.IndexOf (',', 0, gcidx);
4141
if (gcidx > 0 && gcgidx < 0 && gccidx < 0) {
4242
string args = dottedFullName.Substring (gcidx + label.Length).Trim ();
43+
ret.BoundsType = label;
4344
ret.GenericConstraints = ParseCommaSeparatedTypeNames (args).Select (s => Parse (s)).ToArray ();
4445
dottedFullName = dottedFullName.Substring (0, gcidx).Trim ();
4546
}
@@ -69,7 +70,7 @@ public static JavaTypeName Parse (string dottedFullName)
6970

7071
return ret;
7172
}
72-
73+
7374
static IEnumerable<string> ParseCommaSeparatedTypeNames (string args)
7475
{
7576
int comma = args.IndexOf (',');
@@ -79,7 +80,7 @@ static IEnumerable<string> ParseCommaSeparatedTypeNames (string args)
7980
int open = args.IndexOf ('<', 0, comma);
8081
if (open > 0) {
8182
int openCount = 1;
82-
int i = open;
83+
int i = open + 1;
8384
while (i < args.Length) {
8485
if (args [i] == '<')
8586
openCount++;
@@ -90,10 +91,11 @@ static IEnumerable<string> ParseCommaSeparatedTypeNames (string args)
9091
break;
9192
}
9293
yield return args.Substring (0, i);
93-
if (i > args.Length) {
94+
if (i < args.Length) {
9495
comma = args.IndexOf (',', i);
95-
foreach (var s in ParseCommaSeparatedTypeNames (args.Substring (comma + 1)))
96-
yield return s;
96+
if (comma > 0)
97+
foreach (var s in ParseCommaSeparatedTypeNames (args.Substring (comma + 1)))
98+
yield return s;
9799
}
98100
} else {
99101
yield return args.Substring (0, comma);
@@ -104,6 +106,7 @@ static IEnumerable<string> ParseCommaSeparatedTypeNames (string args)
104106
}
105107

106108
public string FullNameNonGeneric { get; set; }
109+
public string BoundsType { get; set; } // " extends " / " super "
107110
public IList<JavaTypeName> GenericConstraints { get; private set; }
108111
public IList<JavaTypeName> GenericArguments { get; private set; }
109112
public string ArrayPart { get; set; }

src/Xamarin.Android.Tools.ApiXmlAdjuster/JavaTypeReference.cs

+22-5
Original file line numberDiff line numberDiff line change
@@ -48,17 +48,28 @@ static JavaTypeReference ()
4848
GenericWildcard = new JavaTypeReference ("?");
4949
}
5050

51-
public JavaTypeReference (string specialName)
51+
JavaTypeReference (string specialName)
5252
{
5353
SpecialName = specialName;
5454
}
5555

56-
public JavaTypeReference (JavaTypeReference referencedType, string arrayPart)
56+
public JavaTypeReference (string constraintLabel, IEnumerable<JavaTypeReference> wildcardConstraints, string arrayPart)
57+
{
58+
SpecialName = GenericWildcard.SpecialName;
59+
ArrayPart = arrayPart;
60+
WildcardBoundsType = constraintLabel;
61+
WildcardConstraints = wildcardConstraints != null && wildcardConstraints.Any () ? wildcardConstraints.ToList () : null;
62+
}
63+
64+
public JavaTypeReference (JavaTypeReference referencedType, string arrayPart, string wildcardBoundsType, IEnumerable<JavaTypeReference> wildcardConstraints)
5765
{
5866
if (referencedType == null)
5967
throw new ArgumentNullException ("referencedType");
6068
SpecialName = referencedType.SpecialName;
69+
WildcardBoundsType = wildcardBoundsType;
70+
WildcardConstraints = wildcardConstraints?.ToList ();
6171
ReferencedType = referencedType.ReferencedType;
72+
ReferencedTypeParameter = referencedType.ReferencedTypeParameter;
6273
TypeParameters = referencedType.TypeParameters;
6374
ArrayPart = arrayPart;
6475
}
@@ -81,17 +92,23 @@ public JavaTypeReference (JavaType referencedType, IList<JavaTypeReference> type
8192
}
8293

8394
public string SpecialName { get; private set; }
95+
96+
public string WildcardBoundsType { get; private set; }
97+
public IList<JavaTypeReference> WildcardConstraints { get; private set; }
98+
8499
public JavaType ReferencedType { get; private set; }
85100
public JavaTypeParameter ReferencedTypeParameter { get; private set; }
86101
public IList<JavaTypeReference> TypeParameters { get; private set; }
87102
public string ArrayPart { get; private set; }
88103

89104
public override string ToString ()
90105
{
91-
if (SpecialName != null)
92-
return SpecialName;
106+
if (SpecialName == GenericWildcard.SpecialName && WildcardConstraints != null)
107+
return SpecialName + WildcardBoundsType + string.Join (" & ", WildcardConstraints);
108+
else if (SpecialName != null)
109+
return SpecialName + ArrayPart;
93110
else if (ReferencedTypeParameter != null)
94-
return ReferencedTypeParameter.Name;
111+
return ReferencedTypeParameter.ToString () + ArrayPart;
95112
else
96113
return string.Format ("{0}{1}{2}{3}{4}",
97114
ReferencedType.Parent.Name,

src/Xamarin.Android.Tools.ApiXmlAdjuster/Tests/JavaApiTest.cs

+18
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,24 @@ public void TestToString ()
2424
var kls = pkg.Types.First (t => t.FullName == "android.database.ContentObservable");
2525
Assert.AreEqual ("[Class] android.database.ContentObservable", kls.ToString ());
2626
}
27+
28+
[Test]
29+
public void ParseName ()
30+
{
31+
var tn = JavaTypeName.Parse ("java.util.Function<java.util.Map.Entry<K, V>, ? extends U>");
32+
Assert.AreEqual ("java.util.Function", tn.FullNameNonGeneric, "top failed to parse name");
33+
Assert.AreEqual (2, tn.GenericArguments.Count, "top incorrect number of parsed generic arguments");
34+
var ga1 = tn.GenericArguments [0];
35+
Assert.AreEqual ("java.util.Map.Entry", ga1.FullNameNonGeneric, "genarg#0 name mismatch");
36+
Assert.AreEqual (2, ga1.GenericArguments.Count, "genarg#0 incorrect number of parsed generic arguments");
37+
Assert.AreEqual ("K", ga1.GenericArguments [0].FullNameNonGeneric, "genarg#0.1 name mismatch");
38+
Assert.AreEqual ("V", ga1.GenericArguments [1].FullNameNonGeneric, "genarg#0.2 name mismatch");
39+
var ga2 = tn.GenericArguments [1];
40+
Assert.AreEqual ("?", ga2.FullNameNonGeneric, "genarg#1 name mismatch");
41+
Assert.AreEqual (" extends ", ga2.BoundsType, "genarg#1 incorrect bounds type");
42+
Assert.AreEqual (1, ga2.GenericConstraints.Count, "genarg#1 incorrect number of parsed generic constraints");
43+
Assert.AreEqual ("U", ga2.GenericConstraints [0].FullNameNonGeneric, "genarg#1.1 constraint name mismatch");
44+
}
2745
}
2846
}
2947

src/Xamarin.Android.Tools.ApiXmlAdjuster/Tests/JavaApiTestHelper.cs

+1-1
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ public class JavaApiTestHelper
1616
"src",
1717
"Xamarin.Android.Tools.ApiXmlAdjuster",
1818
"Tests",
19-
"api-10.xml.in");
19+
"api-24.xml.in");
2020

2121
public static JavaApi GetLoadedApi ()
2222
{

0 commit comments

Comments
 (0)