diff --git a/build-tools/automation/yaml-templates/stage-package-tests.yaml b/build-tools/automation/yaml-templates/stage-package-tests.yaml index 19bc0d5fbcd..1cfaf91277a 100644 --- a/build-tools/automation/yaml-templates/stage-package-tests.yaml +++ b/build-tools/automation/yaml-templates/stage-package-tests.yaml @@ -178,6 +178,15 @@ stages: artifactSource: bin/Test$(XA.Build.Configuration)/$(DotNetTargetFramework)-android/Mono.Android.NET_Tests-Signed.aab artifactFolder: $(DotNetTargetFramework)-NativeAOT + - template: /build-tools/automation/yaml-templates/apk-instrumentation.yaml + parameters: + configuration: $(XA.Build.Configuration) + testName: Mono.Android.NET_Tests-NativeAOTTrimmable + project: tests/Mono.Android-Tests/Mono.Android-Tests/Mono.Android.NET-Tests.csproj + extraBuildArgs: -p:TestsFlavor=NativeAOTTrimmable -p:PublishAot=true -p:AndroidTypeMapImplementation=trimmable + artifactSource: bin/Test$(XA.Build.Configuration)/$(DotNetTargetFramework)-android/Mono.Android.NET_Tests-Signed.aab + artifactFolder: $(DotNetTargetFramework)-NativeAOTTrimmable + - template: /build-tools/automation/yaml-templates/apk-instrumentation.yaml parameters: configuration: $(XA.Build.Configuration) diff --git a/external/Java.Interop/src/Java.Interop/Java.Interop/JniBuiltinMarshalers.cs b/external/Java.Interop/src/Java.Interop/Java.Interop/JniBuiltinMarshalers.cs index f51f6b2e8f0..cb94ae797aa 100644 --- a/external/Java.Interop/src/Java.Interop/Java.Interop/JniBuiltinMarshalers.cs +++ b/external/Java.Interop/src/Java.Interop/Java.Interop/JniBuiltinMarshalers.cs @@ -93,6 +93,11 @@ static bool GetBuiltInTypeSignature (Type type, ref JniTypeSignature signature) signature = GetCachedTypeSignature (ref __BooleanNullableTypeSignature, "java/lang/Boolean"); return true; } + if (type == typeof (Byte?)) { + signature = GetCachedTypeSignature (ref __SByteNullableTypeSignature, "java/lang/Byte"); + return true; + } + if (type == typeof (SByte?)) { signature = GetCachedTypeSignature (ref __SByteNullableTypeSignature, "java/lang/Byte"); return true; @@ -162,6 +167,7 @@ static KeyValuePair[] InitJniBuiltinMarshalers () return new []{ new KeyValuePair(typeof (string), JniStringValueMarshaler.Instance), new KeyValuePair(typeof (JavaProxyObject), ProxyValueMarshaler.Instance), + new KeyValuePair(typeof (Byte?), JniNullableByteValueMarshaler.Instance), new KeyValuePair(typeof (Boolean), JniBooleanValueMarshaler.Instance), new KeyValuePair(typeof (Boolean?), JniNullableBooleanValueMarshaler.Instance), new KeyValuePair(typeof (SByte), JniSByteValueMarshaler.Instance), @@ -465,6 +471,37 @@ public override void DestroyGenericArgumentState (SByte? value, ref JniValueMars } } + sealed class JniNullableByteValueMarshaler : JniValueMarshaler { + + internal static readonly JniNullableByteValueMarshaler Instance = new JniNullableByteValueMarshaler (); + + public override Byte? CreateGenericValue ( + ref JniObjectReference reference, + JniObjectReferenceOptions options, + Type? targetType) + { + if (!reference.IsValid) + return null; + + return unchecked ((Byte) JniByte.GetValueFromJni (ref reference, options, targetType: null)); + } + + public override JniValueMarshalerState CreateGenericObjectReferenceArgumentState ([MaybeNull] Byte? value, ParameterAttributes synchronize) + { + if (!value.HasValue) + return new JniValueMarshalerState (); + var r = JniByte.CreateLocalRef (unchecked ((SByte) value.Value)); + return new JniValueMarshalerState (r); + } + + public override void DestroyGenericArgumentState (Byte? value, ref JniValueMarshalerState state, ParameterAttributes synchronize) + { + var r = state.ReferenceValue; + JniObjectReference.Dispose (ref r); + state = new JniValueMarshalerState (); + } + } + static class JniCharacter { internal const string JniTypeName = "java/lang/Character"; diff --git a/external/Java.Interop/src/Java.Interop/Java.Interop/JniBuiltinMarshalers.tt b/external/Java.Interop/src/Java.Interop/Java.Interop/JniBuiltinMarshalers.tt index 61d0d25cf00..b941af796b0 100644 --- a/external/Java.Interop/src/Java.Interop/Java.Interop/JniBuiltinMarshalers.tt +++ b/external/Java.Interop/src/Java.Interop/Java.Interop/JniBuiltinMarshalers.tt @@ -84,6 +84,11 @@ namespace Java.Interop { if (!type.IsValueType) return false; + if (type == typeof (Byte?)) { + signature = GetCachedTypeSignature (ref __SByteNullableTypeSignature, "java/lang/Byte"); + return true; + } + <# foreach (var type in types) { #> @@ -127,6 +132,7 @@ namespace Java.Interop { return new []{ new KeyValuePair(typeof (string), JniStringValueMarshaler.Instance), new KeyValuePair(typeof (JavaProxyObject), ProxyValueMarshaler.Instance), + new KeyValuePair(typeof (Byte?), JniNullableByteValueMarshaler.Instance), <# foreach (var type in types) { #> @@ -284,6 +290,41 @@ namespace Java.Interop { } } <# + if (type.UnsignedType == "Byte") { +#> + + sealed class JniNullableByteValueMarshaler : JniValueMarshaler { + + internal static readonly JniNullableByteValueMarshaler Instance = new JniNullableByteValueMarshaler (); + + public override Byte? CreateGenericValue ( + ref JniObjectReference reference, + JniObjectReferenceOptions options, + Type? targetType) + { + if (!reference.IsValid) + return null; + + return unchecked ((Byte) JniByte.GetValueFromJni (ref reference, options, targetType: null)); + } + + public override JniValueMarshalerState CreateGenericObjectReferenceArgumentState ([MaybeNull] Byte? value, ParameterAttributes synchronize) + { + if (!value.HasValue) + return new JniValueMarshalerState (); + var r = JniByte.CreateLocalRef (unchecked ((SByte) value.Value)); + return new JniValueMarshalerState (r); + } + + public override void DestroyGenericArgumentState (Byte? value, ref JniValueMarshalerState state, ParameterAttributes synchronize) + { + var r = state.ReferenceValue; + JniObjectReference.Dispose (ref r); + state = new JniValueMarshalerState (); + } + } +<# + } } #> } diff --git a/external/Java.Interop/tests/Java.Interop-Tests/Java.Interop/JavaObjectArrayTest.cs b/external/Java.Interop/tests/Java.Interop-Tests/Java.Interop/JavaObjectArrayTest.cs index 3e5b9c9fc56..e5d2bc2fa98 100644 --- a/external/Java.Interop/tests/Java.Interop-Tests/Java.Interop/JavaObjectArrayTest.cs +++ b/external/Java.Interop/tests/Java.Interop-Tests/Java.Interop/JavaObjectArrayTest.cs @@ -93,6 +93,22 @@ public void ObjectArrayType () } } + [TestFixture] + public class JavaObjectArray_NullableByte_ContractTest : JavaObjectArrayContractTest { + protected override byte? CreateValueA () {return 128;} + protected override byte? CreateValueB () {return 255;} + + [Test] + public void NullableByteValues () + { + var values = new byte? [] { 1, null, 127, 128, 200, 255 }; + using (var array = new JavaObjectArray (values)) { + Assert.AreEqual ("[Ljava/lang/Byte;", array.GetJniTypeName ()); + CollectionAssert.AreEqual (values, array.ToArray ()); + } + } + } + [TestFixture] public class JavaObjectArray_Int32Array_ContractTest : JavaObjectArrayContractTest { protected override int[] CreateValueA () {return new[]{1};} diff --git a/external/Java.Interop/tests/Java.Interop-Tests/Java.Interop/JniTypeManagerTests.cs b/external/Java.Interop/tests/Java.Interop-Tests/Java.Interop/JniTypeManagerTests.cs index 198cb9c97fd..3a407b527d1 100644 --- a/external/Java.Interop/tests/Java.Interop-Tests/Java.Interop/JniTypeManagerTests.cs +++ b/external/Java.Interop/tests/Java.Interop-Tests/Java.Interop/JniTypeManagerTests.cs @@ -32,7 +32,11 @@ public void GetTypeSignature_Type () AssertGetJniTypeInfoForType (typeof (bool), "Z", true, 0); AssertGetJniTypeInfoForType (typeof (void), "V", true, 0); - AssertGetJniTypeInfoForType (typeof (float?), "java/lang/Float", true, 0); + AssertGetJniTypeInfoForType (typeof (float?), "java/lang/Float", true, 0); + AssertGetJniTypeInfoForType (typeof (byte?), "java/lang/Byte", true, 0); + AssertGetJniTypeInfoForType (typeof (byte?[]), "[Ljava/lang/Byte;", true, 1); + AssertGetJniTypeInfoForType (typeof (byte?[][]), "[[Ljava/lang/Byte;", true, 2); + AssertGetJniTypeInfoForType (typeof (byte?[][][]), "[[[Ljava/lang/Byte;", true, 3); AssertGetJniTypeInfoForType (typeof (JavaObject), "java/lang/Object", false, 0); @@ -136,6 +140,7 @@ static void AssertGetJniTypeInfoForType (Type type, string jniType, bool isKeywo Assert.AreEqual (typeof (float), GetType ("F")); Assert.AreEqual (typeof (double), GetType ("D")); Assert.AreEqual (typeof (string), GetType ("java/lang/String")); + Assert.AreEqual (typeof (sbyte?), GetType ("java/lang/Byte")); Assert.AreEqual (typeof (float?), GetType ("java/lang/Float")); Assert.AreEqual (null, GetType ("com/example/does/not/exist")); Assert.AreEqual (null, GetType ("Lcom/example/does/not/exist;")); diff --git a/external/Java.Interop/tests/Java.Interop-Tests/Java.Interop/JniValueMarshalerContractTests.cs b/external/Java.Interop/tests/Java.Interop-Tests/Java.Interop/JniValueMarshalerContractTests.cs index 28a9f659eab..b241836a9d9 100644 --- a/external/Java.Interop/tests/Java.Interop-Tests/Java.Interop/JniValueMarshalerContractTests.cs +++ b/external/Java.Interop/tests/Java.Interop-Tests/Java.Interop/JniValueMarshalerContractTests.cs @@ -282,6 +282,8 @@ protected static string GetTypeName (Type type) switch (type.Name) { case "Boolean": return "bool"; + case "Byte": + return "byte"; case "Char": return "char"; case "Double": @@ -422,6 +424,12 @@ public class JniValueMarshaler_NullableSByte_ContractTests : JniValueMarshalerCo protected override sbyte? Value {get {return (sbyte) 2;}} } + [TestFixture] + [Category("LLVMIgnore")] //FIXME: https://github.com/dotnet/runtime/issues/89190 + public class JniValueMarshaler_NullableByte_ContractTests : JniValueMarshalerContractTests { + protected override byte? Value {get {return (byte) 200;}} + } + [TestFixture] [Category("LLVMIgnore")] //FIXME: https://github.com/dotnet/runtime/issues/89190 public class JniValueMarshaler_NullableChar_ContractTests : JniValueMarshalerContractTests { diff --git a/tests/Mono.Android-Tests/Mono.Android-Tests/Android.Runtime/JnienvArrayMarshaling.cs b/tests/Mono.Android-Tests/Mono.Android-Tests/Android.Runtime/JnienvArrayMarshaling.cs index 66e143855b9..3f500aae55f 100644 --- a/tests/Mono.Android-Tests/Mono.Android-Tests/Android.Runtime/JnienvArrayMarshaling.cs +++ b/tests/Mono.Android-Tests/Mono.Android-Tests/Android.Runtime/JnienvArrayMarshaling.cs @@ -243,13 +243,8 @@ public void GetArray_NullableByte () } [Test] - [Category ("NativeAOTTrimmable")] public void GetArray_NullableByteArrayArray () { - if (!Microsoft.Android.Runtime.RuntimeFeature.TrimmableTypeMap) { - Assert.Ignore ("Test only relevant for the trimmable typemap path."); - } - var values = new [] { new byte? [] { 1, null, 200 }, new byte? [] { 255, 128 }, @@ -265,6 +260,32 @@ public void GetArray_NullableByteArrayArray () } } + [Test] + public void GetArray_NullableByteArrayArrayArray () + { + var values = new [] { + new [] { + new byte? [] { 1, null, 200 }, + new byte? [] { 255, 128 }, + }, + new [] { + new byte? [] { null, 42 }, + }, + }; + using (var array = new Java.Lang.Object (JNIEnv.NewArray (values), JniHandleOwnership.TransferLocalRef)) { + Assert.AreEqual ("[[[Ljava/lang/Byte;", JNIEnv.GetClassNameFromInstance (array.Handle)); + + var copy = JNIEnv.GetArray (array.Handle); + Assert.AreEqual (values.Length, copy.Length); + for (int i = 0; i < values.Length; i++) { + Assert.AreEqual (values [i].Length, copy [i].Length); + for (int j = 0; j < values [i].Length; j++) { + AssertArrays ($"GetArray[{i}][{j}]", copy [i][j], values [i][j]); + } + } + } + } + [Test] public void GetArray_JavaLangStringArrayToJavaLangObjectArray () { diff --git a/tests/Mono.Android-Tests/Mono.Android-Tests/Java.Interop/TrimmableTypeMapTypeManagerTests.cs b/tests/Mono.Android-Tests/Mono.Android-Tests/Java.Interop/TrimmableTypeMapTypeManagerTests.cs index 92a87a99a62..edcdcc09338 100644 --- a/tests/Mono.Android-Tests/Mono.Android-Tests/Java.Interop/TrimmableTypeMapTypeManagerTests.cs +++ b/tests/Mono.Android-Tests/Mono.Android-Tests/Java.Interop/TrimmableTypeMapTypeManagerTests.cs @@ -34,14 +34,15 @@ public void GetStaticMethodFallbackTypes_ReturnsDesugarFallbacks (string jniSimp Assert.AreEqual (expectedFallback, fallbacks [1]); } - [Test] + [TestCase (typeof (byte?[][]), "[[Ljava/lang/Byte;")] + [TestCase (typeof (byte?[][][]), "[[[Ljava/lang/Byte;")] [Category ("NativeAOTTrimmable")] - public void GetTypeSignature_NullableByteArrayArray_ReturnsJavaLangByte () + public void GetTypeSignature_NullableByteJaggedArray_ReturnsJavaLangByte (Type type, string expected) { using var manager = new TestableTrimmableTypeMapTypeManager (); - var signature = manager.GetTypeSignature (typeof (byte?[][])); + var signature = manager.GetTypeSignature (type); - Assert.AreEqual ("[[Ljava/lang/Byte;", signature.Name); + Assert.AreEqual (expected, signature.Name); } // Verifies the generic-type-definition fallback in GetProxyForManagedType: