Skip to content

Commit 131c149

Browse files
authored
[generator] Fix NRE from return type not consistently set (#834)
Fixes: dotnet/android#5921 Context: #834 (comment) Context: https://github.com/xamarin/java.interop/blob/100fffc1dc416f543293d0509870138d9d4f1669/tools/generator/Java.Interop.Tools.Generator.CodeGeneration/CodeGenerator.cs#L1175-L1181 Consider the following Java type declaration: package com.example; public interface FlowIterator<R> { R next(); public static class RangeIterator<T extends Comparable<T>> implements FlowIterator<T> { public T next() {return null;} } } This used to work in d16-7 -- not sure when exactly we broke it -- but it now throws a `NullReferenceException` when using `generator --codegen-target=XAJavaInterop1`, when emitting the nested `FlowIteratorRangerIterator` type: $ mono bin/Debug/generator.exe -o yyy ji-834-fixed.xml --codegen-target=XAJavaInterop1 … Unhandled Exception: System.NullReferenceException: Object reference not set to an instance of an object at Xamarin.SourceWriter.MethodWriter.WriteReturnType (Xamarin.SourceWriter.CodeWriter writer) at Xamarin.SourceWriter.MethodWriter.WriteSignature (Xamarin.SourceWriter.CodeWriter writer) at Xamarin.SourceWriter.MethodWriter.Write (Xamarin.SourceWriter.CodeWriter writer) Fix the `NullReferenceException` by ensuring that the `ReturnType` property and `Parameters` collection are set before the `BoundMethodAbstractDeclaration` constructor completes.
1 parent 100fffc commit 131c149

File tree

2 files changed

+55
-2
lines changed

2 files changed

+55
-2
lines changed

tests/generator-Tests/Unit-Tests/CodeGeneratorTests.cs

+52
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,11 @@
11
using System;
22
using System.Collections.Generic;
33
using System.Linq;
4+
using generator.SourceWriters;
45
using MonoDroid.Generation;
56
using NUnit.Framework;
67
using Xamarin.Android.Binder;
8+
using Xamarin.SourceWriter;
79

810
namespace generatortests
911
{
@@ -1271,5 +1273,55 @@ public void WriteClassInternalBase ()
12711273
Assert.True (result.Contains ("internal static new IntPtr class_ref".NormalizeLineEndings ()));
12721274
Assert.False (result.Contains ("internal static IntPtr class_ref".NormalizeLineEndings ()));
12731275
}
1276+
1277+
[Test]
1278+
public void WriteBoundMethodAbstractDeclarationWithGenericReturn ()
1279+
{
1280+
// Fix a case where the ReturnType of a class method implementing a generic interface method
1281+
// that has a generic parameter type return (like T) wasn't getting set, resulting in a NRE.
1282+
var gens = ParseApiDefinition (@"<api>
1283+
<package name='java.lang' jni-name='java/lang'>
1284+
<class abstract='false' deprecated='not deprecated' final='false' name='Object' static='false' visibility='public' jni-signature='Ljava/lang/Object;' />
1285+
<interface abstract='false' deprecated='not deprecated' final='false' name='Comparable' static='false' visibility='public' jni-signature='Ljava/lang/Double;' />
1286+
</package>
1287+
1288+
<package name='com.example' jni-name='com/example'>
1289+
<interface abstract='true' deprecated='not deprecated' final='false' name='FlowIterator' static='false' visibility='public' jni-signature='Lcom/example/FlowIterator;'>
1290+
<typeParameters>
1291+
<typeParameter name='R' classBound='java.lang.Object' jni-classBound='Ljava/lang/Object;'></typeParameter>
1292+
</typeParameters>
1293+
<method abstract='true' deprecated='not deprecated' final='false' name='next' jni-signature='()Ljava/lang/Object;' bridge='false' native='false' return='R' jni-return='TR;' static='false' synchronized='false' synthetic='false' visibility='public' />
1294+
</interface>
1295+
<class abstract='true' deprecated='not deprecated' extends='java.lang.Object' extends-generic-aware='java.lang.Object' jni-extends='Ljava/lang/Object;' final='false' name='FlowIterator.RangeIterator' static='true' visibility='public' jni-signature='Lcom/example/FlowIterator$RangeIterator;'>
1296+
<implements name='com.example.FlowIterator' name-generic-aware='com.example.FlowIterator&lt;T&gt;' jni-type='Lcom/example/FlowIterator&lt;TT;&gt;;'></implements>
1297+
<typeParameters>
1298+
<typeParameter name='T' interfaceBounds='java.lang.Comparable&lt;T&gt;' jni-interfaceBounds='Ljava/lang/Comparable&lt;TT;&gt;;'>
1299+
<genericConstraints>
1300+
<genericConstraint type='java.lang.Comparable&lt;T&gt;'></genericConstraint>
1301+
</genericConstraints>
1302+
</typeParameter>
1303+
</typeParameters>
1304+
<method abstract='false' deprecated='not deprecated' final='false' name='next' jni-signature='()Ljava/lang/Comparable;' bridge='false' native='false' return='T' jni-return='TT;' static='false' synchronized='false' synthetic='false' visibility='public' />
1305+
</class>
1306+
</package>
1307+
</api>");
1308+
1309+
var declIface = gens.OfType<InterfaceGen> ().Single (c => c.Name == "IFlowIterator");
1310+
var declClass = declIface.NestedTypes.OfType<ClassGen> ().Single (c => c.Name == "FlowIteratorRangeIterator");
1311+
var method = declClass.Methods.Single ();
1312+
1313+
var bmad = new BoundMethodAbstractDeclaration (declClass, method, options, null);
1314+
var source_writer = new CodeWriter (writer);
1315+
1316+
bmad.Write (source_writer);
1317+
1318+
var expected = @"Java.Lang.Object Com.Example.FlowIteratorRangeIterator.Next ()
1319+
{
1320+
throw new NotImplementedException ();
1321+
}";
1322+
1323+
// Ignore nullable operator so this test works on all generators ;)
1324+
Assert.AreEqual (expected.NormalizeLineEndings (), writer.ToString ().NormalizeLineEndings ().Replace ("?", ""));
1325+
}
12741326
}
12751327
}

tools/generator/SourceWriters/BoundMethodAbstractDeclaration.cs

+3-2
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,9 @@ public BoundMethodAbstractDeclaration (GenBase gen, Method method, CodeGeneratio
1919
this.method = method;
2020
this.opt = opt;
2121

22+
ReturnType = new TypeReferenceWriter (opt.GetTypeReferenceName (method.RetVal));
23+
this.AddMethodParameters (method.Parameters, opt);
24+
2225
if (method.RetVal.IsGeneric && gen != null) {
2326
Name = method.Name;
2427
ExplicitInterfaceImplementation = opt.GetOutputName (gen.FullName);
@@ -35,7 +38,6 @@ public BoundMethodAbstractDeclaration (GenBase gen, Method method, CodeGeneratio
3538
IsAbstract = true;
3639
IsShadow = impl.RequiresNew (method.Name, method);
3740
SetVisibility (method.Visibility);
38-
ReturnType = new TypeReferenceWriter (opt.GetTypeReferenceName (method.RetVal));
3941

4042
NewFirst = true;
4143

@@ -51,7 +53,6 @@ public BoundMethodAbstractDeclaration (GenBase gen, Method method, CodeGeneratio
5153
Attributes.Add (new RegisterAttr (method.JavaName, method.JniSignature, method.ConnectorName, additionalProperties: method.AdditionalAttributeString ()));
5254

5355
SourceWriterExtensions.AddMethodCustomAttributes (Attributes, method);
54-
this.AddMethodParameters (method.Parameters, opt);
5556
}
5657

5758
public override void Write (CodeWriter writer)

0 commit comments

Comments
 (0)