Skip to content

Commit 3226a4b

Browse files
authored
Enable NRT for Java.Interop.dll (#554)
C# 8 introduced [Nullable Reference Types][0], which allows reuse of the `nullable_type` grammar in which a `?` suffix on a type indicates that the value may be null -- which previously could only be placed on value types -- so that it can be placed onto reference types. As part of this C# change, within an `enabled` [nullable context][1], values of reference types such as `string` are *not null* by default. `string?` would need to be used to indicate that the value may be `null`. This change presents a potentially significant *semantic* change, particularly if `-warnaserror` is used, as source code which previously allowed `null` values can now elicit C# *warnings* if code flow analysis/etc. indicates that a non-nullable value may be null. Enable Nullable Reference Type support for `Java.Interop.dll`: * Set `$(Nullable)`=enable, so that nullable reference types are enabled project wide. * Perform the *minimal* review of the current API to indicate that certain parameters or return types may accept or return `null` values. * Use [additional custom attributes][2] to further "tighten" the semantics we're trying to describe. In particular, *type parameters* are generally *non-null*, so if a method (1) has a return value which is a type parameter, and (2) the value returned may be null, then the method should be annotated with a [`MaybeNullAttribute`][3] to indicate that the value could be null even though the type appears to forbid it. * Add a `NullableAttributes.cs` file so that we can use the additional custom attributes, as these types may not be present in the BCL that we're building against. `INTERNAL_NULLABLE_ATTRIBUTES` is added to `$(DefineConstants)` so that these types are `internal` to `Java.Interop.dll`. * Update `JniRuntime` to move away from `ConcurrentDictionary` to `lock` + `Dictionary`, because `ConcurrentDictionary.TryUpdate()` didn't support passing `null` as a new value. To do so, we'd have to move to `ConcurrentDictionary<IntPtr, JniRuntime?>`, which would alter the type of `Runtime.Values`, and change other things. * Update `JniPeerMembers` so that we no longer `null` out various members in `JniPeerMembers.Dispose()` and we instead call `InstanceFields.Clear()`. This was easier than making everything nullable, and the `Dispose()` codepath really shouldn't ever be invoked anyway.... After all this, there are two remaining nullable-reference type warnings produced by `Java.Interop.csproj`: src\Java.Interop\Java.Interop\JniRuntime.JniValueManager.cs(399,13): Warning CS8601: Possible null reference assignment. src\Java.Interop\Java.Interop\JniRuntime.JniValueManager.cs(476,13): Warning CS8601: Possible null reference assignment. We currently do not know why this CS8601 is being emitted, and will need to investigate further. Additionally: * Bump to .NET Core 3.1.x which supports nullable reference types. * Install a newer `boots` tool which supports .Net Core 3.1. [0]: https://docs.microsoft.com/en-us/dotnet/csharp/nullable-references [1]: https://docs.microsoft.com/en-us/dotnet/csharp/nullable-references#nullable-contexts [2]: https://docs.microsoft.com/en-us/dotnet/csharp/nullable-attributes#specify-post-conditions-maybenull-and-notnull [3]: https://docs.microsoft.com/en-us/dotnet/api/system.diagnostics.codeanalysis.maybenullattribute?view=netcore-3.1
1 parent d99c809 commit 3226a4b

File tree

58 files changed

+801
-426
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

58 files changed

+801
-426
lines changed

build-tools/automation/azure-pipelines.yaml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ trigger:
66

77
# Global variables
88
variables:
9-
DotNetCoreVersion: 2.1.701
9+
DotNetCoreVersion: 3.1.x
1010
HostedMac: Hosted Mac Internal
1111
HostedWinVS2019: Hosted Windows 2019 with VS2019
1212

@@ -80,7 +80,7 @@ jobs:
8080
version: $(DotNetCoreVersion)
8181

8282
- script: |
83-
dotnet tool install --global boots --version 1.0.0.291
83+
dotnet tool install --global boots
8484
boots https://download.mono-project.com/archive/6.4.0/macos-10-universal/MonoFramework-MDK-6.4.0.198.macos10.xamarin.universal.pkg
8585
displayName: Install Mono 6.4
8686

src/Java.Interop/GlobalSuppressions.cs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,10 @@
7272

7373
[assembly: SuppressMessage ("Performance", "CA1822:Mark members as static", Justification = "<Pending>", Scope = "member", Target = "~M:Java.Interop.JniRuntime.JniMarshalMemberBuilder.IsDirectMethod(System.Reflection.ParameterInfo[])~System.Boolean")]
7474
[assembly: SuppressMessage ("Performance", "CA1822:Mark members as static", Justification = "<Pending>", Scope = "member", Target = "~M:Java.Interop.JniRuntime.JniValueManager.GetJniIdentityHashCode(Java.Interop.JniObjectReference)~System.Int32")]
75+
[assembly: SuppressMessage ("Performance", "CA1822:Mark members as static", Justification = "<Pending>", Scope = "member", Target = "~P:Java.Interop.JniFieldInfo.Name")]
76+
[assembly: SuppressMessage ("Performance", "CA1822:Mark members as static", Justification = "<Pending>", Scope = "member", Target = "~P:Java.Interop.JniFieldInfo.Signature")]
77+
[assembly: SuppressMessage ("Performance", "CA1822:Mark members as static", Justification = "<Pending>", Scope = "member", Target = "~P:Java.Interop.JniMethodInfo.Name")]
78+
[assembly: SuppressMessage ("Performance", "CA1822:Mark members as static", Justification = "<Pending>", Scope = "member", Target = "~P:Java.Interop.JniMethodInfo.Signature")]
7579

7680
[assembly: SuppressMessage ("Performance", "CA1823:Avoid unused private fields", Justification = "Used for native interop", Scope = "type", Target = "~T:Java.Interop.JavaException")]
7781
[assembly: SuppressMessage ("Performance", "CA1823:Avoid unused private fields", Justification = "Used for native interop", Scope = "type", Target = "~T:Java.Interop.JavaObject")]
@@ -85,3 +89,5 @@
8589

8690
[assembly: SuppressMessage ("Usage", "CA2208:Instantiate argument exceptions correctly", Justification = "<Pending>", Scope = "member", Target = "~M:Java.Interop.JniEnvironment.Exceptions.Throw(Java.Interop.JniObjectReference)")]
8791
[assembly: SuppressMessage ("Usage", "CA2208:Instantiate argument exceptions correctly", Justification = "<Pending>", Scope = "member", Target = "~M:Java.Interop.JniEnvironment.Exceptions.ThrowNew(Java.Interop.JniObjectReference,System.String)")]
92+
93+
[assembly: SuppressMessage ("Usage", "CA2213:Disposable fields should be disposed", Justification = "<Pending>", Scope = "member", Target = "~F:Java.Interop.JniRuntime.valueManager")]

src/Java.Interop/Java.Interop-MonoAndroid.csproj

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,18 +15,20 @@
1515
<SignAssembly>true</SignAssembly>
1616
<AssemblyOriginatorKeyFile>..\..\product.snk</AssemblyOriginatorKeyFile>
1717
<BaseIntermediateOutputPath>obj-MonoAndroid\</BaseIntermediateOutputPath>
18+
<LangVersion>8.0</LangVersion>
1819
</PropertyGroup>
1920
<Import Project="..\..\Configuration.props" />
2021
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
2122
<DebugSymbols>true</DebugSymbols>
2223
<DebugType>full</DebugType>
2324
<Optimize>false</Optimize>
2425
<OutputPath>..\..\bin\Debug</OutputPath>
25-
<DefineConstants>DEBUG;INTEROP;FEATURE_JNIENVIRONMENT_JI_PINVOKES;FEATURE_JNIOBJECTREFERENCE_INTPTRS</DefineConstants>
26+
<DefineConstants>DEBUG;INTEROP;FEATURE_JNIENVIRONMENT_JI_PINVOKES;FEATURE_JNIOBJECTREFERENCE_INTPTRS;INTERNAL_NULLABLE_ATTRIBUTES</DefineConstants>
2627
<ErrorReport>prompt</ErrorReport>
2728
<WarningLevel>4</WarningLevel>
2829
<ConsolePause>false</ConsolePause>
2930
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
31+
<Nullable>enable</Nullable>
3032
<DocumentationFile>..\..\bin\Debug\Java.Interop.xml</DocumentationFile>
3133
<JNIEnvGenPath>..\..\bin\BuildDebug</JNIEnvGenPath>
3234
</PropertyGroup>
@@ -37,8 +39,9 @@
3739
<ErrorReport>prompt</ErrorReport>
3840
<WarningLevel>4</WarningLevel>
3941
<ConsolePause>false</ConsolePause>
40-
<DefineConstants>INTEROP;FEATURE_JNIENVIRONMENT_JI_PINVOKES;FEATURE_JNIOBJECTREFERENCE_INTPTRS</DefineConstants>
42+
<DefineConstants>INTEROP;FEATURE_JNIENVIRONMENT_JI_PINVOKES;FEATURE_JNIOBJECTREFERENCE_INTPTRS;INTERNAL_NULLABLE_ATTRIBUTES</DefineConstants>
4143
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
44+
<Nullable>enable</Nullable>
4245
<DocumentationFile>..\..\bin\Release\Java.Interop.xml</DocumentationFile>
4346
<JNIEnvGenPath>..\..\bin\BuildRelease</JNIEnvGenPath>
4447
</PropertyGroup>

src/Java.Interop/Java.Interop.csproj

Lines changed: 31 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -2,30 +2,27 @@
22
<Project Sdk="Microsoft.NET.Sdk">
33
<PropertyGroup>
44
<TargetFramework>netstandard2.0</TargetFramework>
5-
<ProjectGuid>{94BD81F7-B06F-4295-9636-F8A3B6BDC762}</ProjectGuid>
65
<NoWarn>1591</NoWarn>
76
<SignAssembly>true</SignAssembly>
87
<AssemblyOriginatorKeyFile>..\..\product.snk</AssemblyOriginatorKeyFile>
9-
<DefineConstants>INTEROP;FEATURE_JNIENVIRONMENT_JI_PINVOKES;FEATURE_JNIOBJECTREFERENCE_INTPTRS</DefineConstants>
8+
<DefineConstants>INTEROP;FEATURE_JNIENVIRONMENT_JI_PINVOKES;FEATURE_JNIOBJECTREFERENCE_INTPTRS;INTERNAL_NULLABLE_ATTRIBUTES</DefineConstants>
109
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
1110
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
1211
</PropertyGroup>
1312
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
1413
<OutputPath>..\..\bin\Debug</OutputPath>
1514
<DocumentationFile>..\..\bin\Debug\Java.Interop.xml</DocumentationFile>
1615
<JNIEnvGenPath>..\..\bin\BuildDebug</JNIEnvGenPath>
17-
<DefineConstants>DEBUG;$(DefineConstants)</DefineConstants>
18-
</PropertyGroup>
19-
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Gendarme|AnyCPU' ">
20-
<OutputPath>..\..\bin\GendarmeDebug</OutputPath>
21-
<DefineConstants>DEBUG;$(DefineConstants)</DefineConstants>
22-
<DocumentationFile>..\..\bin\Debug\Java.Interop.xml</DocumentationFile>
23-
<JNIEnvGenPath>..\..\bin\BuildDebug</JNIEnvGenPath>
16+
<DefineConstants>$(DefineConstants);DEBUG;NETSTANDARD;NETSTANDARD2_0</DefineConstants>
17+
<LangVersion>8.0</LangVersion>
18+
<Nullable>enable</Nullable>
2419
</PropertyGroup>
2520
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
2621
<OutputPath>..\..\bin\Release</OutputPath>
2722
<DocumentationFile>..\..\bin\Release\Java.Interop.xml</DocumentationFile>
2823
<JNIEnvGenPath>..\..\bin\BuildRelease</JNIEnvGenPath>
24+
<LangVersion>8.0</LangVersion>
25+
<Nullable>enable</Nullable>
2926
</PropertyGroup>
3027
<ItemGroup>
3128
<Compile Remove="Java.Interop\JniLocationException.cs" />
@@ -64,9 +61,32 @@
6461
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
6562
</PackageReference>
6663
<ProjectReference Include="..\..\build-tools\jnienv-gen\jnienv-gen.csproj">
67-
<Project>{6410DA0F-5E14-4FC0-9AEE-F4C542C96C7A}</Project>
68-
<Name>jnienv-gen</Name>
6964
<ReferenceOutputAssembly>False</ReferenceOutputAssembly>
7065
</ProjectReference>
7166
</ItemGroup>
67+
<ItemGroup>
68+
<Compile Condition=" '$(EnableDefaultCompileItems)' == 'true' " Update="Java.Interop\JavaPrimitiveArrays.cs">
69+
<DependentUpon>JavaPrimitiveArrays.tt</DependentUpon>
70+
</Compile>
71+
<Compile Condition=" '$(EnableDefaultCompileItems)' == 'true' " Update="Java.Interop\JniBuiltinMarshalers.cs">
72+
<DependentUpon>JniBuiltinMarshalers.tt</DependentUpon>
73+
</Compile>
74+
<Compile Condition=" '$(EnableDefaultCompileItems)' == 'true' " Update="Java.Interop\JniPeerMembers.JniFields.cs">
75+
<DependentUpon>JniPeerMembers.JniFields.tt</DependentUpon>
76+
</Compile>
77+
<Compile Condition=" '$(EnableDefaultCompileItems)' == 'true' " Update="Java.Interop\JniPeerMembers.JniInstanceMethods_Invoke.cs">
78+
<DependentUpon>JniPeerMembers.JniInstanceMethods_Invoke.tt</DependentUpon>
79+
</Compile>
80+
</ItemGroup>
81+
<ProjectExtensions>
82+
<MonoDevelop>
83+
<Properties>
84+
<Policies>
85+
<VersionControlPolicy>
86+
<CommitMessageStyle Indent="&#x9;" LineAlign="0" IncludeDirectoryPaths="True" />
87+
</VersionControlPolicy>
88+
</Policies>
89+
</Properties>
90+
</MonoDevelop>
91+
</ProjectExtensions>
7292
</Project>

src/Java.Interop/Java.Interop/IJavaPeerable.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
#nullable enable
2+
13
using System;
24

35
namespace Java.Interop

src/Java.Interop/Java.Interop/JavaArray.cs

Lines changed: 22 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,10 @@
1+
#nullable enable
2+
13
using System;
24
using System.Collections;
35
using System.Collections.Generic;
46
using System.Diagnostics;
7+
using System.Diagnostics.CodeAnalysis;
58
using System.Linq;
69
using System.Reflection;
710

@@ -25,6 +28,7 @@ public int Length {
2528
get {return JniEnvironment.Arrays.GetArrayLength (PeerReference);}
2629
}
2730

31+
[MaybeNull]
2832
public abstract T this [int index] {
2933
get;
3034
set;
@@ -56,7 +60,9 @@ public virtual IEnumerator<T> GetEnumerator ()
5660
{
5761
int len = Length;
5862
for (int i = 0; i < len; ++i)
63+
#pragma warning disable CS8603 // Possible null reference return.
5964
yield return this [i];
65+
#pragma warning restore CS8603 // Possible null reference return.
6066
}
6167

6268
internal static void CheckArrayCopy (int sourceIndex, int sourceLength, int destinationIndex, int destinationLength, int length)
@@ -98,7 +104,7 @@ internal static IList<T> ToList (IEnumerable<T> value)
98104
return value.ToList ();
99105
}
100106

101-
internal IList<T> ToTargetType (Type targetType, bool dispose)
107+
internal IList<T> ToTargetType (Type? targetType, bool dispose)
102108
{
103109
if (TargetTypeIsCurrentType (targetType))
104110
return this;
@@ -113,26 +119,27 @@ internal IList<T> ToTargetType (Type targetType, bool dispose)
113119
throw CreateMarshalNotSupportedException (GetType (), targetType);
114120
}
115121

116-
internal virtual bool TargetTypeIsCurrentType (Type targetType)
122+
internal virtual bool TargetTypeIsCurrentType ([NotNullWhen (false)]Type? targetType)
117123
{
118124
return targetType == null || targetType == typeof (JavaArray<T>);
119125
}
120126

121-
internal static Exception CreateMarshalNotSupportedException (Type sourceType, Type targetType)
127+
internal static Exception CreateMarshalNotSupportedException (Type sourceType, Type? targetType)
122128
{
123-
throw new NotSupportedException (
124-
string.Format ("Do not know how to marshal a '{0}' into a '{1}'.",
125-
sourceType.FullName, targetType.FullName));
129+
return new NotSupportedException (
130+
string.Format ("Do not know how to marshal a `{0}`{1}.",
131+
sourceType.FullName,
132+
targetType != null ? $" into a `{targetType.FullName}`" : ""));
126133
}
127134

128-
internal static IList<T> CreateValue<TArray> (ref JniObjectReference reference, JniObjectReferenceOptions transfer, Type targetType, ArrayCreator<TArray> creator)
135+
internal static IList<T> CreateValue<TArray> (ref JniObjectReference reference, JniObjectReferenceOptions transfer, Type? targetType, ArrayCreator<TArray> creator)
129136
where TArray : JavaArray<T>
130137
{
131138
return creator (ref reference, transfer)
132139
.ToTargetType (targetType, dispose: true);
133140
}
134141

135-
internal static JniValueMarshalerState CreateArgumentState<TArray> (IList<T> value, ParameterAttributes synchronize, Func<IList<T>, bool, TArray> creator)
142+
internal static JniValueMarshalerState CreateArgumentState<TArray> (IList<T>? value, ParameterAttributes synchronize, Func<IList<T>, bool, TArray> creator)
136143
where TArray : JavaArray<T>
137144
{
138145
if (value == null)
@@ -149,10 +156,10 @@ internal static JniValueMarshalerState CreateArgumentState<TArray> (IList<T
149156
return new JniValueMarshalerState (a);
150157
}
151158

152-
internal static void DestroyArgumentState<TArray> (IList<T> value, ref JniValueMarshalerState state, ParameterAttributes synchronize)
159+
internal static void DestroyArgumentState<TArray> (IList<T>? value, ref JniValueMarshalerState state, ParameterAttributes synchronize)
153160
where TArray : JavaArray<T>
154161
{
155-
var source = (TArray) state.PeerableValue;
162+
var source = (TArray?) state.PeerableValue;
156163
if (source == null)
157164
return;
158165

@@ -187,7 +194,9 @@ internal virtual void CopyToList (IList<T> list, int index)
187194
{
188195
int len = Length;
189196
for (int i = 0; i < len; i++) {
197+
#pragma warning disable CS8601 // Possible null reference assignment.
190198
list [index + i] = this [i];
199+
#pragma warning restore CS8601 // Possible null reference assignment.
191200
}
192201
}
193202

@@ -217,9 +226,11 @@ bool IList.IsFixedSize {
217226
}
218227
}
219228

220-
object IList.this [int index] {
229+
object? IList.this [int index] {
221230
get {return this [index];}
231+
#pragma warning disable 8601
222232
set {this [index] = (T) value;}
233+
#pragma warning restore 8601
223234
}
224235

225236
void ICollection.CopyTo (Array array, int index)

src/Java.Interop/Java.Interop/JavaException.cs

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
#nullable enable
2+
13
using System;
24

35
namespace Java.Interop
@@ -8,7 +10,7 @@ unsafe public class JavaException : Exception, IJavaPeerable
810
internal const string JniTypeName = "java/lang/Throwable";
911
readonly static JniPeerMembers _members = new JniPeerMembers (JniTypeName, typeof (JavaException));
1012

11-
public string JavaStackTrace { get; private set; }
13+
public string? JavaStackTrace { get; private set; }
1214
public int JniIdentityHashCode { get; private set; }
1315
public JniManagedPeerStates JniManagedPeerState { get; private set; }
1416

@@ -177,7 +179,7 @@ public override unsafe int GetHashCode ()
177179
return _members.InstanceMethods.InvokeVirtualInt32Method ("hashCode.()I", this, null);
178180
}
179181

180-
static string GetMessage (ref JniObjectReference reference, JniObjectReferenceOptions transfer)
182+
static string? GetMessage (ref JniObjectReference reference, JniObjectReferenceOptions transfer)
181183
{
182184
if (transfer == JniObjectReferenceOptions.None)
183185
return null;
@@ -187,7 +189,7 @@ static string GetMessage (ref JniObjectReference reference, JniObjectReferenceOp
187189
return JniEnvironment.Strings.ToString (ref s, JniObjectReferenceOptions.CopyAndDispose);
188190
}
189191

190-
static Exception GetCause (ref JniObjectReference reference, JniObjectReferenceOptions transfer)
192+
static Exception? GetCause (ref JniObjectReference reference, JniObjectReferenceOptions transfer)
191193
{
192194
if (transfer == JniObjectReferenceOptions.None)
193195
return null;
@@ -197,7 +199,7 @@ static Exception GetCause (ref JniObjectReference reference, JniObjectReferenceO
197199
return JniEnvironment.Runtime.GetExceptionForThrowable (ref e, JniObjectReferenceOptions.CopyAndDispose);
198200
}
199201

200-
unsafe string GetJavaStack (JniObjectReference handle)
202+
unsafe string? GetJavaStack (JniObjectReference handle)
201203
{
202204
using (var StringWriter_class = new JniType ("java/io/StringWriter"))
203205
using (var PrintWriter_class = new JniType ("java/io/PrintWriter")) {

src/Java.Interop/Java.Interop/JavaObject.cs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
#nullable enable
2+
13
using System;
24

35
namespace Java.Interop
@@ -125,7 +127,7 @@ public override unsafe int GetHashCode ()
125127
return _members.InstanceMethods.InvokeVirtualInt32Method ("hashCode.()I", this, null);
126128
}
127129

128-
public override unsafe string ToString ()
130+
public override unsafe string? ToString ()
129131
{
130132
var lref = _members.InstanceMethods.InvokeVirtualObjectMethod (
131133
"toString.()Ljava/lang/String;",

0 commit comments

Comments
 (0)