Commit c93fea0
authored
[jcw-gen, jnimarshalmethod-gen] Native method consistency (#1160)
Context: #1153
Context: 4787e01
Context: 77800dd
PR #1153 is exploring the use of [.NET Native AOT][0] to produce a
native library which is used *within* a `java`-originated process:
% java -cp … com/microsoft/hello_from_jni/App
# launches NativeAOT-generated native lib, executes C# code…
As NativeAOT has no support for `System.Reflection.Emit`, the only
way for Java code to invoke managed code -- in a Desktop Java.Base
environment! [^0] see 4787e01 -- would be to pre-generate the
required marshal methods via `jnimarshalmethod-gen`.
This in turn requires updating `jcw-gen` to support the pre-existing
`Java.Interop.JavaCallableAttribute`, so that C# code could
reasonably declare methods visible to Java, along with the
introduction of, and support for, a new
`Java.Interop.JavaCallableConstructorAttribute` type. This allows
straightforward usage:
[JniTypeSignature ("example/ManagedType")] // for a nice Java name!
class ManagedType : Java.Lang.Object {
int value;
[JavaCallableConstructor(SuperConstructorExpression="")]
public ManagedType (int value) {
this.value = value;
}
[JavaCallable ("getString")]
public Java.Lang.String GetString () {
return new Java.Lang.String ($"Hello from C#, via Java.Interop! Value={value}");
}
}
Run this through `jcw-gen` and `jnimarshalmethod-gen`, run the app,
and nothing worked (?!), because not all pieces were in agreement.
Java `native` method registration is One Of Those Things™ that
involves lots of moving pieces:
* `generator` emits bindings for Java types, which includes Java
method names, signatures, and (on .NET Android) the "connector
method" to use:
[Register ("toString", "()Ljava/lang/String;", "GetToStringHandler")] // .NET Android
[JniMethodSignature ("toString", "()Ljava/lang/String;")] // Java.Base
public override unsafe string? ToString () {…}
* `jcw-gen` uses `generator` output, *prefixing* Java method names
with `n_` for `native` method declarations, along with a
method wrapper [^1]
public String toString() {return n_toString();}
private native String n_toString();
* `jnimarshalmethod-gen` emits marshal methods for Java.Base,
and needs to register the `native` methods declared by `jcw-gen`.
`jnimarshalmethod-gen` and `jcw-gen` need to be consistent with
each other.
* `MarshalMemberbuilder.CreateMarshalToManagedMethodRegistration()`
creates a `JniNativeMethodRegistration` instance which contains
the name of the Java `native` method to register, and was
using a name inconsistent with `jcw-gen`.
Turns Out, `jcw-gen`, `jnimarshalmethod-gen`, and
`MarshalMemberBuilder` were *not* consistent.
The only "real" `jnimarshalmethod-gen` usage (77800dd) is with the
`Java.Interop.Export-Tests` unit test assembly, which *did not use*
`jcw-gen`; it contained only hand-written Java code. Consequently,
*none* of the Java `native` methods declared within it had an `n_`
prefix, and since this worked with `jnimarshalmethod-gen`, this means
that `jnimarshalmethod-gen` registration logic likewise didn't use
`n_` prefixed method names.
The result is that in the NativeAOT app, it would attempt to register
the `native` Java method `ManagedType.getString()`, while what
`jcw-gen` declared was `ManagedType.n_getString()`!
Java promptly threw an exception, and the app crashed.
Update `Java.Interop.Export-Tests` so that all the methods used with
`MarshalMemberBuilder` are declared with `n_` prefixes, and add
a `Java.Lang.Object` subclass example to the unit tests:
Update `tests/Java.Interop.Tools.JavaCallableWrappers-Tests` to
add a test for `.CodeGenerationTarget==JavaInterop1`.
Add `$(NoWarn)` to
`Java.Interop.Tools.JavaCallableWrappers-Tests.csproj` in order to
"work around" warnings-as-errors:
…/src/Java.Interop.NamingCustomAttributes/Java.Interop/ExportFieldAttribute.cs(19,63): error CA1019: Remove the property setter from Name or reduce its accessibility because it corresponds to positional argument name
…/src/Java.Interop.NamingCustomAttributes/Android.Runtime/RegisterAttribute.cs(53,4): error CA1019: Remove the property setter from Name or reduce its accessibility because it corresponds to positional argument name
…/src/Java.Interop.NamingCustomAttributes/Java.Interop/ExportFieldAttribute.cs(12,16): error CA1813: Avoid unsealed attributes
…
These are "weird"; the warnings/errors appear to come in because
`Java.Interop.Tools.JavaCallableWrappers-Tests.csproj` now includes:
<Compile Include="..\..\src\Java.Interop\Java.Interop\JniTypeSignatureAttribute.cs" />
which appears to pull in `src/Java.Interop/.editorconfig`, which makes
CA1019 and CA1813 errors. (I do not understand what is happening.)
Update `jnimarshalmethod-gen` so that the Java `native` methods it
registers have an `n_` prefix.
Refactor `ExpressionAssemblyBuilder.CreateRegistrationMethod()` to
`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…
Update `ExpressionAssemblyBuilder` so that the delegate types it
creates for marshal method registration all have
`[UnmanagedFunctionPointer(CallingConvention.Winapi)]`. (This isn't
needed *here*, but is needed in the context of NativeAOT, as
NativeAOT will only emit "marshal stubs" for delegate types which
have `[UnmanagedFunctionPointer]`.) Unfortunately, adding
`[UnmanagedFunctionPointer]` broke things:
error JM4006: jnimarshalmethod-gen: Unable to process assembly '…/Hello-NativeAOTFromJNI.dll'
Failed to resolve System.Runtime.InteropServices.CallingConvention
Mono.Cecil.ResolutionException: Failed to resolve System.Runtime.InteropServices.CallingConvention
at Mono.Cecil.Mixin.CheckedResolve(TypeReference self)
at Mono.Cecil.SignatureWriter.WriteCustomAttributeEnumValue(TypeReference enum_type, Object value)
…
The problem is that `CallingConvention` was resolved from
`System.Private.CoreLib`, and when we removed that assembly
reference, the `CallingConvention` couldn't be resolved at all.
We could "fix" this by explicitly adding a reference to
`System.Runtime.InteropServices.dll`, but how many more such corner
cases exist? The current approach is not viable.
Remove the code from 77800dd which attempts to remove
`System.Private.CoreLib`. So long as `ExpressionAssemblyBuilder`
output is *only* used in "completed" apps (not distributed in NuGet
packages or some "intermediate" form), referencing
`System.Private.CoreLib` is "fine".
Update `jnimarshalmethod-gen` assembly location probing: in #1153, it
was attempting to resolve the *full assembly name* of `Java.Base`, as
`Java.Base, Version=7.0.0.0, Culture=neutral, PublicKeyToken=null`,
causing it to attempt to load the file
`Java.Base, Version=7.0.0.0, Culture=neutral, PublicKeyToken=null.dll`,
which doesn't exist. Use `AssemblyName` to parse the string and
extract out the assembly name, so that `Java.Base.dll` is probed for
and found.
Update `JreTypeManager` to *also* register the marshal methods
generated by
`Runtime.MarshalMemberBuilder.GetExportedMemberRegistrations()`.
With all that, the updated `Java.Interop.Export-Tests` test now work
both before and after `jnimarshalmethod-gen` is run:
% dotnet test --logger "console;verbosity=detailed" bin/TestDebug-net7.0/Java.Interop.Export-Tests.dll &&
dotnet bin/Debug-net7.0/jnimarshalmethod-gen.dll bin/TestDebug-net7.0/Java.Interop.Export-Tests.dll &&
dotnet test --logger "console;verbosity=detailed" bin/TestDebug-net7.0/Java.Interop.Export-Tests.dll
…
TODO:
* `generator --codegen-target=JavaInterop1` should emit
JNI method signature information for constructors!
This would likely remove the need for
`[JavaCallableConstructor(SuperConstructorExpression="")`.
* #1159
[0]: https://learn.microsoft.com/dotnet/core/deploying/native-aot
[^0]: In a .NET Android environment, marshal methods are part of
`generator` output, so things would be more straightforward
there, though all the `_JniMarshal_*` types that are declared
would also need to have
`[UnmanagedFunctionPointer(CallingConvention.Winapi)]`…
[^1]: Why not just declare `toString()` as `native`? Why have the
separate `n_`-prefixed version? To make the
`#if MONODROID_TIMING` block more consistent within
`JavaCallableWrapperGenerator.GenerateMethod()`.
It's likely "too late" to *easily* change this now.1 parent f9a16d5 commit c93fea0
File tree
20 files changed
+351
-66
lines changed- src
- Java.Interop.Export/Java.Interop
- Java.Interop.Tools.Expressions/Java.Interop.Tools.Expressions
- Java.Interop.Tools.JavaCallableWrappers/Java.Interop.Tools.JavaCallableWrappers
- Java.Interop/Java.Interop
- Java.Runtime.Environment/Java.Interop
- tests
- Java.Interop.Export-Tests
- Java.Interop
- java
- com/xamarin/interop/export
- net/dot/jni/test
- Java.Interop.Tools.JavaCallableWrappers-Tests
- Java.Interop.Tools.JavaCallableWrappers
- tools/jnimarshalmethod-gen
20 files changed
+351
-66
lines changedLines changed: 1 addition & 0 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
1 | 2 | | |
2 | 3 | | |
3 | 4 | | |
| |||
Lines changed: 16 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 | + | |
Lines changed: 1 addition & 1 deletion
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
68 | 68 | | |
69 | 69 | | |
70 | 70 | | |
71 | | - | |
| 71 | + | |
72 | 72 | | |
73 | 73 | | |
74 | 74 | | |
| |||
Lines changed: 14 additions & 23 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
2 | 2 | | |
3 | 3 | | |
4 | 4 | | |
| 5 | + | |
5 | 6 | | |
6 | 7 | | |
7 | 8 | | |
| |||
58 | 59 | | |
59 | 60 | | |
60 | 61 | | |
61 | | - | |
| 62 | + | |
62 | 63 | | |
63 | 64 | | |
64 | 65 | | |
| |||
70 | 71 | | |
71 | 72 | | |
72 | 73 | | |
| 74 | + | |
| 75 | + | |
73 | 76 | | |
74 | 77 | | |
75 | 78 | | |
| |||
83 | 86 | | |
84 | 87 | | |
85 | 88 | | |
86 | | - | |
| 89 | + | |
87 | 90 | | |
88 | 91 | | |
89 | 92 | | |
| |||
116 | 119 | | |
117 | 120 | | |
118 | 121 | | |
119 | | - | |
120 | | - | |
121 | | - | |
122 | 122 | | |
123 | 123 | | |
124 | 124 | | |
| |||
187 | 187 | | |
188 | 188 | | |
189 | 189 | | |
| 190 | + | |
| 191 | + | |
| 192 | + | |
| 193 | + | |
| 194 | + | |
| 195 | + | |
| 196 | + | |
| 197 | + | |
| 198 | + | |
190 | 199 | | |
191 | 200 | | |
192 | 201 | | |
| |||
237 | 246 | | |
238 | 247 | | |
239 | 248 | | |
240 | | - | |
241 | | - | |
242 | | - | |
243 | | - | |
244 | | - | |
245 | | - | |
246 | | - | |
247 | 249 | | |
248 | 250 | | |
249 | 251 | | |
250 | 252 | | |
251 | | - | |
252 | | - | |
253 | | - | |
254 | | - | |
255 | | - | |
256 | 253 | | |
257 | 254 | | |
258 | 255 | | |
| |||
261 | 258 | | |
262 | 259 | | |
263 | 260 | | |
264 | | - | |
265 | | - | |
266 | | - | |
267 | | - | |
268 | | - | |
269 | 261 | | |
270 | 262 | | |
271 | 263 | | |
272 | 264 | | |
273 | 265 | | |
274 | 266 | | |
275 | | - | |
276 | 267 | | |
277 | 268 | | |
278 | 269 | | |
| |||
Lines changed: 38 additions & 3 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
164 | 164 | | |
165 | 165 | | |
166 | 166 | | |
167 | | - | |
| 167 | + | |
| 168 | + | |
| 169 | + | |
| 170 | + | |
| 171 | + | |
| 172 | + | |
| 173 | + | |
168 | 174 | | |
169 | 175 | | |
170 | 176 | | |
| |||
412 | 418 | | |
413 | 419 | | |
414 | 420 | | |
| 421 | + | |
| 422 | + | |
| 423 | + | |
| 424 | + | |
| 425 | + | |
| 426 | + | |
| 427 | + | |
| 428 | + | |
| 429 | + | |
| 430 | + | |
| 431 | + | |
| 432 | + | |
| 433 | + | |
| 434 | + | |
| 435 | + | |
| 436 | + | |
| 437 | + | |
| 438 | + | |
| 439 | + | |
415 | 440 | | |
416 | 441 | | |
417 | 442 | | |
| |||
447 | 472 | | |
448 | 473 | | |
449 | 474 | | |
450 | | - | |
| 475 | + | |
| 476 | + | |
| 477 | + | |
| 478 | + | |
| 479 | + | |
451 | 480 | | |
452 | 481 | | |
453 | 482 | | |
| |||
458 | 487 | | |
459 | 488 | | |
460 | 489 | | |
461 | | - | |
| 490 | + | |
| 491 | + | |
| 492 | + | |
| 493 | + | |
| 494 | + | |
| 495 | + | |
| 496 | + | |
462 | 497 | | |
463 | 498 | | |
464 | 499 | | |
| |||
Lines changed: 12 additions & 4 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
412 | 412 | | |
413 | 413 | | |
414 | 414 | | |
415 | | - | |
| 415 | + | |
416 | 416 | | |
| 417 | + | |
417 | 418 | | |
418 | | - | |
419 | | - | |
| 419 | + | |
| 420 | + | |
| 421 | + | |
| 422 | + | |
| 423 | + | |
| 424 | + | |
| 425 | + | |
420 | 426 | | |
421 | 427 | | |
422 | 428 | | |
| |||
466 | 472 | | |
467 | 473 | | |
468 | 474 | | |
| 475 | + | |
| 476 | + | |
469 | 477 | | |
470 | | - | |
| 478 | + | |
471 | 479 | | |
472 | 480 | | |
473 | 481 | | |
| |||
Lines changed: 2 additions & 0 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
11 | 11 | | |
12 | 12 | | |
13 | 13 | | |
| 14 | + | |
14 | 15 | | |
| 16 | + | |
15 | 17 | | |
16 | 18 | | |
17 | 19 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
216 | 216 | | |
217 | 217 | | |
218 | 218 | | |
219 | | - | |
| 219 | + | |
220 | 220 | | |
221 | 221 | | |
222 | 222 | | |
| |||
Lines changed: 4 additions & 0 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
52 | 52 | | |
53 | 53 | | |
54 | 54 | | |
| 55 | + | |
| 56 | + | |
| 57 | + | |
| 58 | + | |
55 | 59 | | |
56 | 60 | | |
57 | 61 | | |
| |||
Lines changed: 3 additions & 6 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
22 | 22 | | |
23 | 23 | | |
24 | 24 | | |
| 25 | + | |
25 | 26 | | |
26 | 27 | | |
27 | 28 | | |
28 | 29 | | |
29 | 30 | | |
30 | 31 | | |
31 | | - | |
| 32 | + | |
32 | 33 | | |
33 | 34 | | |
34 | | - | |
35 | | - | |
36 | | - | |
37 | | - | |
38 | | - | |
| 35 | + | |
39 | 36 | | |
40 | 37 | | |
0 commit comments