You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Commit 69c90ba expanded into a "Full(er)" sample, with just one issue:
it didn't fully work:
Exception in thread "main" com.xamarin.java_interop.internal.JavaProxyThrowable: System.IO.FileNotFoundException: Could not resolve assembly 'Hello-NativeAOTFromJNI'.
at System.Reflection.TypeNameParser.ResolveAssembly(String) + 0x97
at System.Reflection.TypeNameParser.GetType(String, ReadOnlySpan`1, String) + 0x32
at System.Reflection.TypeNameParser.NamespaceTypeName.ResolveType(TypeNameParser&, String) + 0x17
at System.Reflection.TypeNameParser.GetType(String, Func`2, Func`4, Boolean, Boolean, Boolean, String) + 0x99
at Java.Interop.ManagedPeer.RegisterNativeMembers(IntPtr jnienv, IntPtr klass, IntPtr n_nativeClass, IntPtr n_assemblyQualifiedName, IntPtr n_methods) + 0x103
at com.xamarin.java_interop.ManagedPeer.registerNativeMembers(Native Method)
at example.ManagedType.<clinit>(ManagedType.java:15)
at com.microsoft.hello_from_jni.App.main(App.java:13)
The problem is that `ManagedPeer.RegisterNativeMembers()` calls
`Type.GetType("Example.ManagedType, Hello-NativeAOTFromJNI")`, which
throws `FileNotFoundException`.
Let's attempt to fix that:
Update `MangedPeer.RegisterNativeMembers()` to call the (new!) method:
partial class JniRuntime {
partial class JniTypeManager {
public virtual void RegisterNativeMembers (
JniType nativeClass,
ReadOnlySpan<char> assmblyQualifiedTypeName,
ReadOnlySpan<char> methods);
}
}
which allows a subclass to *avoid* the `Type.GetType()` call.
Add a `NativeAotTypeManager` class which subclasses
`JniRuntime.JniTypeManager`, overriding `RegisterNativeMembers()`
so as to avoid the `Type.GetType()` call. (It also "fakes" its own
"typemap" implementation…)
Add `Hello-NativeAOTFromJNI.xml`, a Linker Descriptor, to preserve
the `JavaException` constructors, which are needed when things
break horrifically.
TODO: figure out the appropriate `DynamicDependencyAttribute`
incantations to replace `Hello-NativeAOTFromJNI.xml`.
Update the `_AddMarshalMethods` build task to *also* update
`$(IntermediateOutputPath)$(TargetFileName)`, as the copy in
`$(IntermediateOutputPath)` is used by the `IlcCompile` target.
*Not* updating the copy in `$(IntermediateOutputPath)` means that
we don't get *any* marshal methods, and things break.
Rename `ExpressionAssemblyBuilder.CreateRegistrationMethod()` to
Rename `ExpressionAssemblyBuilder.AddRegistrationMethod()`, so that
the `EmitConsoleWriteLine()` invocation can provide the *full*
type name of the `__RegisterNativeMembers()` method, which helps
when there is more than one such method running around…
Various changes to `JniRuntime.JniTypeManager.cs`, to increase
logging verbosity, and to make the optimization effort in
`TryLoadJniMarshalMethods()` actually work; `Type.GetRuntimeMethod()`
*will not find* non-public methods, and `__RegisterNativeMembers()`
is rarely/never public. Thus, it was basically dead code, and we'd
always fallback to the "look at all methods and see which ones have
`[JniAddNativeMethodRegistration]`" codepath, which is by definition
slower. Use `Type.GetMethod()` instead.
Update `jnimarshalmethod-gen` & co so that they're consistent with
the output of `jcw-gen`. Without these changes, the JCW would declare
`n_GetString()`, while `jnimarshalmethod-gen` would try to register
`getString`, and Java would thrown an exception because there is no
`getString` member to register. Doh!
Finally, and the one thing that keeps this from being "perfect",
add an "activation constructor"
`Example.ManagedType(ref JniObjectReference, JniObjectReferenceOptions)`.
This constructor is currently needed because "JavaInterop1"-style
Java Callable Wrappers don't contain constructors (?!), so no
`Example.ManagedType` instance is created *until* the
`ManagedType.n_GetString()` marshal method is executed and attempts
to invoke the `ManagedType.GetString()` method.
We'll need to update `jcw-gen` & related to address this.
Current output:
% (cd bin/Release/osx-x64/publish ; java -cp hello-from-java.jar:java-interop.jar com/microsoft/hello_from_jni/App)
Hello from Java!
C# init()
Hello from .NET NativeAOT!
String returned to Java: Hello from .NET NativeAOT!
C# RegisterNativeMembers(JniType(Name='example/ManagedType' PeerReference=0x7fe545812ed8/G), "Example.ManagedType, Hello-NativeAOTFromJNI", "n_GetString:()Ljava/lang/String;:__export__
")
# jonp: called `Example.ManagedType/__<$>_jni_marshal_methods.__RegisterNativeMembers()` w/ 1 methods to register.
mt.getString()=Hello from C#, via Java.Interop!
Console.WriteLine($"# jonp: could not find method `{marshalType.FullName}.__<$>_jni_marshal_methods.__RegisterNativeMembers`; will look for methods with `[JniAddNativeMethodRegistration]`…]");
thrownewInvalidOperationException($"The method {methodInfo} marked with {nameof(JniAddNativeMethodRegistrationAttribute)} must be static");
486
+
thrownewInvalidOperationException($"The method `{declaringTypeName}.{methodInfo}` marked with [{nameof(JniAddNativeMethodRegistrationAttribute)}] must be static!");
0 commit comments