Skip to content

Commit c9d5316

Browse files
jonathanpeppersjonpryor
authored andcommitted
[generator] fix for NRE in FixupAccessModifiers (#299)
Context: 4ec5d4e In improving our support for "package-private" classes, we added a check to compare if two methods have the same signature. The expression was of the form: m.Name == baseMethod.Name && m.Parameters.JavaSignature == baseMethod.Parameters.JavaSignature The problem with this is that the `JavaSignature` property can throw a `NullReferenceException` if one of the parameter types is unknown, or only becomes valid later on in generator's process: System.NullReferenceException: Object reference not set to an instance of an object at MonoDroid.Generation.Parameter.get_JavaType () at MonoDroid.Generation.ParameterList.get_JavaSignature () at MonoDroid.Generation.ClassGen+<>c__DisplayClass29_0.<FixupAccessModifiers>b__0 (MonoDroid.Generation.Method m) at System.Linq.Enumerable.TryGetFirst[TSource] (System.Collections.Generic.IEnumerable`1[T] source, System.Func`2[T,TResult] predicate, System.Boolean& found) at System.Linq.Enumerable.FirstOrDefault[TSource] (System.Collections.Generic.IEnumerable`1[T] source, System.Func`2[T,TResult] predicate) at MonoDroid.Generation.ClassGen.FixupAccessModifiers () at Xamarin.Android.Binder.CodeGenerator.Validate (System.Collections.Generic.List`1[T] gens, MonoDroid.Generation.CodeGenerationOptions opt) at Xamarin.Android.Binder.CodeGenerator.Run (Xamarin.Android.Binder.CodeGeneratorOptions options, Java.Interop.Tools.Cecil.DirectoryAssemblyResolver resolver) at Xamarin.Android.Binder.CodeGenerator.Run (Xamarin.Android.Binder.CodeGeneratorOptions options) at Xamarin.Android.Binder.CodeGenerator.Main (System.String[] args) Instead of using `JavaSignature`, we need to rely *only* on the original Java information, and loop over the parameters individually. To fix this: - Create a `bool Matches (MethodBase other)` method in `MethodBase` - Override `Matches()` in `Method`, also check the return type - Add some unit tests for these scenarios for both `XmlMethod` and `ManagedMethod`
1 parent 6023584 commit c9d5316

File tree

5 files changed

+95
-1
lines changed

5 files changed

+95
-1
lines changed

tools/generator/ClassGen.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -246,7 +246,7 @@ public override void FixupAccessModifiers ()
246246
if (baseClass != null && RawVisibility == "public" && baseClass.RawVisibility != "public") {
247247
//Skip the BaseType and copy over any "missing" methods
248248
foreach (var baseMethod in baseClass.Methods) {
249-
var method = Methods.FirstOrDefault (m => m.Name == baseMethod.Name && m.Parameters.JavaSignature == baseMethod.Parameters.JavaSignature);
249+
var method = Methods.FirstOrDefault (m => m.Matches (baseMethod));
250250
if (method == null)
251251
Methods.Add (baseMethod);
252252
}

tools/generator/Method.cs

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -387,6 +387,22 @@ public string ReturnType {
387387
get { return RetVal.FullName; }
388388
}
389389

390+
public override bool Matches (MethodBase other)
391+
{
392+
bool ret = base.Matches (other);
393+
if (!ret)
394+
return ret;
395+
396+
var otherMethod = other as Method;
397+
if (otherMethod == null)
398+
return false;
399+
400+
if (RetVal.RawJavaType != otherMethod.RetVal.RawJavaType)
401+
return false;
402+
403+
return true;
404+
}
405+
390406
public string GetMetadataXPathReference (GenBase declaringType)
391407
{
392408
return string.Format ("{0}/method[@name='{1}'{2}]", declaringType.MetadataXPathReference, JavaName, Parameters.GetMethodXPathPredicate ());

tools/generator/MethodBase.cs

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -176,6 +176,22 @@ public string Visibility {
176176
public bool IsValid { get; private set; }
177177
public string Annotation { get; internal set; }
178178

179+
public virtual bool Matches (MethodBase other)
180+
{
181+
if (Name != other.Name)
182+
return false;
183+
184+
if (Parameters.Count != other.Parameters.Count)
185+
return false;
186+
187+
for (int i = 0; i < Parameters.Count; i++) {
188+
if (Parameters [i].RawNativeType != other.Parameters [i].RawNativeType)
189+
return false;
190+
}
191+
192+
return true;
193+
}
194+
179195
public bool Validate (CodeGenerationOptions opt, GenericParameterDefinitionList type_params)
180196
{
181197
opt.ContextMethod = this;

tools/generator/Tests/Unit-Tests/ManagedTests.cs

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,12 @@ public void Bar () { }
1919
[Register ("barWithParams", "(ZID)Ljava/lang/String;", "")]
2020
public string BarWithParams (bool a, int b, double c) => string.Empty;
2121

22+
[Register ("unknownTypes", "(Lmy/package/foo/Unknown;)V", "")]
23+
public void UnknownTypes (object unknown) { }
24+
25+
[Register ("unknownTypes", "(Lmy/package/foo/Unknown;)Lmy/package/foo/Unknown;", "")]
26+
public object UnknownTypesReturn (object unknown) => null;
27+
2228
[Register ("value")]
2329
public const int Value = 1234;
2430
}
@@ -85,6 +91,31 @@ public void Method ()
8591
Assert.IsNull (method.Deprecated);
8692
}
8793

94+
[Test]
95+
public void Method_Matches_True ()
96+
{
97+
var type = module.GetType ("Com.Mypackage.Foo");
98+
var @class = new ManagedClassGen (type);
99+
var unknownTypes = type.Methods.First (m => m.Name == "UnknownTypes");
100+
var methodA = new ManagedMethod (@class, unknownTypes);
101+
var methodB = new ManagedMethod (@class, unknownTypes);
102+
Assert.IsTrue (methodA.Matches (methodB), "Methods should match!");
103+
}
104+
105+
[Test]
106+
public void Method_Matches_False ()
107+
{
108+
var type = module.GetType ("Com.Mypackage.Foo");
109+
var @class = new ManagedClassGen (type);
110+
var unknownTypesA = type.Methods.First (m => m.Name == "UnknownTypes");
111+
var unknownTypesB = type.Methods.First (m => m.Name == "UnknownTypesReturn");
112+
unknownTypesB.Name = "UnknownTypes";
113+
var methodA = new ManagedMethod (@class, unknownTypesA);
114+
var methodB = new ManagedMethod (@class, unknownTypesB);
115+
//Everything the same besides return type
116+
Assert.IsFalse (methodA.Matches (methodB), "Methods should not match!");
117+
}
118+
88119
[Test]
89120
public void MethodWithParameters ()
90121
{

tools/generator/Tests/Unit-Tests/XmlTests.cs

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,12 @@ public void SetUp ()
3434
<parameter name=""b"" type=""int"" />
3535
<parameter name=""c"" type=""double"" />
3636
</method>
37+
<method abstract=""false"" deprecated=""not deprecated"" final=""false"" name=""unknownTypes"" native=""false"" return=""void"" static=""false"" synchronized=""false"" visibility=""public"" managedReturn=""System.Void"">
38+
<parameter name=""unknown"" type=""my.package.Unknown"" />
39+
</method>
40+
<method abstract=""false"" deprecated=""not deprecated"" final=""false"" name=""unknownTypesReturn"" native=""false"" return=""my.package.Unknown"" static=""false"" synchronized=""false"" visibility=""public"" managedReturn=""System.Object"">
41+
<parameter name=""unknown"" type=""my.package.Unknown"" />
42+
</method>
3743
<field deprecated=""not deprecated"" final=""true"" name=""value"" static=""true"" transient=""false"" type=""int"" type-generic-aware=""int"" visibility=""public"" volatile=""false"" value=""1234"" />
3844
</class>
3945
<interface abstract=""true"" deprecated=""not deprecated"" final=""false"" name=""service"" static=""false"" visibility=""public"" />
@@ -87,6 +93,31 @@ public void Method ()
8793
Assert.IsNull (method.Deprecated);
8894
}
8995

96+
[Test]
97+
public void Method_Matches_True ()
98+
{
99+
var element = package.Element ("class");
100+
var @class = new XmlClassGen (package, element);
101+
var unknownTypes = element.Elements ("method").Where (e => e.Attribute ("name").Value == "unknownTypes").First ();
102+
var methodA = new XmlMethod (@class, unknownTypes);
103+
var methodB = new XmlMethod (@class, unknownTypes);
104+
Assert.IsTrue (methodA.Matches (methodB), "Methods should match!");
105+
}
106+
107+
[Test]
108+
public void Method_Matches_False ()
109+
{
110+
var element = package.Element ("class");
111+
var @class = new XmlClassGen (package, element);
112+
var unknownTypesA = element.Elements ("method").Where (e => e.Attribute ("name").Value == "unknownTypes").First ();
113+
var unknownTypesB = element.Elements ("method").Where (e => e.Attribute ("name").Value == "unknownTypesReturn").First ();
114+
unknownTypesB.Attribute ("name").Value = "unknownTypes";
115+
var methodA = new XmlMethod (@class, unknownTypesA);
116+
var methodB = new XmlMethod (@class, unknownTypesB);
117+
//Everything the same besides return type
118+
Assert.IsFalse (methodA.Matches (methodB), "Methods should not match!");
119+
}
120+
90121
[Test]
91122
public void MethodWithParameters ()
92123
{

0 commit comments

Comments
 (0)