-
Notifications
You must be signed in to change notification settings - Fork 55
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
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!
- Loading branch information
Showing
11 changed files
with
112 additions
and
24 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
<linker> | ||
<assembly fullname="Java.Interop"> | ||
<type fullname="Java.Interop.JavaException"> | ||
<method name=".ctor" /> | ||
</type> | ||
</assembly> | ||
</linker> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,48 @@ | ||
using Java.Interop; | ||
|
||
namespace Hello_NativeAOTFromJNI; | ||
|
||
class NativeAotTypeManager : JniRuntime.JniTypeManager { | ||
|
||
#pragma warning disable IL2026 | ||
Dictionary<string, Type> typeMappings = new () { | ||
["com/xamarin/java_interop/internal/JavaProxyThrowable"] = typeof (JniEnvironment).Assembly.GetType ("Java.Interop.JavaProxyThrowable", throwOnError: true)!, | ||
["example/ManagedType"] = typeof (Example.ManagedType), | ||
}; | ||
#pragma warning restore IL2026 | ||
|
||
public override void RegisterNativeMembers (JniType nativeClass, ReadOnlySpan<char> assemblyQualifiedTypeName, ReadOnlySpan<char> methods) | ||
{ | ||
Console.WriteLine ($"C# RegisterNativeMembers({nativeClass}, \"{assemblyQualifiedTypeName}\", \"{methods}\")"); | ||
if (typeMappings.TryGetValue (nativeClass.Name, out var type)) { | ||
base.TryRegisterNativeMembers (nativeClass, type, methods); | ||
} | ||
else { | ||
throw new NotSupportedException ($"Unsupported registration type: \"{nativeClass.Name}\" <=> \"{assemblyQualifiedTypeName}\"!"); | ||
} | ||
} | ||
|
||
protected override IEnumerable<Type> GetTypesForSimpleReference (string jniSimpleReference) | ||
{ | ||
if (typeMappings.TryGetValue (jniSimpleReference, out var target)) | ||
yield return target; | ||
foreach (var t in base.GetTypesForSimpleReference (jniSimpleReference)) | ||
yield return t; | ||
} | ||
|
||
protected override IEnumerable<string> GetSimpleReferences (Type type) | ||
{ | ||
return base.GetSimpleReferences (type) | ||
.Concat (CreateSimpleReferencesEnumerator (type)); | ||
} | ||
|
||
IEnumerable<string> CreateSimpleReferencesEnumerator (Type type) | ||
{ | ||
if (typeMappings == null) | ||
yield break; | ||
foreach (var e in typeMappings) { | ||
if (e.Value == type) | ||
yield return e.Key; | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters