Skip to content

Commit d62008d

Browse files
authored
[Java.Interop] GetSimpleReferences(): fallback for GetTypeSignatures() (#1305)
Context: dotnet/android#9768 dotnet/android#9768 attempts to add types from `Java.Interop.dll` to the .NET for Android typemaps, in order to fix Debug warnings like: I monodroid-assembly: typemap: unable to find mapping to a Java type from managed type 'Java.Interop.ManagedPeer, Java.Interop' Unfortunately, the initial attempt to generate typemaps for `Java.Interop.dll` caused the assertion: AssertGetJniTypeInfoForType (typeof (JavaArray<JavaObject>), "[Ljava/lang/Object;", false, 1); within `Java.InteropTests.JniTypeManagerTests.GetTypeSignature_Type()` to fail with: Expected string length 19 but was 33. Strings differ at index 0. Expected: "[Ljava/lang/Object;" But was: "crc64d5d92128469ae06d/JavaArray_1" -----------^ The immediate cause of the failure is that `JniRuntime.JniTypeManager.GetTypeSignature()` called `JniRuntime.JniTypeManager.GetSimpleReference()` *before* it tried to see if the type was `JavaArray<T>`. As `Java.Interop.dll` was now being processed for typemap purposes, and because `JavaArray<T>` did not have a `[JniTypeSignatureAttribute]`, the typemap got the default behavior of `crc64[hash…]`. The broader cause is that `GetSimpleReference()` should be the *fallback* implementation, used after all other attempts to get a JNI name have failed. Update `GetTypeSignature()` and `GetTypeSignatures()` so that `GetSimpleReference()` and/or `GetSimpleReferences()` are in fact treated as fallbacks. Additionally, update `AssertGetJniTypeInfoForType()` to assert that the value returned by `GetTypeSignature()` is the same as the value return3ed by` GetTypeSignatures().First()`. This was *commented* as being the case, but we should *verify* that as well. Finally, *move* the `type.GetCustomAttribute<JniTypeSignatureAttribute()` and `GetReplacementType()` logic into `GetSimpleReferences()`. This This emphasizes the "fallback" nature of `GetSimpleReference()`, adds an missing `GetReplacementType()` invocation from the `GetTypeSignatures()` codepath, and this is what [`AndroidTypeManager.GetSimpleReference()`][0] was already doing. [0]: https://github.com/dotnet/android/blob/21c413195e300b6440eb437dade4f3a114e795f7/src/Mono.Android/Android.Runtime/AndroidRuntime.cs#L279-L289
1 parent 57f7bc8 commit d62008d

File tree

4 files changed

+48
-43
lines changed

4 files changed

+48
-43
lines changed

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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010

1111
namespace Java.Interop
1212
{
13+
[JniTypeSignature ("java/lang/Object", ArrayRank=1, GenerateJavaPeer=false)]
1314
public abstract class JavaArray<T> : JavaObject, IList, IList<T>
1415
{
1516
internal delegate TArray ArrayCreator<TArray> (ref JniObjectReference reference, JniObjectReferenceOptions transfer)
@@ -362,6 +363,7 @@ public void Dispose ()
362363
}
363364
}
364365

366+
[JniTypeSignature ("java/lang/Object", ArrayRank=1, GenerateJavaPeer=false)]
365367
public abstract class JavaPrimitiveArray<
366368
[DynamicallyAccessedMembers (Constructors)]
367369
T

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77

88
namespace Java.Interop
99
{
10+
[JniTypeSignature ("java/lang/Object", ArrayRank=1, GenerateJavaPeer=false)]
1011
public class JavaObjectArray<
1112
[DynamicallyAccessedMembers (Constructors)]
1213
T

src/Java.Interop/Java.Interop/JniRuntime.JniTypeManager.cs

Lines changed: 34 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -156,36 +156,23 @@ public JniTypeSignature GetTypeSignature (Type type)
156156
if (GetBuiltInTypeArraySignature (type, ref signature))
157157
return signature.AddArrayRank (rank);
158158

159-
var simpleRef = GetSimpleReference (type);
160-
if (simpleRef != null)
161-
return new JniTypeSignature (simpleRef, rank, false);
162-
163-
var name = type.GetCustomAttribute<JniTypeSignatureAttribute> (inherit: false);
164-
if (name != null) {
165-
#if NET
166-
var altRef = GetReplacementType (name.SimpleReference);
167-
if (altRef != null) {
168-
return new JniTypeSignature (altRef, name.ArrayRank + rank, name.IsKeyword);
169-
}
170-
#endif // NET
171-
return new JniTypeSignature (name.SimpleReference, name.ArrayRank + rank, name.IsKeyword);
172-
}
173-
174159
var isGeneric = type.IsGenericType;
175160
var genericDef = isGeneric ? type.GetGenericTypeDefinition () : type;
176161
if (isGeneric) {
177162
if (genericDef == typeof (JavaArray<>) || genericDef == typeof (JavaObjectArray<>)) {
178163
var r = GetTypeSignature (type.GenericTypeArguments [0]);
179164
return r.AddArrayRank (rank + 1);
180165
}
181-
}
182166

183-
if (isGeneric) {
184-
simpleRef = GetSimpleReference (genericDef);
185-
if (simpleRef != null)
186-
return new JniTypeSignature (simpleRef, rank, false);
167+
var genericSimpleRef = GetSimpleReference (genericDef);
168+
if (genericSimpleRef != null)
169+
return new JniTypeSignature (genericSimpleRef, rank, false);
187170
}
188171

172+
var simpleRef = GetSimpleReference (type);
173+
if (simpleRef != null)
174+
return new JniTypeSignature (simpleRef, rank, false);
175+
189176
return default;
190177
}
191178

@@ -194,44 +181,39 @@ public IEnumerable<JniTypeSignature> GetTypeSignatures (Type type)
194181
{
195182
AssertValid ();
196183

184+
if (type == null)
185+
yield break;
197186
if (type.ContainsGenericParameters)
198187
throw new ArgumentException ($"'{type}' contains a generic type definition. This is not supported.", nameof (type));
199188

200189
type = GetUnderlyingType (type, out int rank);
201190

202-
var signature = new JniTypeSignature (null);
191+
var signature = JniTypeSignature.Empty;
203192
if (GetBuiltInTypeSignature (type, ref signature))
204193
yield return signature.AddArrayRank (rank);
205194
if (GetBuiltInTypeArraySignature (type, ref signature))
206195
yield return signature.AddArrayRank (rank);
207196

208-
foreach (var simpleRef in GetSimpleReferences (type)) {
209-
if (simpleRef == null)
210-
continue;
211-
yield return new JniTypeSignature (simpleRef, rank, false);
212-
}
213-
214-
var name = type.GetCustomAttribute<JniTypeSignatureAttribute> (inherit: false);
215-
if (name != null) {
216-
yield return new JniTypeSignature (name.SimpleReference, name.ArrayRank + rank, name.IsKeyword);
217-
}
218-
219-
var isGeneric = type.IsGenericType;
220-
var genericDef = isGeneric ? type.GetGenericTypeDefinition () : type;
197+
var isGeneric = type.IsGenericType;
198+
var genericDef = isGeneric ? type.GetGenericTypeDefinition () : type;
221199
if (isGeneric) {
222-
if (genericDef == typeof(JavaArray<>) || genericDef == typeof(JavaObjectArray<>)) {
200+
if (genericDef == typeof (JavaArray<>) || genericDef == typeof (JavaObjectArray<>)) {
223201
var r = GetTypeSignature (type.GenericTypeArguments [0]);
224202
yield return r.AddArrayRank (rank + 1);
225203
}
226-
}
227204

228-
if (isGeneric) {
229-
foreach (var simpleRef in GetSimpleReferences (genericDef)) {
230-
if (simpleRef == null)
205+
foreach (var genericSimpleRef in GetSimpleReferences (genericDef)) {
206+
if (genericSimpleRef == null)
231207
continue;
232-
yield return new JniTypeSignature (simpleRef, rank, false);
208+
yield return new JniTypeSignature (genericSimpleRef, rank, false);
233209
}
234210
}
211+
212+
foreach (var simpleRef in GetSimpleReferences (type)) {
213+
if (simpleRef == null)
214+
continue;
215+
yield return new JniTypeSignature (simpleRef, rank, false);
216+
}
235217
}
236218

237219
static Type GetUnderlyingType (Type type, out int rank)
@@ -266,7 +248,18 @@ protected virtual IEnumerable<string> GetSimpleReferences (Type type)
266248
throw new ArgumentNullException (nameof (type));
267249
if (type.IsArray)
268250
throw new ArgumentException ("Array type '" + type.FullName + "' is not supported.", nameof (type));
269-
return EmptyStringArray;
251+
252+
var name = type.GetCustomAttribute<JniTypeSignatureAttribute> (inherit: false);
253+
if (name != null) {
254+
var altRef = GetReplacementType (name.SimpleReference);
255+
if (altRef != null) {
256+
yield return altRef;
257+
} else {
258+
yield return name.SimpleReference;
259+
}
260+
}
261+
262+
yield break;
270263
}
271264

272265
static readonly string[] EmptyStringArray = Array.Empty<string> ();

tests/Java.Interop-Tests/Java.Interop/JniTypeManagerTests.cs

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -79,15 +79,24 @@ public void GetTypeSignature_Type ()
7979

8080
#if !__ANDROID__
8181
// Re-enable once typemap files contain `JavaObject` subclasses, not just Java.Lang.Object subclasses
82+
//
83+
// Note: dotnet/android@5c23bcda updates Java.Lang.Object to inherit JavaObject; this is not enough,
84+
// as `<GenerateJavaStubs/>` only processes assemblies if they reference Mono.Android.dll.
8285
AssertGetJniTypeInfoForType (typeof (GenericHolder<int>), GenericHolder<int>.JniTypeName, false, 0);
8386
#endif // !__ANDROID__
8487
}
8588

8689
static void AssertGetJniTypeInfoForType (Type type, string jniType, bool isKeyword, int arrayRank)
8790
{
8891
var info = JniRuntime.CurrentRuntime.TypeManager.GetTypeSignature (type);
89-
Assert.AreEqual (jniType, info.Name);
90-
Assert.AreEqual (arrayRank, info.ArrayRank);
92+
93+
// `GetTypeSignature() and `GetTypeSignatures()` should be "in sync"; verify that!
94+
var info2 = JniRuntime.CurrentRuntime.TypeManager.GetTypeSignatures (type).FirstOrDefault ();
95+
96+
Assert.AreEqual (jniType, info.Name, $"info.Name for `{type}`");
97+
Assert.AreEqual (jniType, info2.Name, $"info.Name for `{type}`");
98+
Assert.AreEqual (arrayRank, info.ArrayRank, $"info.ArrayRank for `{type}`");
99+
Assert.AreEqual (arrayRank, info2.ArrayRank, $"info.ArrayRank for `{type}`");
91100
}
92101

93102
[Test]

0 commit comments

Comments
 (0)