This repository was archived by the owner on Jul 6, 2026. It is now read-only.
Commit f998c42
authored
[generator]
Context: 3043d89
Context: https://xamarin.github.io/bugzilla-archives/23/2367/bug.html
Context: https://github.com/xamarin/monodroid/commit/6679dfc62eae462b5acc9deb095d0fa41786f80b
Constructors, how do they work?
Commit 3043d89 goes into some details about the interaction between
Java constructors and managed-side constructors when the Java
constructor is invoked first.
What happens when the managed-side constructor is invoked first?
class JavaInteropExample : Java.Lang.Object {
[JavaCallableConstructor(SuperConstructorExpression="")]
public JavaInteropExample (int a, int b) {}
}
*Before* that code runs (ideally), `jcw-gen` (or equivalent) will
run, creating a Java Callable Wrapper for `JavaInteropExample`,
and that Java Callable Wrapper (JCW) will contain a constructor:
// JCW
/* partial */ class JavaInteropExample extends java.lang.Object {
public JavaInteropExample(int p0, int p1) {
super ();
if (getClass () == JavaInteropExample.class) {
ManagedPeer.construct (…);
}
}
}
Then someone tries:
// C#
var o = new JavaInteropExample(42);
What happens is:
1. `JavaInteropExample(int, int)` constructor begins execution,
*immediately* executes (implicit) base constructor invocation
`: base()`.
2. `Java.Lang.Object` default constructor executes, which invokes
`JavaObject(ref JniObjectReference, JniObjectReferenceOptions)`
constructor, which is a no-op as the `JniObjectReference` is invalid.
3. `Java.Lang.Object` default constructor continues, hitting:
var peer = JniPeerMembers.InstanceMethods.StartCreateInstance ("()V", GetType (), null);
which looks up the Java peer, and invokes the default constructor
on the Java peer.
4. `JavaObject.PeerReference` (eventually) is `peer.NewGlobalRef()`,
and the `JavaInteropExample` constructor can *now* begin executing.
If the JCW doesn't contain a default constructor, then things fail:
Error Message:
Java.Interop.JavaException : Lnet/dot/jni/test/JavaCallableExample;.<init>()V
Stack Trace:
at Java.Interop.JniEnvironment.InstanceMethods.GetMethodID(JniObjectReference type, String name, String signature) in /Users/jon/Developer/src/xamarin/java.interop/src/Java.Interop/obj/Debug/net7.0/JniEnvironment.g.cs:line 19947
at Java.Interop.JniType.GetConstructor(String signature) in /Users/jon/Developer/src/xamarin/java.interop/src/Java.Interop/Java.Interop/JniType.cs:line 182
at Java.Interop.JniPeerMembers.JniInstanceMethods.GetConstructor(String signature) in /Users/jon/Developer/src/xamarin/java.interop/src/Java.Interop/Java.Interop/JniPeerMembers.JniInstanceMethods.cs:line 63
at Java.Interop.JniPeerMembers.JniInstanceMethods.FinishCreateInstance(String constructorSignature, IJavaPeerable self, JniArgumentValue* parameters) in /Users/jon/Developer/src/xamarin/java.interop/src/Java.Interop/Java.Interop/JniPeerMembers.JniInstanceMethods.cs:line 173
at Java.Lang.Object..ctor() in /Users/jon/Developer/src/xamarin/java.interop/src/Java.Base/obj/Debug-net7.0/mcw/Java.Lang.Object.cs:line 33
at Java.InteropTests.JavaCallableExample..ctor(Int32 a) in /Users/jon/Developer/src/xamarin/java.interop/tests/Java.Interop.Export-Tests/Java.Interop/JavaCallableExample.cs:line 11
at Java.InteropTests.JavaCallableExampleTest.ManagedCtorInvokesJavaDefaultCtor() in /Users/jon/Developer/src/xamarin/java.interop/tests/Java.Interop.Export-Tests/Java.Interop/JavaCallableExampleTests.cs:line 22
at System.RuntimeMethodHandle.InvokeMethod(Object target, Void** arguments, Signature sig, Boolean isConstructor)
at System.Reflection.MethodInvoker.Invoke(Object obj, IntPtr* args, BindingFlags invokeAttr)
--- End of managed Java.Interop.JavaException stack trace ---
java.lang.NoSuchMethodError: Lnet/dot/jni/test/JavaCallableExample;.<init>()V
But why would the JCW for `JavaInteropExample` contain a default
constructor at all?
In .NET Android, bound constructors have `[Register]`, and `jcw-gen`
will emit constructors based on "visible" `[Register]`ed constructors.
This ensures that we get *at least one* constructor that exists in
Java, which the C# derived types will need to invoke.
`generator --codegen-target=JavaInterop1`-style bindings didn't
previously emit anything for bound constructors, preventing `jcw-gen`
from performing this same logic. Consequently, the JCW for
`JavaInteropExample` *didn't* have a default constructor.
Add a new `Java.Interop.JniConstructorSignatureAttribute` type, and
update `generator` to emit this attribute on bound constructors.
Update `jcw-gen` to support `JniConstructorSignatureAttribute`.
This adds a default constructor to `JavaInteropExample`:
// JCW
/* partial */ class JavaInteropExample extends java.lang.Object {
public JavaInteropExample() {
super ();
if (getClass () == JavaInteropExample.class) {
ManagedPeer.construct (…);
}
}
public JavaInteropExample(int p0, int p1) {
super ();
if (getClass () == JavaInteropExample.class) {
ManagedPeer.construct (…);
}
}
}
Note: while there is a public default constructor on the JCW, Java
code cannot call it. If it attempts to do so, an exception will
be thrown because the C#-side type doesn't have a default constructor.[JniConstructorSignature] on bound constructors (#1162)1 parent c93fea0 commit f998c42
21 files changed
Lines changed: 71 additions & 4 deletions
File tree
- src
- Java.Interop.Tools.JavaCallableWrappers/Java.Interop.Tools.JavaCallableWrappers
- Java.Interop/Java.Interop
- tests
- Java.Base-Tests
- Java.Interop.Export-Tests/Java.Interop
- generator-Tests
- Unit-Tests/CodeGeneratorExpectedResults/JavaInterop1
- expected.ji
- AccessModifiers
- Constructors
- Streams
- TestInterface
- tools/generator/SourceWriters
- Attributes
Lines changed: 20 additions & 0 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
391 | 391 | | |
392 | 392 | | |
393 | 393 | | |
| 394 | + | |
| 395 | + | |
| 396 | + | |
| 397 | + | |
| 398 | + | |
| 399 | + | |
| 400 | + | |
| 401 | + | |
| 402 | + | |
| 403 | + | |
| 404 | + | |
| 405 | + | |
| 406 | + | |
394 | 407 | | |
395 | 408 | | |
396 | 409 | | |
| |||
461 | 474 | | |
462 | 475 | | |
463 | 476 | | |
| 477 | + | |
| 478 | + | |
| 479 | + | |
| 480 | + | |
| 481 | + | |
| 482 | + | |
| 483 | + | |
464 | 484 | | |
465 | 485 | | |
466 | 486 | | |
| |||
Lines changed: 20 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 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
24 | 24 | | |
25 | 25 | | |
26 | 26 | | |
27 | | - | |
| 27 | + | |
28 | 28 | | |
29 | 29 | | |
30 | 30 | | |
| |||
Lines changed: 6 additions & 0 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
16 | 16 | | |
17 | 17 | | |
18 | 18 | | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
| 24 | + | |
19 | 25 | | |
20 | 26 | | |
21 | 27 | | |
Lines changed: 2 additions & 0 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
8 | 8 | | |
9 | 9 | | |
10 | 10 | | |
| 11 | + | |
11 | 12 | | |
12 | 13 | | |
13 | 14 | | |
| |||
24 | 25 | | |
25 | 26 | | |
26 | 27 | | |
| 28 | + | |
27 | 29 | | |
28 | 30 | | |
29 | 31 | | |
| |||
Lines changed: 1 addition & 0 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
30 | 30 | | |
31 | 31 | | |
32 | 32 | | |
| 33 | + | |
33 | 34 | | |
34 | 35 | | |
35 | 36 | | |
| |||
Lines changed: 1 addition & 0 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
64 | 64 | | |
65 | 65 | | |
66 | 66 | | |
| 67 | + | |
67 | 68 | | |
68 | 69 | | |
69 | 70 | | |
| |||
Lines changed: 1 addition & 0 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
30 | 30 | | |
31 | 31 | | |
32 | 32 | | |
| 33 | + | |
33 | 34 | | |
34 | 35 | | |
35 | 36 | | |
| |||
Lines changed: 2 additions & 0 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
30 | 30 | | |
31 | 31 | | |
32 | 32 | | |
| 33 | + | |
33 | 34 | | |
34 | 35 | | |
35 | 36 | | |
| |||
47 | 48 | | |
48 | 49 | | |
49 | 50 | | |
| 51 | + | |
50 | 52 | | |
51 | 53 | | |
52 | 54 | | |
| |||
Lines changed: 1 addition & 0 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
30 | 30 | | |
31 | 31 | | |
32 | 32 | | |
| 33 | + | |
33 | 34 | | |
34 | 35 | | |
35 | 36 | | |
| |||
0 commit comments