Skip to content

Commit 5325902

Browse files
[TrimmableTypeMap] Add reflection-free TrimmableTypeMapType/ValueManager
Adds TrimmableTypeMapValueManager + reflection-free TrimmableTypeMapTypeManager and wires opt-in selection (RuntimeFeature.TrimmableTypeMap) in JNIEnvInit. Builds on the CoreCLR JavaMarshal split. Default stays managed; depends on array-codegen (#11753) and other slices to be fully functional at runtime. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
1 parent 85a605f commit 5325902

29 files changed

Lines changed: 1313 additions & 400 deletions

src/Mono.Android/Android.App/IntentFilterAttribute.Partial.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
#nullable enable
1+
#nullable disable
22

33
using System;
44

src/Mono.Android/Android.Graphics/Color.cs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -440,7 +440,10 @@ public override Type MarshalType {
440440
get { return typeof (int); }
441441
}
442442

443-
public override Color CreateGenericValue (ref JniObjectReference reference, JniObjectReferenceOptions options, Type targetType)
443+
public override Color CreateGenericValue (
444+
ref JniObjectReference reference,
445+
JniObjectReferenceOptions options,
446+
Type targetType)
444447
{
445448
throw new NotImplementedException ();
446449
}

src/Mono.Android/Android.Runtime/AndroidRuntime.cs

Lines changed: 22 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,10 @@ public override string GetCurrentManagedThreadStackTrace (int skipFrames, bool f
5858
if (!reference.IsValid)
5959
return null;
6060
var peeked = JniEnvironment.Runtime.ValueManager.PeekPeer (reference);
61+
if (peeked is JavaProxyThrowable proxyThrowable) {
62+
JniObjectReference.Dispose (ref reference, options);
63+
return proxyThrowable.InnerException;
64+
}
6165
var peekedExc = peeked as Exception;
6266
if (peekedExc == null) {
6367
var throwable = Java.Lang.Object.GetObject<Java.Lang.Throwable> (reference.Handle, JniHandleOwnership.DoNotTransfer);
@@ -310,10 +314,13 @@ public override void DeleteWeakGlobalReference (ref JniObjectReference value)
310314
}
311315
}
312316

313-
[UnconditionalSuppressMessage ("Trimming", "IL2026", Justification = "Temporary suppression for Java.Interop reflection manager base.")]
317+
[RequiresDynamicCode ("This type manager is reflection-backed and is not compatible with Native AOT.")]
318+
[RequiresUnreferencedCode ("This type manager is reflection-backed and is not trimming-compatible.")]
314319
class AndroidTypeManager : JniRuntime.ReflectionJniTypeManager {
315320
bool jniAddNativeMethodRegistrationAttributePresent;
316321

322+
const DynamicallyAccessedMemberTypes Constructors = DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.NonPublicConstructors;
323+
317324
public AndroidTypeManager (bool jniAddNativeMethodRegistrationAttributePresent)
318325
{
319326
this.jniAddNativeMethodRegistrationAttributePresent = jniAddNativeMethodRegistrationAttributePresent;
@@ -329,7 +336,6 @@ protected override IEnumerable<Type> GetTypesForSimpleReference (string jniSimpl
329336
yield return t;
330337
}
331338

332-
[UnconditionalSuppressMessage ("Trimming", "IL2073", Justification = "Temporary suppression until legacy typemap entries carry DAM annotations.")]
333339
protected override Type? GetTypeForSimpleReference (string jniSimpleReference)
334340
{
335341
var type = base.GetTypeForSimpleReference (jniSimpleReference);
@@ -346,29 +352,30 @@ protected override IEnumerable<Type> GetTypesForSimpleReference (string jniSimpl
346352
if (j != null) {
347353
return GetReplacementTypeCore (j) ?? j;
348354
}
349-
return base.GetSimpleReference (type);
355+
// Intentionally don't call base.GetSimpleReference(type): Android's
356+
// non-trimmable runtime uses the generated/registered typemap, not
357+
// Java.Interop's JniTypeSignatureAttribute fallback.
358+
return null;
350359
}
351360

352361
protected override IEnumerable<string> GetSimpleReferences (Type type)
353362
{
354363
string? j = JNIEnv.TypemapManagedToJava (type);
355-
j = GetReplacementTypeCore (j) ?? j;
364+
j = GetReplacementTypeCore (j) ?? j;
356365

357366
if (j != null) {
358-
yield return j;
359-
yield break;
360-
}
361-
foreach (var r in base.GetSimpleReferences (type)) {
362-
yield return r;
367+
return [j];
363368
}
369+
// Keep this in sync with GetSimpleReference(): no base fallback.
370+
return [];
364371
}
365372

366373
protected override IReadOnlyList<string>? GetStaticMethodFallbackTypesCore (string jniSimpleReference)
367374
{
368375
return JniRemappingLookup.GetStaticMethodFallbackTypes (jniSimpleReference, useReplacementTypes: true);
369376
}
370377

371-
protected override string? GetReplacementTypeCore (string jniSimpleReference)
378+
protected override string? GetReplacementTypeCore (string? jniSimpleReference)
372379
{
373380
return JniRemappingLookup.GetReplacementType (jniSimpleReference);
374381
}
@@ -393,8 +400,6 @@ protected override IEnumerable<string> GetSimpleReferences (Type type)
393400
static MethodInfo? dynamic_callback_gen;
394401

395402
// See ExportAttribute.cs
396-
[UnconditionalSuppressMessage ("Trimming", "IL2026", Justification = "Mono.Android.Export.dll is preserved when [Export] is used via [DynamicDependency].")]
397-
[UnconditionalSuppressMessage ("Trimming", "IL2075", Justification = "Mono.Android.Export.dll is preserved when [Export] is used via [DynamicDependency].")]
398403
static Delegate CreateDynamicCallback (MethodInfo method)
399404
{
400405
if (dynamic_callback_gen == null) {
@@ -489,20 +494,10 @@ static bool CallRegisterMethodByIndex (JniNativeMethodRegistrationArguments argu
489494
}
490495

491496
[Obsolete ("Use RegisterNativeMembers(JniType, Type, ReadOnlySpan<char>) instead.")]
492-
public override void RegisterNativeMembers (
493-
JniType nativeClass,
494-
Type type,
495-
string? methods) =>
497+
public override void RegisterNativeMembers (JniType nativeClass, Type type, string? methods) =>
496498
RegisterNativeMembers (nativeClass, type, methods.AsSpan ());
497499

498-
[UnconditionalSuppressMessage ("Trimming", "IL2057", Justification = "Type.GetType() can never statically know the string value parsed from parameter 'methods'.")]
499-
[UnconditionalSuppressMessage ("Trimming", "IL2067", Justification = "Delegate.CreateDelegate() can never statically know the string value parsed from parameter 'methods'.")]
500-
[UnconditionalSuppressMessage ("Trimming", "IL2070", Justification = "GetMethods can never statically know the string value parsed from parameter 'methods'.")]
501-
[UnconditionalSuppressMessage ("Trimming", "IL2072", Justification = "Delegate.CreateDelegate() can never statically know the string value parsed from parameter 'methods'.")]
502-
public override void RegisterNativeMembers (
503-
JniType nativeClass,
504-
Type type,
505-
ReadOnlySpan<char> methods)
500+
public override void RegisterNativeMembers (JniType nativeClass, Type type, ReadOnlySpan<char> methods)
506501
{
507502
try {
508503
if (methods.IsEmpty) {
@@ -586,15 +581,6 @@ public override void RegisterNativeMembers (
586581
} catch (Exception e) {
587582
JniEnvironment.Runtime.RaisePendingException (e);
588583
}
589-
590-
bool ShouldRegisterDynamically (string callbackTypeName, string callbackString, string typeName, string callbackName)
591-
{
592-
if (String.Compare (typeName, callbackTypeName, StringComparison.Ordinal) != 0) {
593-
return false;
594-
}
595-
596-
return String.Compare (callbackName, callbackString, StringComparison.Ordinal) == 0;
597-
}
598584
}
599585

600586
static int CountMethods (ReadOnlySpan<char> methodsSpan)
@@ -631,7 +617,8 @@ static void SplitMethodLine (
631617
}
632618
}
633619

634-
[UnconditionalSuppressMessage ("Trimming", "IL2026", Justification = "Temporary suppression for Java.Interop reflection manager base.")]
620+
[RequiresDynamicCode ("This value manager is reflection-backed and is not compatible with Native AOT.")]
621+
[RequiresUnreferencedCode ("This value manager is reflection-backed and is not trimming-compatible.")]
635622
class AndroidValueManager : JniRuntime.ReflectionJniValueManager {
636623

637624
Dictionary<IntPtr, IdentityHashTargets> instances = new Dictionary<IntPtr, IdentityHashTargets> ();
@@ -841,11 +828,7 @@ internal void RemovePeer (IJavaPeerable value, IntPtr hash)
841828
return null;
842829
}
843830

844-
public override void ActivatePeer (
845-
JniObjectReference reference,
846-
Type type,
847-
ConstructorInfo cinfo,
848-
object?[]? argumentValues)
831+
public override void ActivatePeer (JniObjectReference reference, Type type, ConstructorInfo cinfo, object?[]? argumentValues)
849832
{
850833
Java.Interop.TypeManager.Activate (reference.Handle, cinfo, argumentValues);
851834
}

src/Mono.Android/Android.Runtime/IJavaObjectValueMarshaler.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,8 @@ public override Expression CreateReturnValueFromManagedExpression (JniValueMarsh
4646
[RequiresUnreferencedCode (ExpressionRequiresUnreferencedCode)]
4747
public override Expression CreateParameterToManagedExpression (JniValueMarshalerContext context, ParameterExpression sourceValue, ParameterAttributes synchronize, Type? targetType)
4848
{
49+
ArgumentNullException.ThrowIfNull (targetType);
50+
4951
var r = Expression.Variable (targetType, sourceValue.Name + "_val");
5052
context.LocalVariables.Add (r);
5153
context.CreationStatements.Add (

src/Mono.Android/Android.Runtime/JNIEnv.cs

Lines changed: 26 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -27,20 +27,22 @@ public static partial class JNIEnv {
2727
static Array ArrayCreateInstance (Type elementType, int length)
2828
{
2929
if (RuntimeFeature.TrimmableTypeMap) {
30-
if (System.Runtime.CompilerServices.RuntimeFeature.IsDynamicCodeSupported) {
30+
if (RuntimeFeature.IsCoreClrRuntime) {
3131
// CoreCLR runtime type loader can construct any T[] dynamically.
3232
// IsDynamicCodeSupported is a [FeatureGuard] so this branch is
3333
// dead-coded under PublishAot.
3434
return Array.CreateInstance (elementType, length);
3535
}
3636

37-
// NativeAOT: resolve via per-rank typemap + Array.CreateInstanceFromArrayType.
38-
if (TrimmableTypeMap.Instance.TryGetArrayType (elementType, out var arrayType)) {
39-
return Array.CreateInstanceFromArrayType (arrayType, length);
37+
if (RuntimeFeature.IsNativeAotRuntime) {
38+
// NativeAOT: resolve via per-rank typemap + generated array proxy.
39+
if (TrimmableTypeMap.Instance.TryGetArrayProxy (elementType, additionalRank: 1, out var arrayProxy)) {
40+
return arrayProxy.CreateManagedArray (length);
41+
}
4042
}
4143

4244
throw new NotSupportedException (
43-
$"No TrimmableTypeMap array entry for element type '{elementType}'. " +
45+
$"No TrimmableTypeMap array proxy entry for element type '{elementType}'. " +
4446
$"Array lookups use the element type within the per-rank __ArrayMapRank{GetArrayRank (elementType)} typemap group; " +
4547
$"ensure the mapping is emitted for that rank (for example by increasing _AndroidTrimmableTypeMapMaxArrayRank) or report an issue.");
4648
}
@@ -64,13 +66,6 @@ static int GetArrayRank (Type elementType)
6466
return rank;
6567
}
6668

67-
static Type MakeArrayType (Type type) =>
68-
// FIXME: https://github.com/xamarin/xamarin-android/issues/8724
69-
// IL3050 disabled in source: if someone uses NativeAOT, they will get the warning.
70-
#pragma warning disable IL3050
71-
type.MakeArrayType ();
72-
#pragma warning restore IL3050
73-
7469
internal static IntPtr IdentityHash (IntPtr v)
7570
{
7671
return JniEnvironment.References.GetIdentityHashCode (new JniObjectReference (v));
@@ -309,10 +304,7 @@ public static IntPtr FindClass (System.Type type)
309304
}
310305
sig = sig.AddArrayRank (rank);
311306

312-
JniObjectReference local_ref = JniEnvironment.Types.FindClass (sig.Name);
313-
IntPtr global_ref = local_ref.NewGlobalRef ().Handle;
314-
JniObjectReference.Dispose (ref local_ref);
315-
return global_ref;
307+
return FindClass (sig.Name);
316308
} catch (Java.Lang.Throwable e) {
317309
if (!((e is Java.Lang.NoClassDefFoundError) || (e is Java.Lang.ClassNotFoundException)))
318310
throw;
@@ -577,9 +569,9 @@ public static unsafe IntPtr NewString (char[]? text, int length)
577569
return JniEnvironment.Strings.NewString (s, length).Handle;
578570
}
579571

580-
static void AssertCompatibleArrayTypes (Type sourceType, IntPtr destArray)
572+
static void AssertCompatibleArrayTypes (Type srcElementType, IntPtr destArray)
581573
{
582-
IntPtr grefSource = FindClass (sourceType);
574+
IntPtr grefSource = FindArrayClassByElementType (srcElementType);
583575
IntPtr lrefDest = GetObjectClass (destArray);
584576
try {
585577
if (!IsAssignableFrom (grefSource, lrefDest)) {
@@ -592,9 +584,9 @@ static void AssertCompatibleArrayTypes (Type sourceType, IntPtr destArray)
592584
}
593585
}
594586

595-
static void AssertCompatibleArrayTypes (IntPtr sourceArray, Type destType)
587+
static void AssertCompatibleArrayTypes (IntPtr sourceArray, Type destElementType)
596588
{
597-
IntPtr grefDest = FindClass (destType);
589+
IntPtr grefDest = FindArrayClassByElementType (destElementType);
598590
IntPtr lrefSource = GetObjectClass (sourceArray);
599591
try {
600592
if (!IsAssignableFrom (lrefSource, grefDest)) {
@@ -607,12 +599,19 @@ static void AssertCompatibleArrayTypes (IntPtr sourceArray, Type destType)
607599
}
608600
}
609601

602+
static IntPtr FindArrayClassByElementType (Type elementType)
603+
{
604+
int rank = JavaNativeTypeManager.GetArrayInfo (elementType, out elementType) + 1;
605+
var typeSignature = JniRuntime.CurrentRuntime.TypeManager.GetTypeSignature (elementType).AddArrayRank (rank);
606+
return FindClass (typeSignature.Name);
607+
}
608+
610609
public static void CopyArray (IntPtr src, bool[] dest)
611610
{
612611
if (dest == null)
613612
throw new ArgumentNullException ("dest");
614613

615-
AssertCompatibleArrayTypes (src, typeof (bool[]));
614+
AssertCompatibleArrayTypes (src, destElementType: typeof (bool));
616615

617616
_GetBooleanArrayRegion (src, 0, dest.Length, dest);
618617
}
@@ -804,7 +803,7 @@ public static void CopyArray (IntPtr src, Array dest, Type? elementType = null)
804803
throw new ArgumentNullException ("dest");
805804

806805
if (elementType != null && elementType.IsValueType)
807-
AssertCompatibleArrayTypes (src, MakeArrayType (elementType));
806+
AssertCompatibleArrayTypes (src, destElementType: elementType);
808807

809808
if (elementType != null && elementType.IsArray) {
810809
for (int i = 0; i < dest.Length; ++i) {
@@ -840,7 +839,7 @@ public static void CopyArray<T> (IntPtr src, T[] dest)
840839
throw new ArgumentNullException ("dest");
841840

842841
if (typeof (T).IsValueType)
843-
AssertCompatibleArrayTypes (src, typeof (T[]));
842+
AssertCompatibleArrayTypes (src, destElementType: typeof (T));
844843

845844
if (typeof (T).IsArray) {
846845
CopyArray (src, dest, typeof (T));
@@ -858,7 +857,7 @@ public static unsafe void CopyArray (bool[] src, IntPtr dest)
858857
if (src == null)
859858
throw new ArgumentNullException ("src");
860859

861-
AssertCompatibleArrayTypes (typeof (bool[]), dest);
860+
AssertCompatibleArrayTypes (srcElementType: typeof (bool), dest);
862861

863862
fixed (bool* p = src)
864863
JniEnvironment.Arrays.SetBooleanArrayRegion (new JniObjectReference (dest), 0, src.Length, p);
@@ -947,7 +946,7 @@ public static void CopyArray (Array source, Type elementType, IntPtr dest)
947946
throw new ArgumentNullException ("elementType");
948947

949948
if (elementType.IsValueType)
950-
AssertCompatibleArrayTypes (MakeArrayType (elementType), dest);
949+
AssertCompatibleArrayTypes (srcElementType: elementType, dest);
951950

952951
Action<Array, IntPtr> converter = GetConverter (CopyManagedToNativeArray, elementType, dest);
953952

@@ -1072,7 +1071,7 @@ public static void CopyArray<T> (T[] src, IntPtr dest)
10721071
return null;
10731072

10741073
if (element_type != null && element_type.IsValueType)
1075-
AssertCompatibleArrayTypes (array_ptr, MakeArrayType (element_type));
1074+
AssertCompatibleArrayTypes (array_ptr, destElementType: element_type);
10761075

10771076
int cnt = _GetArrayLength (array_ptr);
10781077

@@ -1119,7 +1118,7 @@ static int _GetArrayLength (IntPtr array_ptr)
11191118
return null;
11201119

11211120
if (typeof (T).IsValueType)
1122-
AssertCompatibleArrayTypes (array_ptr, typeof (T[]));
1121+
AssertCompatibleArrayTypes (array_ptr, destElementType: typeof (T));
11231122

11241123
int cnt = _GetArrayLength (array_ptr);
11251124
T[] ret = new T [cnt];

0 commit comments

Comments
 (0)