Commit 1a2eb95
[Xamarin.Android.Build.Tasks] jnimarshalmethod-gen.exe integration (#2153)
Context: https://github.com/xamarin/xamarin-android/projects/1
Context: #2138
A "JNI Marshal Method" is a method which the JVM eventually executes
when a Java `native` method is invoked. JNI Marshal Methods are
currently contained within binding assemblies for all `virtual`
methods, e.g. `Java.Lang.Object` contains:
partial class Object {
static int n_GetHashCode (IntPtr jnienv, IntPtr native__this)
{
var __this = Object.GetObject<Object> (jnienv, native__this, JniHandleOwnership.DoNotTransfer);
return __this.GetHashCode ();
}
}
If a C# class overrides `Java.Lang.Object.GetHashCode()`, then
`Object.n_GetHashCode()` will be invoked when Java code calls
`value.hashCode()` on an instance of that C# class.
JNI Marshal Methods are responsible for marshaling parameters and
return types.
However, there is one problem with JNI Marshal Methods as currently
constructed: they *require* the use of `System.Reflection.Emit`,
via `Android.Runtime.JNINativeWrapper.CreateDelegate()`.
`Object.n_GetHashCode()` isn't *directly* registered with JNI.
Instead, a "wrapper" is generated at runtime, and it's the wrapper
which is registered with JNI. The wrapper effectively does:
int wrapper_n_GetHashCode (IntPtr jnienv, IntPtr native__this)
{
try {
JNIEnv.WaitForBridgeProcessing();
return n_GetHashCode (jnienv, native__this);
}
catch (Exception e) when Debugger.IsAttached || !JNIEnv.PropagateExceptions {
AndroidEnvironment.UnhandledException (e);
}
}
Previously, the use of `DynamicMethod` and `System.Reflection.Emit`
was unavoidable, as we needed to ensure that all exceptions were
handled appropriately, no matter which `generator` version was used
to generate the JNI Marshal Methods.
Enter `jnimarshalmethod-gen.exe`, which is a utility which will
generate "complete" JNI Marshal Methods that can be registered
directly with JNI, *without* using `JNINativeWrapper` or requiring
use of `DynamicMethod`.
`jnimarshalmethod-gen.exe` would process the hypothetical C#
`GetHashCode()` override and generate the JNI Marshal Method:
partial class ExampleObjectSubclass : Java.Lang.Object {
public override int GetHashCode () {return 42;}
/* generated by `jnimarshalmethod-gen.exe` */
partial class '__<$>_jni_marshal_methods' {
public int GetHashCode (IntPtr __jnienv, IntPtr native__this)
{
var jniTransition = new JniTransition (__jnienv);
JniRuntime runtime = default;
try {
runtime = JniEnvironment.Runtime;
var valueManager = runtime.ValueManager;
valueManager.WaitForGCBridgeProcessing ();
var __this = valueManager.GetValue<ExampleObjectSubclass>(native__this);
return __this.GetHashCode ();
}
catch (Exception e) when (runtime.ExceptionShouldTransitionToJni (ex)) {
jniTransition.SetPendingException (ex);
}
finally {
jniTransition.Dispose ();
}
}
}
}
The eventual hope and intent is that this will improve process
startup times, as we'll need to do less work.
This is an initial effort, and not yet complete.
`jnimarshalmethod-gen.exe` is invoked from the new
`_GenerateJniMarshalMethods` target, which uses *xamarin-android*'s
mono, *not* a system mono or other managed runtime. This is done so
that `$MONO_PATH` can be overridden, allowing "normal" use of
System.Reflection *by `JniValueMarshaler` instances* during *build*
time, *not* runtime. The
`$(AndroidGenerateJniMarshalMethodsAdditionalArguments)` MSBuild
property can be used to add additional parameters to the
`jnimarshalmethod-gen.exe` invocation. This is useful for debugging,
so that options such as `-v`, `-d`, or `--keeptemp` can be used.
Enable use of `jnimarshalmethod-gen.exe` for user assemblies and
`Mono.Android.dll` by setting `$(AndroidGenerateJniMarshalMethods)`
to True.
This is currently only supported on non-Windows platforms.
Additionally, remove the `NotImplementedException` throws from the
`Android.Runtime.AndroidValueManager`. `AndroidValueManager` is now
used from the generated marshal methods, which handle primitive
arrays.
In order to make this whole process work, some additional custom
value marshalers are needed. Add custom JNI value marshalers for
`Android.Graphics.Color` and `Android.Runtime.IJavaObject`.
Additional examples `jnimarshalmethod-gen.exe` output:
using System;
using Java.Interop;
using Android.Runtime;
public static void n_setAdapter_Landroid_widget_ListAdapter_ (IntPtr __jnienv, IntPtr __this, IntPtr value)
{
JniTransition jniTransition = new JniTransition (__jnienv);
JniRuntime runtime = default(JniRuntime);
try {
runtime = JniEnvironment.Runtime;
JniRuntime.JniValueManager valueManager = runtime.ValueManager;
valueManager.WaitForGCBridgeProcessing ();
AbsListView value2 = valueManager.GetValue<AbsListView> (__this);
IListAdapter listAdapter2 = value2.Adapter = Java.Interop.JavaConvert.FromJniHandle<IListAdapter> (value, JniHandleOwnership.DoNotTransfer);
} catch (Exception ex) when (runtime.ExceptionShouldTransitionToJni (ex)) {
jniTransition.SetPendingException (ex);
} finally {
jniTransition.Dispose ();
}
}
public static IntPtr n_getAdapter (IntPtr __jnienv, IntPtr __this)
{
JniTransition jniTransition = new JniTransition (__jnienv);
JniRuntime runtime = default(JniRuntime);
try {
runtime = JniEnvironment.Runtime;
JniRuntime.JniValueManager valueManager = runtime.ValueManager;
valueManager.WaitForGCBridgeProcessing ();
AbsListView value = valueManager.GetValue<AbsListView> (__this);
IListAdapter adapter = value.Adapter;
return JNIEnv.ToLocalJniHandle (adapter);
} catch (Exception ex) when (runtime.ExceptionShouldTransitionToJni (ex)) {
jniTransition.SetPendingException (ex);
return default(IntPtr);
} finally {
jniTransition.Dispose ();
}
IntPtr intPtr = default(IntPtr);
return intPtr;
}
Profiling results of the Xamarin.Forms Integration Test running on
Pixel 2 XL phone:
Old marshaling:
133 1 15 Android.Runtime.JNIEnv:RegisterJniNatives (intptr,int,intptr,intptr,int)
288 2 1 Android.Runtime.JNIEnv:Initialize (Android.Runtime.JnienvInitializeArgs*)
New marshaling:
68 1 15 Android.Runtime.JNIEnv:RegisterJniNatives (intptr,int,intptr,intptr,int)
264 2 1 Android.Runtime.JNIEnv:Initialize (Android.Runtime.JnienvInitializeArgs*)
Native member registration for a type is ~2x faster and
`JNIEnv.Initialize()` is ~20ms faster.
Finally, `RunJavaInteropTests` was moved from
`@(_RunParallelTestTarget)` item group to the `@(_RunTestTarget)`
group, because it overwrites `Java.Runtime.Environment.dll.config`,
which is required by `jnimarshalmethod-gen.exe` to run. We need to
run these tests after the `.apk` tests. This should be fixed in the
future, hopefully by adding and/or fixing the Inputs/Outputs of the
relevant targets.1 parent 0d6e65a commit 1a2eb95
File tree
15 files changed
+208
-31
lines changed- build-tools/scripts
- external
- src
- Mono.Android
- Android.Graphics
- Android.Runtime
- Test
- Xamarin.Android.Build.Tasks
- Tasks
- monodroid
- tests/Xamarin.Forms-Performance-Integration/Droid
15 files changed
+208
-31
lines changed| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
| 24 | + | |
| 25 | + | |
| 26 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
147 | 147 | | |
148 | 148 | | |
149 | 149 | | |
150 | | - | |
151 | 150 | | |
152 | 151 | | |
153 | 152 | | |
| 153 | + | |
154 | 154 | | |
155 | 155 | | |
156 | 156 | | |
| |||
Submodule Java.Interop updated from 8ee34a3 to ec2813a
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
1 | 1 | | |
2 | 2 | | |
3 | 3 | | |
| 4 | + | |
| 5 | + | |
4 | 6 | | |
5 | 7 | | |
6 | 8 | | |
7 | 9 | | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
8 | 13 | | |
9 | 14 | | |
| 15 | + | |
10 | 16 | | |
11 | 17 | | |
12 | 18 | | |
| |||
386 | 392 | | |
387 | 393 | | |
388 | 394 | | |
| 395 | + | |
| 396 | + | |
| 397 | + | |
| 398 | + | |
| 399 | + | |
| 400 | + | |
| 401 | + | |
| 402 | + | |
| 403 | + | |
| 404 | + | |
| 405 | + | |
| 406 | + | |
| 407 | + | |
| 408 | + | |
| 409 | + | |
| 410 | + | |
| 411 | + | |
| 412 | + | |
| 413 | + | |
| 414 | + | |
| 415 | + | |
| 416 | + | |
| 417 | + | |
| 418 | + | |
| 419 | + | |
| 420 | + | |
| 421 | + | |
| 422 | + | |
| 423 | + | |
| 424 | + | |
| 425 | + | |
| 426 | + | |
| 427 | + | |
| 428 | + | |
| 429 | + | |
| 430 | + | |
| 431 | + | |
| 432 | + | |
| 433 | + | |
| 434 | + | |
| 435 | + | |
| 436 | + | |
| 437 | + | |
| 438 | + | |
| 439 | + | |
| 440 | + | |
389 | 441 | | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
363 | 363 | | |
364 | 364 | | |
365 | 365 | | |
366 | | - | |
367 | 366 | | |
368 | 367 | | |
369 | 368 | | |
370 | 369 | | |
371 | | - | |
372 | 370 | | |
373 | 371 | | |
374 | 372 | | |
| |||
378 | 376 | | |
379 | 377 | | |
380 | 378 | | |
381 | | - | |
382 | 379 | | |
383 | 380 | | |
384 | 381 | | |
385 | 382 | | |
386 | | - | |
387 | 383 | | |
388 | 384 | | |
389 | 385 | | |
390 | 386 | | |
391 | | - | |
| 387 | + | |
392 | 388 | | |
393 | 389 | | |
394 | 390 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
2 | 2 | | |
3 | 3 | | |
4 | 4 | | |
| 5 | + | |
5 | 6 | | |
6 | 7 | | |
7 | 8 | | |
| |||
Lines changed: 54 additions & 0 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
| 24 | + | |
| 25 | + | |
| 26 | + | |
| 27 | + | |
| 28 | + | |
| 29 | + | |
| 30 | + | |
| 31 | + | |
| 32 | + | |
| 33 | + | |
| 34 | + | |
| 35 | + | |
| 36 | + | |
| 37 | + | |
| 38 | + | |
| 39 | + | |
| 40 | + | |
| 41 | + | |
| 42 | + | |
| 43 | + | |
| 44 | + | |
| 45 | + | |
| 46 | + | |
| 47 | + | |
| 48 | + | |
| 49 | + | |
| 50 | + | |
| 51 | + | |
| 52 | + | |
| 53 | + | |
| 54 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
83 | 83 | | |
84 | 84 | | |
85 | 85 | | |
| 86 | + | |
86 | 87 | | |
87 | 88 | | |
88 | 89 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
43 | 43 | | |
44 | 44 | | |
45 | 45 | | |
| 46 | + | |
46 | 47 | | |
47 | 48 | | |
48 | 49 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
8 | 8 | | |
9 | 9 | | |
10 | 10 | | |
| 11 | + | |
11 | 12 | | |
12 | 13 | | |
13 | 14 | | |
| |||
16 | 17 | | |
17 | 18 | | |
18 | 19 | | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
19 | 24 | | |
0 commit comments