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
[Java.Base] Begin binding JDK-11 java.base module (#909)
Context: #858
What do *I* want? To be able to use our wonderful Java binding
infrastructure against *Desktop Java*, not just Android.
At the same time, I don't want "Android-isms" "leaking" into such a
binding. *Just* Java.Interop, no xamarin-android.
"Take over" the `generator --codegen-target=JavaInterop1` format
so that it *isn't* useful for Xamarin.Android, and is instead usable
for non-Android usage.
This is a work-in-progress, and *far* from complete. For prototype
purposes, this *only* binds:
* `java.lang.Class`
* `java.lang.Math`
* `java.lang.Number`
* `java.lang.Object`
* `java.lang.Throwable`
The `Java.Base` binding is only for .NET 6 and above. I'm not
interested in .NET Standard support at this point in time.
~~ Build Changes ~~
Additionally, this `Java.Base` binding is only for the
`java.base.jmod` file provided by JDK-11. `make prepare` is updated
to look for a JDK-11 installation path.
~~ Test Changes ~~
Update `samples/Hello` so that it (1) works, and (2) instantiates the
`Java.Lang.Object` binding:
dotnet run --project samples/Hello
Update `tests/generator-Tests` so that
`tests/generator-Tests/Unit-Tests/CodeGeneratorExpectedResults/JavaInterop1`
is now used *exclusively* for `generator --codegen-target=JavaInterop1`
output; `generator --codegen-target=XAJavaInterop1` now has its own
separate files in
`tests/generator-Tests/Unit-Tests/CodeGeneratorExpectedResults/XAJavaInterop1`.
(This contributes to much of the commit diff.)
Update `tests/generator-Tests/SupportFiles` so that types in
`Android.*` are excluded when `JAVA_INTEROP1` is `#define`d, and
update the unit test infrastructure so that building for `JavaInterop1`
causes `JAVA_INTEROP1` to be `#define`d. This allows many of the unit
tests to ensure that *some* `JavaInterop1` constructs *compile*.
However, not all unit tests compile under `JavaInterop1`. The new
`BaseGeneratorTest.TryJavaInterop1` property can be set to false to
prevent compiling a test suite using `JavaInterop1`. This will allow
us to slowly implement `JavaInterop1` support over time.
The build logs will also now emit a command-line that can be used to
manually compile unit test code. See e.g.
`bin/TestDebug/TestOutput-generator-Tests.txt`.
~~ API Changes ~~
Update `Java.Interop.JavaObject` so that
`JavaObject.DisposeUnlessReferenced()` is now `virtual`.
Override `DisposeUnlessReferenced()` from the `Java*Array` types
so that if the instance was created via the new
`JniEnvironment.Arrays.CreateMarshal*Array()` methods, the array
instance will be disposed. This is intended for marshaling array
parameters:
public void WithArray(int[] array)
{
const string __id = "withArray.[I";
var native_array = JniEnvironment.Arrays.CreateMarshalInt32Array (array);
try {
JniArgumentValue* __args = stackalloc JniArgumentValue [1];
__args [0] = new JniArgumentValue (native_array);
_members.StaticMethods.InvokeVoidMethod (__id, __args);
} finally {
if (array != null) native_array.DisposeUnlessReferenced ();
}
}
Add `Java.Interop.JavaTypeParametersAttribute(string[] typeParameters)`
from Xamarin.Android.
~~ Bindings vs. Xamarin.Android ~~
Pull in `src/Java.Base/Transforms/map.csv` [from xamarin-android][0],
removing the `android*` types.
Instead of `[Android.Runtime.RegisterAttribute]` on types, use
`[Java.Interop.JniTypeSignatureAttribute]`.
Java arrays are bound as appropriate `IList<T>`, using the
`Java.Interop.Java*Array` types as an intermediary. This should help
reduce marshaling logic & overhead, as if the "source" array is a
`Java*Array`, it doesn't need to be "deep copied". The exception is
C# `params` arrays, which continue to be bound as arrays, and are
marshaled via an appropriate `Java*Array` type.
`java.io.InputStream` isn't bound as `System.IO.Stream`, etc.
"Java.Interop-style" constructors are used (25de1f3), e.g.
// This
DeclaringType (ref JniObjectReference reference, JniObjectReferenceOptions options);
// Not Xamarin.Android-style
DeclaringType (IntPtr handle, JniHandleOwnership transfer);
"Java.Interop-style" wrapper construction is used, e.g.
// This
var wrapper = JniEnvironment.Runtime.ValueManager.GetValue<DeclaringType>(ref h, JniObjectReferenceOptions.CopyAndDispose);
// Not this
var wrapper = Java.Lang.Object.GetObject<DeclaringType>(handle);
~~ TODO: Marshal Methods ~~
Marshal methods are currently skipped. Java-to-managed invocations
are not currently supported.
Xamarin.Android uses Java Callable Wrappers + `Runtime.register()`
to specify which methods to register, via lots of reflection, etc.
Proposal: For Desktop, JCW's shouldn't need to specify all the
methods to register. Instead, use the `jnimarshalmethod-gen`-
originated strategy of `[JniAddNativeMethodRegistrationAttribute]`
within the binding, and then have it use `MarshalMethodBuilder` to
generate the marshal methods. Need to update `MarshalMethodBuilder`
to look for overrides in addition to methods with `[JavaCallable]`,
which in turn may require an equivalent to
`[Android.Runtime.RegisterAttribute(…)]`.
Perhaps `[JniMethodSignatureAttribute(string name, string sig)]`?
In the meantime, `Java.Base` will skip all marshal-method logic
plus runtime method generation. Leave that for later.
~~ TODO: Other Binding Changes? ~~
We should eventually "unify" `java.lang.Object` and `System.Object`.
Consider `java.lang.Class`:
/* partial */ class Class<T> {
public boolean isInstance(java.lang.Object);
public java.lang.Object[] getSigners();
}
If we unify `java.lang.Object` and `System.Object`, we'd have a
binding of:
partial class Class {
public bool IsInstance (object value);
public IList<object> GetSigners();
}
~~ Open Questions ~~
What's up with `java.lang.Class.getAnnotationsByType()`?
During an iteration of this PR, I got:
public unsafe Java.Interop.JavaObjectArray<Java.Lang.Object>? GetAnnotationsByType (Java.Lang.Class? annotationClass)
{
const string __id = "getAnnotationsByType.(Ljava/lang/Class;)[Ljava/lang/annotation/Annotation;";
From `__id` we see that the Java return type is `Annotation[]`, yet
we bind it as an `Object` array? This is because of
[#669][1].
That said, it's currently "differently *worse*"; I don't know why,
but `__id` is now:
const string __id = "getAnnotationsByType.(Ljava/lang/Class;)[Ljava/lang/Object;";
i.e. the return type is an `Object` array instead of an `Annotation`
array, which is wrong, as per `javap`:
% javap -s java.lang.Class
…
public <A extends java.lang.annotation.Annotation> A getAnnotation(java.lang.Class<A>);
descriptor: (Ljava/lang/Class;)Ljava/lang/annotation/Annotation;
This needs additional investigation.
[0]: https://github.com/xamarin/xamarin-android/blob/99523feab02e8622a3357e9e6a025f5afc44c970/src/Mono.Android/map.csv
[1]: #669
0 commit comments