Skip to content
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

[Java.Interop] Fix JNI signature generating #249

Merged
merged 2 commits into from
Jan 9, 2018

Conversation

radekdoulik
Copy link
Member

Commit
cb161d2
introduced a regression described in
dotnet/android#1149 (comment)

The signature of the InitializeCreator method was wrongly calculated
as ()Ljava/lang/Object; instead of original
()Lmd59b7a7576784821ea63294fbca8600da1/Creator_1;

The reason for that is, that
cb161d2#diff-9e153905b02dbe50c0e0b874ba40bf3eR262
started using IEnumerable<T>::OfType<T>, which traverses
inheritance, unlike the original Type::GetCustomAttributes called
with inherit = false.

Thus for the Creator<Player> we got the RegisterAttribute from the
parent type Java.Lang.Object which led to non-null result from
ToJniNameFromAttributes method. At the end the result was used here
cb161d2#diff-9e153905b02dbe50c0e0b874ba40bf3eR361
instead of returning ToJniName (type, exportKind) result.

Commit
dotnet@cb161d2
introduced a regression described in
dotnet/android#1149 (comment)

The signature of the `InitializeCreator` method was wrongly calculated
as `()Ljava/lang/Object;` instead of original
`()Lmd59b7a7576784821ea63294fbca8600da1/Creator_1;`

The reason for that is, that
dotnet@cb161d2#diff-9e153905b02dbe50c0e0b874ba40bf3eR262
started using `IEnumerable<T>::OfType<T>`, which traverses
inheritance, unlike the original `Type::GetCustomAttributes` called
with `inherit = false`.

Thus for the `Creator<Player>` we got the `RegisterAttribute` from the
parent type `Java.Lang.Object` which led to non-null result from
`ToJniNameFromAttributes` method. At the end the result was used here
dotnet@cb161d2#diff-9e153905b02dbe50c0e0b874ba40bf3eR361
instead of returning `ToJniName (type, exportKind)` result.
@jonpryor jonpryor merged commit d8862d8 into dotnet:master Jan 9, 2018
jonpryor pushed a commit that referenced this pull request Jan 9, 2018
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.
@github-actions github-actions bot locked and limited conversation to collaborators Apr 14, 2024
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants