-
Notifications
You must be signed in to change notification settings - Fork 54
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[generator] Add nullable reference types (NRT) support. #563
Conversation
e22c29a
to
0bfc0e9
Compare
@@ -233,12 +234,12 @@ public override Boolean CreateGenericValue (ref JniObjectReference reference, Jn | |||
return JniBoolean.GetValueFromJni (ref reference, options, targetType); | |||
} | |||
|
|||
public override JniValueMarshalerState CreateGenericArgumentState (Boolean value, ParameterAttributes synchronize = ParameterAttributes.In) | |||
public override JniValueMarshalerState CreateGenericArgumentState ([MaybeNull]Boolean value, ParameterAttributes synchronize = ParameterAttributes.In) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do we have commit message info for why the introduction of [MaybeNull]
? Why wasn't this needed before?
Additionally, there should be a corresponding change to src/Java.Interop/Java.Interop/JniBuiltinMarshalers.tt
. Visual Studio (for Mac?) should update JniBuiltinMarshalers.cs
based on JniBuiltinMarshalers.tt
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Probably "too late", but did we need the Java.Interop.dll
changes intermixed with the generator
changes?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The method it is overriding specifies [MaybeNull]
, so you get a "nullability mismatch warning":
I can't find an instance of it right now, from what I can tell this warning was still being tweaked by the Roslyn team, and it only appears on newer versions of the C# compiler.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I removed these changes for now, as they'll need to be done in the .tt
files. There's about 60 warnings that I am seeing now:
I don't know how I missed these before. I suggest we go ahead and continue getting this PR merged and then we can go back and clean up those warnings.
@@ -246,7 +247,7 @@ public override IList<Boolean> CreateGenericValue (ref JniObjectReference refere | |||
(ref JniObjectReference h, JniObjectReferenceOptions o) => new JavaBooleanArray (ref h, o)); | |||
} | |||
|
|||
public override JniValueMarshalerState CreateGenericObjectReferenceArgumentState (IList<Boolean> value, ParameterAttributes synchronize) | |||
public override JniValueMarshalerState CreateGenericObjectReferenceArgumentState ([MaybeNull]IList<Boolean> value, ParameterAttributes synchronize) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
These changes should likewise prompt a change to src/Java.Interop/Java.Interop/JavaPrimitiveArrays.tt
.
@@ -5,7 +5,7 @@ int Count { | |||
[Register ("set_Count", "(I)V", "Getset_Count_IHandler:java.code.IMyInterfaceInvoker, ")] set; | |||
} | |||
|
|||
java.lang.String Key { | |||
string Key { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This change concerns me: why did it occur? For that matter, how was it java.lang.String
in the first place?
Fortunately this doesn't trigger any API breakage in Mono.Android.dll
, as per https://devdiv.visualstudio.com/DevDiv/_build/results?buildId=3648625&view=results
I just find this really weird. What change to generator
is triggering this? Was it deliberate? What's the rationale?
I don't see this mentioned in the commit message.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The change is that previously we were writing this type from property.Type
, which is Setter != null ? Setter.Parameters [0].Type : Getter.ReturnType
.
This was refactored and combined with other places that were writing property.Getter.ReturnType
, to a new method which always prefers Getter.ReturnType
:
The test is a unit test and does not go through the full pipeline of resolving all the types, thus its Setter.Parameters [0].Type
isn't changing from java.lang.String
to string
, but its Getter.ReturnType
is getting resolved to string
.
That is, the unit test code is incorrectly this:
Setter.Parameters [0].Type
=java.lang.String
Getter.ReturnType
=string
In the real world, Setter.Parameters [0].Type
and Getter.ReturnType
should always refer to the same symbol, so this should not affect real code.
Fixes: #468 Context: dotnet/android#4227 Add [C#8 nullable reference type][0] (NRT) support to `generator` when given `generator -lang-features=nullable-reference-types`. This uses a variety of Java annotations to infer nullable information (18c29b7) via the `//method/@return-not-null` and `//parameter/@not-null` attribute values within `api.xml` to "forward" nullability information to the generated C# binding. ~~ Goals ~~ `generator` should be able to interpret the nullable annotations provided by an input `.jar` file (via `class-parse`). It should use this information to generate bindings that expose similar nullable annotations on the produced public C# API. For example, this Java: // Java public class Foo { public void bar (@NotNull Object baz, String value) { … } } Should generate this C# API: // C# Binding public class Foo : Java.Lang.Object { public void Bar (Java.Lang.Object baz, string? value) { … } } Additionally, the generated binding code should not produce any additional warnings *on its own*. That is, the internal plumbing code itself should not create warnings. ~~ Non-Goals ~~ There exists cases in our generated plumbing code that do not play nicely with the provability of C#8 nullable reference types. For example, we may generate code like this: int Java.Lang.IComparable.CompareTo (Java.Lang.Object o) { return CompareTo (global::Java.Interop.JavaObjectExtensions.JavaCast<Android.Util.Half>(o)); } Technically `.JavaCast<>()` can return `null`, which cannot be passed to `.CompareTo (object o)` because it does not accept `null`. In these cases we liberally use the [null forgiving operator (`!`)][1] to suppress warnings. It may be desirable to change how this code is structured to be better provably `null`-safe, however this PR does not attempt to make those modification. It is assumed that the code is currently working, so `null` is prevented here via other mechanisms. No functional changes are made to generated code. Additionally, there are cases where Java nullable annotations can create scenarios that will produce warnings in C#, particularly around inheritance. For example: // Java public class Base { public void m (@NotNull Object baz) { … } } public class Derived extends Base { @OverRide public void m (Object baz) { … } } This would produce a C# warning such as: CS8610: Nullability of reference types in type of parameter 'M' doesn't match overridden member. `generator` will not attempt to resolve this error, it is an exercise for the user. This can be accomplished by fixing the Java code or using `metadata` to override the `//@not-null` attribute such as: <attr path="/api/package[@name='blah']/class[@name='Foo2']/method[@name='Bar' and count(parameter)=1 and parameter[1][@type='object']]/parameter" name="not-null">true</attr> ~~ Unit Test Changes ~~ Several of the unit test "expected output" files changed their property type from `java.lang.String` to `string`. This occurred due to a related refactoring of parameter & return type generation code. This change shouldn't be "user visible" because the unit tests don't go through a "complete" pipeline which would involve ensuring that get- and set-method pairs have consistent parameter & return types. [0]: https://docs.microsoft.com/en-us/dotnet/csharp/nullable-references [1]: https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/operators/null-forgiving
Context: #468
XA Companion PR: dotnet/android#4227
Adds nullable reference types (NRT) support in
generator
, when passed the flag-lang-features=nullable-reference-types
, using Java's@NotNull
annotations to annotate the C# public API.Goals
Surface Java Provided Annotations
generator
should be able to interpret the nullable annotations provided by an input jar (technically the info provided byclass-parse
). It should use this information to generate bindings that expose the same nullable annotations on the produced public C# API.For example, this Java:
Should generate this C# API:
No Additional Infrastructure Warnings
The generated binding code should not produce any additional warnings on its own. That is, the internal plumbing code itself should not create warnings.
Non-Goals
Generated Binding Code Audit
There exists cases in our generated plumbing code that do not play nicely with the provability of NRT. For example, we may generate code like this:
Technically
JavaCast<>
can returnnull
, which cannot be passed toCompareTo (object o)
because it does not acceptnull
. In these cases we liberally use the null forgiveness operator (!
) to suppress warnings. It may be desirable to change how this code is structured to be better provably null-safe, however this PR does not attempt to make those modification. It is assumed that the code is currently working, sonull
is prevented here via other mechanisms. No functional changes are made to generated code.Fixing "Wrong" Java Annotations
There are cases where Java nullable annotations can create scenarios that will produce warnings in C#, particularly around inheritance. For example:
This would produce a C# warning such as:
generator
will not attempt to resolve this error, it is an exercise for the user. This can be accomplished by fixing the Java code or usingmetadata
such as:Release Notes
I don't think this change needs to be specifically called out, this is just a reminder to call it out in the 2 places it will be visible to users:
Mono.Android.dll
: [Mono.Android] Add NRT annotations. android#4227