Skip to content

Commit eb6e3c4

Browse files
radekdoulikjonpryor
authored andcommitted
[Java.Interop] Fix JNI signature generation (#249)
Fixes: dotnet/android#1149 Commit cb161d2 broke the use of `[Java.Interop.ExportAttribute]` and `[Java.Interop.ExportFieldAttribute]`. Repro: 1. [Create a class with an `[Export]` or `[ExportField]` method][0]: partial class MyParcelable : Java.Lang.Object, IParcelable { [ExportField ("CREATOR")] public static MyParcelableCreator InitializeCreator () { /* ... */ } } 2. Launch the app, and cause the `MyParcelable` Java Callable Wrapper (JCW) to be initialized: // Will eventually cause JCW MyParcelable.class to be init'd var whatever = new MyParcelable(); Expected result: It works! Actual result: System.InvalidOperationException: Specified managed method 'InitializeCreator' was not found. Signature: ()Lmd5<<whatever>>/MyParcelableCreator; at Android.Runtime.JNIEnv.RegisterJniNatives (System.IntPtr typeName_ptr, System.Int32 typeName_len, System.IntPtr jniClass, System.IntPtr methods_ptr, System.Int32 methods_len) [0x001d2] in <36a2bb1be1a64b51b22cbef15eee5437>:0 at (wrapper managed-to-native) Java.Interop.NativeMethods.java_interop_jnienv_alloc_object(intptr,intptr&,intptr) at Java.Interop.JniEnvironment+Object.AllocObject (Java.Interop.JniObjectReference type) [0x00027] in <8e80eb3ca41f4c3eb745bd7c73458796>:0 at Java.Interop.JniType.AllocObject () [0x0000c] in <8e80eb3ca41f4c3eb745bd7c73458796>:0 at Java.Interop.JniPeerMembers+JniInstanceMethods.StartCreateInstance (System.String constructorSignature, System.Type declaringType, Java.Interop.JniArgumentValue* parameters) [0x0003e] in <8e80eb3ca41f4c3eb745bd7c73458796>:0 at Java.Lang.Object..ctor () [0x00034] in <36a2bb1be1a64b51b22cbef15eee5437>:0 ... [0]: https://github.com/xamarin/monodroid-samples/blob/f944f42001fa134915069fa7bbb9cb493bfd446d/ExportAttribute/ExportAttributeTest/MainActivity.cs#L52-L86 The `InvalidOperationException` is thrown from [`JNIEnv.RegisterJniNatives()` for `__export__` methods][reg], which requires that `JavaNativeTypeManager.GetJniSignature(MethodInfo)` return the same value as what the Java Callable Wrapper contains. [reg]: https://github.com/xamarin/xamarin-android/blob/master/src/Mono.Android/Android.Runtime/JNIEnv.cs#L172-L178 Rephrased: we require that the Reflection-based `JavaNativeTypeManager.GetJniSignature(MethodInfo)` generate identical values as the Cecil-based `JavaNativeTypeManager.GetJniSignature(MethodDefinition)`: the former used by `JNIEnv.RegisterJniNatives()`, the latter by `JavaCallableWrapperGenerator`. These *must* be consistent. The problem is, they weren't: `JavaNativeTypeManager.ToJniNameFromAttributes(Type)` -- called by `JavaNativeTypeManager.GetJniSignature(MethodInfo)` and others -- was erroneously updated to check for custom attributes on the specified `Type` *and all base classes*. Checking base classes would eventually cause `Java.Lang.Object` to be checked, causing *everything* to become `java.lang.Object` unless explicitly overridden in the inheritance chain (e.g. `Activity`, which has its own `[Register]` attribute). The result is that `JavaNativeTypeManager.GetJniSignature(MethodInfo)` for `MyParcelable.InitializeCreator()` was returning: ()Ljava/lang/Object; while `JavaNativeTypeManager.GetJniSignature(MethodDefinition)` was generating: ()Lmd5<<whatever>>/MyParcelableCreator; Because these didn't match, `InvalidOperationException` was thrown. Fix `JavaNativeTypeManager.ToJniNameFromAttributes(Type)` so that base classes are *not* searched for custom attributes, restoring prior behavior.
1 parent 57d5d51 commit eb6e3c4

File tree

1 file changed

+2
-2
lines changed

1 file changed

+2
-2
lines changed

src/Java.Interop.Tools.TypeNameMappings/Java.Interop.Tools.TypeNameMappings/JavaNativeTypeManager.cs

+2-2
Original file line numberDiff line numberDiff line change
@@ -261,8 +261,8 @@ static string GetSpecialExportJniType (string typeName, ExportParameterKind expo
261261
// Keep in sync with ToJniNameFromAttributes(TypeDefinition)
262262
public static string ToJniNameFromAttributes (Type type)
263263
{
264-
var a = type.GetCustomAttributes ().OfType<IJniNameProviderAttribute> ().FirstOrDefault (j => !string.IsNullOrEmpty (j.Name));
265-
return a == null ? null : a.Name.Replace ('.', '/');
264+
var aa = (IJniNameProviderAttribute []) type.GetCustomAttributes (typeof (IJniNameProviderAttribute), inherit: false);
265+
return aa.Length > 0 && !string.IsNullOrEmpty (aa [0].Name) ? aa [0].Name.Replace ('.', '/') : null;
266266
}
267267

268268
/*

0 commit comments

Comments
 (0)