-
Notifications
You must be signed in to change notification settings - Fork 60
Comparing changes
Open a pull request
base repository: dotnet/java-interop
base: 8b85462e5f304d9aa2d91c02966d7dc9751516c7
head repository: dotnet/java-interop
compare: 07c73009571d3b5d36ae79d0d4d69a02062dd6e8
- 7 commits
- 60 files changed
- 4 contributors
Commits on Dec 1, 2023
-
[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
Configuration menu - View commit details
-
Copy full SHA for 0f1efeb - Browse repository at this point
Copy the full SHA 0f1efebView commit details
Commits on Dec 2, 2023
-
[Java.Interop] Avoid
Type.GetType()
inManagedPeer
(#1168)Fixes: #1165 Context: #1153 Context: #1157 Context: f60906c When building for NativeAOT (#1153) or when building .NET Android apps with `-p:IsAotcompatible=true` (#1157), we get [IL2057][0] warnings from `ManagedPeer.cs`: ManagedPeer.cs(93,19,93,112): warning IL2057: Unrecognized value passed to the parameter 'typeName' of method 'System.Type.GetType(String, Boolean)'. It's not possible to guarantee the availability of the target type. ManagedPeer.cs(156,18,156,65): warning IL2057: Unrecognized value passed to the parameter 'typeName' of method 'System.Type.GetType(String, Boolean)'. It's not possible to guarantee the availability of the target type. ManagedPeer.cs(198,35,198,92): warning IL2057: Unrecognized value passed to the parameter 'typeName' of method 'System.Type.GetType(String, Boolean)'. It's not possible to guarantee the availability of the target type. These warnings are because `ManagedPeer.Construct()` and `ManagedPeer.RegisterNativeMembers()` use `Type.GetType()` on string values provided *from Java code*, and thus the IL trimmer does not have visibility into those strings, and thus cannot reliably determine which types need to be preserved: // Java Callable Wrapper /* partial */ class ManagedType { public static final String __md_methods; static { __md_methods = "n_GetString:()Ljava/lang/String;:__export__\n" + ""; net.dot.jni.ManagedPeer.registerNativeMembers ( /* nativeClass */ ManagedType.class, /* assemblyQualifiedName */ "Example.ManagedType, Hello-NativeAOTFromJNI", /* methods */ __md_methods); } public ManagedType (int p0) { super (); if (getClass () == ManagedType.class) { net.dot.jni.ManagedPeer.construct ( /* self */ this, /* assemblyQualifiedName */ "Example.ManagedType, Hello-NativeAOTFromJNI", /* constructorSignature */ "System.Int32, System.Runtime", /* arguments */ new java.lang.Object[] { p0 }); } } } `ManagedPeer.construct()` passes *two* sets of assembly-qualified type names: `assemblyQualifiedName` contains the type to construct, while `constructorSignature` contains a `:`-separated list of assembly-qualified type names for the constructor parameters. Each of these are passed to `Type.GetType()`. `ManagedPeer.registerNativeMembers()` passes an assembly-qualified type name to `ManagedPeer.RegisterNativeMembers()`, which passes the assembly-qualified type name to `Type.GetType()` to find the type to register native methods for. If we more strongly rely on JNI signatures, we can remove the need for Java Callable Wrappers to contain assembly-qualified type names entirely, thus removing the need for `ManagedPeer` to use `Type.GetType()`, removing the IL2057 warnings. For `ManagedPeer.construct()`, `assemblyQualifiedName` can be replaced with getting the JNI type signature from `self.getClass()`, and `constructorSignature` can be replaced with a *JNI method signature* of the calling constructor. For `ManagedPeer.registerNativeMembers()`, `assemblyQualifiedName` can be replaced with getting the JNI type signature from `nativeClass`. `jcw-gen --codegen-target=JavaInterop1` output becomes: // New JavaInterop1 Java Callable Wrapper /* partial */ class ManagedType { public static final String __md_methods; static { __md_methods = "n_GetString:()Ljava/lang/String;:__export__\n" + ""; net.dot.jni.ManagedPeer.registerNativeMembers ( /* nativeClass */ ManagedType.class, /* methods */ __md_methods); } public ManagedType (int p0) { super (); if (getClass () == ManagedType.class) { net.dot.jni.ManagedPeer.construct ( /* self */ this, /* constructorSignature */ "(I)V", /* arguments */ new java.lang.Object[] { p0 }); } } } This does not alter `jcw-gen --codegen-target=XAJavaInterop1` output; .NET Android will continue to require `Type.GetType()` calls within xamarin/xamarin-android, e.g. [`AndroidTypeManager.RegisterNativeMembers()`][2]. Furthermore, if we add `[DynamicallyAccessedMembers]` to `JniRuntime.JniTypeManager.GetType()`, we can fix some [IL2075][1] warnings which appeared after fixing the IL2057 warnings. Aside: Excising assembly-qualified type names from Java Callable Wrappers had some "interesting" knock-on effects in the unit tests, requiring that more typemap information be explicitly provided. (This same information was *implicitly* provided before, via the provision of assembly-qualified type names everywhere…) One problem with the approach of using JNI signatures instead of using assembly-qualified names is *ambiguity*: there can be multiple managed types which correspond to a given JNI signature. Consider the JNI signature `[I`, which is a Java `int[]`. This is bound as: * C# `int[]` * `JavaArray<int>` * `JavaPrimitiveArray<int>` * `JavaInt32Array` How do we know which to use? Using assembly-qualified type names for constructor parameters nicely solved this issue, but if we're not using them anymore… Update `JavaCallableExample` to demonstrate this: partial class JavaCallableExample { [JavaCallableConstructor(SuperConstructorExpression="")] public JavaCallableExample (int[] a, JavaInt32Array b); } The intention is twofold: 1. This should result in a Java Callable Wrapper constructor with signature `JavaCallableExample(int[] p0, int[] p1)`, and 2. Java code should be able to invoke this constructor. Turns out, neither of these worked when `Type.GetType()` is not used for constructor argument lookup: `JavaCallableWrapperGenerator` didn't fully support e.g. `[JniTypeSignature("I", ArrayRank=1)]` (present on `JavaInt32Array`), so it didn't know what to do with the `JavaInt32Array` parameter. Once (1) was fixed, (2) would fail because `JniRuntime.JniTypeManager.GetType(JniTypeSignature.Parse("[I"))` would return `JavaPrimitiveArray<int>`, which wasn't used in `JavaCallableExample`, resulting in: System.NotSupportedException : Unable to find constructor Java.InteropTests.JavaCallableExample(Java.Interop.JavaPrimitiveArray`1[[System.Int32, System.Private.CoreLib, Version=7.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e]], Java.Interop.JavaPrimitiveArray`1[[System.Int32, System.Private.CoreLib, Version=7.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e]]). Please provide the missing constructor. ----> Java.Interop.JniLocationException : Exception of type 'Java.Interop.JniLocationException' was thrown. Stack Trace: at Java.Interop.ManagedPeer.GetConstructor(JniTypeManager typeManager, Type type, String signature, Type[]& parameterTypes) at Java.Interop.ManagedPeer.Construct(IntPtr jnienv, IntPtr klass, IntPtr n_self, IntPtr n_constructorSignature, IntPtr n_constructorArguments) … --- End of managed Java.Interop.JavaException stack trace --- java.lang.Throwable at net.dot.jni.ManagedPeer.construct(Native Method) at net.dot.jni.test.JavaCallableExample.<init>(JavaCallableExample.java:32) at net.dot.jni.test.UseJavaCallableExample.test(UseJavaCallableExample.java:8) The constructor couldn't be found because `JniRuntime.JniTypeManager.GetTypes()` was incomplete, which is a longstanding limitation from f60906c: for `[I`, it would only return `JavaPrimitiveArray<int>` and `int[]`, in that order. Fix both of these. `JniRuntime.JniTypeManager.GetTypes(JniTypeSignature.Parse("[I"))` will now include: * `JavaArray<int>` * `JavaPrimitiveArray<int>` * `JavaInt32Array` * `int[]` This now allows the `JavaCallableExample` constructor to be invoked from Java. Because `ManagedPeer.Construct()` is now doing so much extra work in order to find the `ConstructorInfo` to invoke, cache the lookups. (Technically this is a "memory leak," as cache entries are never removed.) Finally, update `CecilCompilerExpressionVisitor` to emit `newobj` in certain `VisitNew()` invocations. This was needed while trying: partial class JavaCallableExample { [JavaCallable ("getA")] public int[] GetA() => this.a; } in order to fix the IL error: % $HOME/.dotnet/tools/ilverify bin/TestDebug-net7.0/Java.Interop.Export-Tests.dll \ --tokens --system-module System.Private.CoreLib \ -r 'bin/TestDebug-net7.0/*.dll' \ -r '/usr/local/share/dotnet/shared/Microsoft.NETCore.App/7.0.10/*.dll' [IL]: Error [StackUnderflow]: […/bin/TestDebug-net7.0/Java.Interop.Export-Tests.dll : .__<$>_jni_marshal_methods::n_GetA(native int, native int)][offset 0x0000002F] Stack underflow. Unfortunately, even after the above fix invalid IL was generated during `jnimarshalmethod-gen` processing, which will be investigated later. [0]: https://learn.microsoft.com/en-us/dotnet/core/deploying/trimming/trim-warnings/IL2057 [1]: https://learn.microsoft.com/en-us/dotnet/core/deploying/trimming/trim-warnings/il2075 [2]: https://github.com/xamarin/xamarin-android/blob/main/src/Mono.Android/Android.Runtime/AndroidRuntime.cs#L481-L577
Configuration menu - View commit details
-
Copy full SHA for 005c914 - Browse repository at this point
Copy the full SHA 005c914View commit details -
Bump to xamarin/xamarin-android-tools/main@4889bf0 (#1172)
Changes: dotnet/android-tools@8d38281...4889bf0 * dotnet/android-tools@4889bf0: [MSBuildReferences.projitems] Require opt-in to use `Microsoft.Build` (dotnet/android-tools#220) * dotnet/android-tools@21de3d7: [build] update $(MSBuildPackageReferenceVersion) to 17.6.3 (dotnet/android-tools#221) * dotnet/android-tools@08a6990: Bump android-sdk NDK version to 26.1.10909125 * dotnet/android-tools@6ae1f2a: Bump android-sdk build-tool version to 34.0.0 * dotnet/android-tools@184b6b3: Bump android-sdk cmdline-tools to version 11.0 * dotnet/android-tools@1365e33: Bump android-sdk platforms-tools to version 34.0.5 Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Configuration menu - View commit details
-
Copy full SHA for 473ef74 - Browse repository at this point
Copy the full SHA 473ef74View commit details
Commits on Dec 11, 2023
-
[invocation-overhead] Add generated source files (#1175)
Building the `Java.Interop.sln` solution always produces some generated files that `git` wants to try to commit: Changes not staged for commit: (use "git add <file>..." to update what will be committed) (use "git restore <file>..." to discard changes in working directory) modified: tests/invocation-overhead/jni.cs Untracked files: (use "git add <file>..." to include in what will be committed) tests/invocation-overhead/jni-api.h tests/invocation-overhead/jni.c Commit these generated files, and update the generator for `jni.cs` to always use native platform new lines so it does not show up as modified on Windows.
Configuration menu - View commit details
-
Copy full SHA for d5afa0a - Browse repository at this point
Copy the full SHA d5afa0aView commit details
Commits on Jan 10, 2024
-
Context: https://devdiv.visualstudio.com/DevDiv/_wiki/wikis/DevDiv.wiki/25351/APIScan-step-by-step-guide-to-setting-up-a-Pipeline The ApiScan task has been added to pipeline runs against `main`. This task should help us identify related issues earlier, rather than having to wait for a full scan of VS.
Configuration menu - View commit details
-
Copy full SHA for def5bc0 - Browse repository at this point
Copy the full SHA def5bc0View commit details
Commits on Jan 26, 2024
-
Bump to xamarin/xamarin-android-tools/main@ed102fc (#1182)
Changes: dotnet/android-tools@4889bf0...ed102fc * dotnet/android-tools@ed102fc: [Xamarin.Android.Tools.Versions] Add JavaSdkVersion (#226) * dotnet/android-tools@b175674: [ci] Only enable CodeQL on Windows build job (#224) * dotnet/android-tools@2a2e64b: [ci] Add API Scan job (#225) Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Configuration menu - View commit details
-
Copy full SHA for d529f3b - Browse repository at this point
Copy the full SHA d529f3bView commit details
Commits on Feb 2, 2024
-
[Java.Interop] Typemap support for JavaObject &
[JniTypeSignature]
(#……1181) Context: dotnet/android#8543 Context: dotnet/android#8625 Context: dotnet/android#8681 Context: #1168 Context: def5bc0 Context: 005c914 dotnet/android#8543 tested PR #1168, was Totally Green™ -- finding no issues -- and so we merged PR #1168 into 005c914. Enter dotnet/android#8625, which bumps xamarin-android to use def5bc0, which includes 005c914. dotnet/android#8625 contains *failing unit tests* (?!), including `Java.InteropTests.InvokeVirtualFromConstructorTests()`: Java.Lang.LinkageError : net.dot.jni.test.CallVirtualFromConstructorDerived ----> System.NotSupportedException : Could not find System.Type corresponding to Java type JniTypeSignature(TypeName=net/dot/jni/test/CallVirtualFromConstructorDerived ArrayRank=0 Keyword=False) . at Java.Interop.JniEnvironment.StaticMethods.GetStaticMethodID(JniObjectReference , String , String ) at Java.Interop.JniType.GetStaticMethod(String , String ) at Java.Interop.JniPeerMembers.JniStaticMethods.GetMethodInfo(String , String ) at Java.Interop.JniPeerMembers.JniStaticMethods.GetMethodInfo(String ) at Java.Interop.JniPeerMembers.JniStaticMethods.InvokeObjectMethod(String , JniArgumentValue* ) at Java.InteropTests.CallVirtualFromConstructorDerived.NewInstance(Int32 value) at Java.InteropTests.InvokeVirtualFromConstructorTests.ActivationConstructor() at System.Reflection.MethodBaseInvoker.InterpretedInvoke_Method(Object obj, IntPtr* args) at System.Reflection.MethodBaseInvoker.InvokeWithNoArgs(Object , BindingFlags ) --- End of managed Java.Lang.LinkageError stack trace --- java.lang.NoClassDefFoundError: net.dot.jni.test.CallVirtualFromConstructorDerived at crc643df67da7b13bb6b1.TestInstrumentation_1.n_onStart(Native Method) at crc643df67da7b13bb6b1.TestInstrumentation_1.onStart(TestInstrumentation_1.java:35) at android.app.Instrumentation$InstrumentationThread.run(Instrumentation.java:2189) Caused by: android.runtime.JavaProxyThrowable: [System.NotSupportedException]: Could not find System.Type corresponding to Java type JniTypeSignature(TypeName=net/dot/jni/test/CallVirtualFromConstructorDerived ArrayRank=0 Keyword=False) . at Java.Interop.ManagedPeer.GetTypeFromSignature(Unknown Source:0) at Java.Interop.ManagedPeer.RegisterNativeMembers(Unknown Source:0) at net.dot.jni.ManagedPeer.registerNativeMembers(Native Method) at net.dot.jni.test.CallVirtualFromConstructorDerived.<clinit>(CallVirtualFromConstructorDerived.java:12) ... 3 more --NotSupportedException at Java.Interop.ManagedPeer.GetTypeFromSignature(JniTypeManager , JniTypeSignature , String ) at Java.Interop.ManagedPeer.RegisterNativeMembers(IntPtr jnienv, IntPtr klass, IntPtr n_nativeClass, IntPtr n_methods) :shocked-pikachu-face: (But dotnet/android#8543 was green!) The problem is twofold: 1. 005c914 now requires the presence of typemap entries from e.g. `net.dot.jni.test.CallVirtualFromConstructorDerived` to `Java.InteropTests.CallVirtualFromConstructorDerived`. 2. `Java.Interop.Tools.JavaCallableWrappers` et al doesn't create typemap entries for `Java.Interop.JavaObject` subclasses which have `[JniTypeSignature]`. Consequently, our units tests fail (and apparently weren't *run* on dotnet/android#8543?! Still not sure what happened.) Update typemap generation by adding a new `TypeDefinition.HasJavaPeer()` extension method to replace all the `.IsSubclassOf("Java.Lang.Object")` and similar checks, extending it to also check for `Java.Interop.JavaObject` and `Java.Interop.JavaException` base types. (Continuing to use base type checks is done instead of just relying on implementation of `Java.Interop.IJavaPeerable` as a performance optimization, as there could be *lots* of interface types to check.) Additionally, @jonathanpeppers -- while trying to investigate all this -- ran across a build failure: obj\Debug\net9.0-android\android\src\java\lang\Object.java(7,15): javac.exe error JAVAC0000: error: cyclic inheritance involving Object This suggests that `Java.Interop.Tools.JavaCallableWrappers` was encountering `Java.Interop.JavaObject` -- or some other type which has `[JniTypeSignature("java/lang/Object")]` -- which is why `java/lang/Object.java` was being generated. Audit all `[JniTypeSignature]` attributes, and add `GenerateJavaPeer=false` to all types which should *not* hava a Java Callable Wrapper generated for them. This includes nearly everything within `Java.Interop-Tests.dll`. (We want the typemaps! We *don't* want generated Java source, as we have hand-written Java peer types for those tests.) Add `[JniTypeSignature]` to `GenericHolder<T>`. This type mapping isn't *actually* required, but it *is* used in `JavaVMFixture`, and it confuses people (me!) if things are inconsistent. Additionally, remove `tests/` from the Java-side name, for consistency. ~~ Avoid multiple java/lang/Object bindings ~~ A funny thing happened when in dotnet/android#8681 -- which tested this commit -- when using an intermediate version of this commit: unit tests started crashing! E monodroid-assembly: typemap: unable to load assembly 'Java.Interop-Tests' when looking up managed type corresponding to Java type 'java/lang/Object' What appears to be happening is an Unfortunate Interaction™: 1. `Java.Interop-Tests.dll` contained *multiple bindings* for `java/lang/Object`. e.g. [JniTypeSignature ("java/lang/Object", GenerateJavaPeer=false)] partial class JavaDisposedObject : JavaObject { } 2. The typemap generator has no functionality to "prioritize" one binding vs. another; it's random. As such, there is nothing to cause `Java.Lang.Object, Mono.Android` to be used as the preferred binding for `java/lang/Object`. This meant that when we hit the typemap codepath in .NET Android, we looked for the C# type that corresponded to `java/lang/Object`, found *some random type* from `Java.Interop-Tests`, and… …and then we hit another oddity: that codepath only supported looking for C# types in assemblies which had already been loaded. This was occurring during startup, so `Java.Interop-Tests` had not yet been loaded yet, so it errored out, returned `nullptr`, and later Android just aborts things: F droid.NET_Test: runtime.cc:638] JNI DETECTED ERROR IN APPLICATION: use of deleted local reference 0x79 Just…eep! This didn't happen before because `Java.Interop.JavaObject` subclasses *didn't* participate in typemap generation. This commit *adds* that support, introducing this unforeseen interaction. Fix this by *removing* most "alternate bindings" for `java/lang/Object`: - [JniTypeSignature ("java/lang/Object", GenerateJavaPeer=false)] + [JniTypeSignature (JniTypeName)] partial class JavaDisposedObject : JavaObject { + internal const string JniTypeName = "net/dot/jni/test/JavaDisposedObject"; } This implicitly requires that we now have a Java Callable Wrapper for this type, so update `Java.Interop-Tests.csproj` to run `jcw-gen` as part of the build process. This ensures that we create the JCW for e.g. `JavaDisposedObject`. Update `JavaVMFixture` to add the required typemap entries. --- Aside: this project includes [T4 Text Templates][0]. To regenerate the output files *without involving Visual Studio*, you can install the [`dotnet-t4`][1] tool: $ dotnet tool install --global dotnet-t4 then run it separately for each `.tt` file: $HOME/.dotnet/tools/t4 -o src/Java.Interop/Java.Interop/JavaPrimitiveArrays.cs \ src/Java.Interop/Java.Interop/JavaPrimitiveArrays.tt [0]: https://learn.microsoft.com/visualstudio/modeling/code-generation-and-t4-text-templates?view=vs-2022 [1]: https://www.nuget.org/packages/dotnet-t4/
Configuration menu - View commit details
-
Copy full SHA for 07c7300 - Browse repository at this point
Copy the full SHA 07c7300View commit details
This comparison is taking too long to generate.
Unfortunately it looks like we can’t render this comparison for you right now. It might be too big, or there might be something weird with your repository.
You can try running this command locally to see the comparison on your machine:
git diff 8b85462e5f304d9aa2d91c02966d7dc9751516c7...07c73009571d3b5d36ae79d0d4d69a02062dd6e8