Skip to content

Commit 0f1efeb

Browse files
authored
[Java.Interop] Use PublicApiAnalyzers to ensure we do not break API (#1170)
Fixes: #1169 Context: dotnet/android@76ab8b2 dotnet/android@76ab8b2c mentions: > Now that we are in the .NET `TargetFramework` world we also need > to ensure we do not *add* any new API to a Target Framework once it > has shipped. A `TargetFramework` is essentially a contract that we > cannot change. (Imagine if you had different minor versions of .NET > on your local machine and CI machine, what works on one should work > on the other.) This prevents an issue where a user on `.NET 8.0.300` uses a method that isn't available to their coworker or CI on `.NET 8.0.100`. This logical argument also applies to `Java.Interop.dll`. Enable Microsoft's [PublicApiAnalyzers][0] for `Java.Interop.dll`. ([PublicApiAnalyzers documentation][1].) This ensures that we don't add new API once we've shipped `Java.Interop.dll` for a given .NET version. Update `build-tools/jnienv-gen` so that `JniEnvironment.g.cs` enables nullable reference types. This allows us to *avoid* disabling RS0041. Co-authored-by: Jonathan Pryor <jonpryor@vt.edu> [0]: https://github.com/dotnet/roslyn-analyzers/tree/ace28a1039d09626a94d3b0f8ce69e547ca2bcbf/src/PublicApiAnalyzers [1]: https://github.com/dotnet/roslyn-analyzers/blob/ace28a1039d09626a94d3b0f8ce69e547ca2bcbf/src/PublicApiAnalyzers/PublicApiAnalyzers.Help.md
1 parent 8b85462 commit 0f1efeb

File tree

8 files changed

+2017
-1090
lines changed

8 files changed

+2017
-1090
lines changed

build-tools/jnienv-gen/Generator.cs

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,7 @@ static void GenerateFile (TextWriter o)
8686
o.WriteLine ("// Generated file; DO NOT EDIT!");
8787
o.WriteLine ("//");
8888
o.WriteLine ("// To make changes, edit monodroid/tools/jnienv-gen-interop and rerun");
89+
o.WriteLine ("#nullable enable");
8990
o.WriteLine ();
9091
o.WriteLine ("#if !FEATURE_JNIENVIRONMENT_SAFEHANDLES && !FEATURE_JNIENVIRONMENT_JI_INTPTRS && !FEATURE_JNIENVIRONMENT_JI_PINVOKES && !FEATURE_JNIENVIRONMENT_XA_INTPTRS && !FEATURE_JNIENVIRONMENT_JI_FUNCTION_POINTERS");
9192
o.WriteLine ("#define FEATURE_JNIENVIRONMENT_JI_PINVOKES");
@@ -255,7 +256,7 @@ static void GenerateJniNativeInterfaceInvoker (TextWriter o, HandleStyle style)
255256
if (e.Prebind)
256257
o.WriteLine ("\t\tpublic readonly {0} {1};\n", d, e.Name);
257258
else {
258-
o.WriteLine ("\t\t{0} _{1};", d, e.Name);
259+
o.WriteLine ("\t\t{0}? _{1};", d, e.Name);
259260
o.WriteLine ("\t\tpublic {0} {1} {{", d, e.Name);
260261
o.WriteLine ("\t\t\tget {");
261262
o.WriteLine ("\t\t\t\tif (_{0} == null)\n\t\t\t\t\t{1}", e.Name, Initialize (e, "_", d));
@@ -551,7 +552,7 @@ static void RaiseException (TextWriter o, JniFunction entry, HandleStyle style)
551552
return;
552553

553554
o.WriteLine ();
554-
o.WriteLine ("\t\t\tException __e = JniEnvironment.GetExceptionForLastThrowable ({0});",
555+
o.WriteLine ("\t\t\tException? __e = JniEnvironment.GetExceptionForLastThrowable ({0});",
555556
(style == HandleStyle.JIIntPtrPinvokeWithErrors || style == HandleStyle.JIFunctionPtrWithErrors)
556557
? "thrown"
557558
: "");
@@ -1127,7 +1128,7 @@ public override string[] GetMarshalToManagedStatements (HandleStyle style, strin
11271128
case HandleStyle.JIFunctionPtrWithErrors:
11281129
return new[] {
11291130
string.Format ("if ({0} == IntPtr.Zero)", variable),
1130-
string.Format ("\treturn null;"),
1131+
string.Format ($"\tthrow new InvalidOperationException (\"Should not be reached; `{entry.Name}` should have thrown!\");"),
11311132
string.Format ("return new {0} ({1}, {2}, {3}, isStatic: {4});", type, entry.Parameters [1].Name, entry.Parameters [2].Name, variable, IsStatic ? "true" : "false"),
11321133
};
11331134
case HandleStyle.XAIntPtr:

src/Java.Interop/Java.Interop.csproj

Lines changed: 4 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -88,18 +88,11 @@
8888
<DependentUpon>JniPeerMembers.JniInstanceMethods_Invoke.tt</DependentUpon>
8989
</Compile>
9090
</ItemGroup>
91-
<ProjectExtensions>
92-
<MonoDevelop>
93-
<Properties>
94-
<Policies>
95-
<VersionControlPolicy>
96-
<CommitMessageStyle Indent="&#x9;" LineAlign="0" IncludeDirectoryPaths="True" />
97-
</VersionControlPolicy>
98-
</Policies>
99-
</Properties>
100-
</MonoDevelop>
101-
</ProjectExtensions>
10291
<ItemGroup>
92+
<PackageReference Include="Microsoft.CodeAnalysis.PublicApiAnalyzers" Version="3.3.4">
93+
<PrivateAssets>all</PrivateAssets>
94+
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
95+
</PackageReference>
10396
<PackageReference Include="Microsoft.SourceLink.GitHub" PrivateAssets="All" />
10497
</ItemGroup>
10598
<Import Project="Java.Interop.targets" />

src/Java.Interop/Java.Interop/JavaObjectArray.cs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ public class JavaObjectArray<T> : JavaArray<T>
1111
{
1212
internal static readonly ValueMarshaler Instance = new ValueMarshaler ();
1313

14+
[SuppressMessage ("ApiDesign", "RS0022:Constructor make noninheritable base class inheritable", Justification = "Existing public API")]
1415
public JavaObjectArray (ref JniObjectReference handle, JniObjectReferenceOptions options)
1516
: base (ref handle, options)
1617
{
@@ -29,20 +30,23 @@ static JniObjectReference NewArray (int length)
2930
}
3031
}
3132

33+
[SuppressMessage ("ApiDesign", "RS0022:Constructor make noninheritable base class inheritable", Justification = "Existing public API")]
3234
public unsafe JavaObjectArray (int length)
3335
: this (ref *InvalidJniObjectReference, JniObjectReferenceOptions.None)
3436
{
3537
var peer = NewArray (CheckLength (length));
3638
Construct (ref peer, JniObjectReferenceOptions.CopyAndDispose);
3739
}
3840

41+
[SuppressMessage ("ApiDesign", "RS0022:Constructor make noninheritable base class inheritable", Justification = "Existing public API")]
3942
public JavaObjectArray (IList<T> value)
4043
: this (CheckLength (value))
4144
{
4245
for (int i = 0; i < value.Count; ++i)
4346
SetElementAt (i, value [i]);
4447
}
4548

49+
[SuppressMessage ("ApiDesign", "RS0022:Constructor make noninheritable base class inheritable", Justification = "Existing public API")]
4650
public JavaObjectArray (IEnumerable<T> value)
4751
: this (ToList (value))
4852
{

src/Java.Interop/Java.Interop/JniEnvironment.Types.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -262,7 +262,7 @@ public static void RegisterNatives (JniObjectReference type, JniNativeMethodRegi
262262
}
263263
#endif // DEBUG && NETCOREAPP
264264

265-
int r = _RegisterNatives (type, methods, numMethods);
265+
int r = _RegisterNatives (type, methods ?? Array.Empty<JniNativeMethodRegistration>(), numMethods);
266266

267267
if (r != 0) {
268268
throw new InvalidOperationException (

src/Java.Interop/Java.Interop/JniValueMarshaler.cs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@ public struct JniValueMarshalerState : IEquatable<JniValueMarshalerState> {
4949
public IJavaPeerable? PeerableValue {get; private set;}
5050
public object? Extra {get; private set;}
5151

52+
[SuppressMessage ("ApiDesign", "RS0026:Do not add multiple public overloads with optional parameters", Justification = "Existing public API")]
5253
public JniValueMarshalerState (JniArgumentValue jniArgumentValue, object? extra = null)
5354
{
5455
JniArgumentValue = jniArgumentValue;
@@ -57,6 +58,7 @@ public JniValueMarshalerState (JniArgumentValue jniArgumentValue, object? extra
5758
Extra = extra;
5859
}
5960

61+
[SuppressMessage ("ApiDesign", "RS0026:Do not add multiple public overloads with optional parameters", Justification = "Existing public API")]
6062
public JniValueMarshalerState (JniObjectReference referenceValue, object? extra = null)
6163
{
6264
JniArgumentValue = new JniArgumentValue (referenceValue);
@@ -65,6 +67,7 @@ public JniValueMarshalerState (JniObjectReference referenceValue, object? extra
6567
Extra = extra;
6668
}
6769

70+
[SuppressMessage ("ApiDesign", "RS0026:Do not add multiple public overloads with optional parameters", Justification = "Existing public API")]
6871
public JniValueMarshalerState (IJavaPeerable? peerableValue, object? extra = null)
6972
{
7073
PeerableValue = peerableValue;

0 commit comments

Comments
 (0)