Skip to content

Commit d4d88e2

Browse files
[TrimmableTypeMap] Reflection-free TrimmableTypeMapType/ValueManager (#11801)
## Summary This PR adds the two runtime managers that make the **trimmable typemap** a fully functional, reflection-free peer-resolution path: - **`TrimmableTypeMapValueManager`** — creates and tracks Java↔managed peers without reflection or `Activator`. - **`TrimmableTypeMapTypeManager`** — resolves managed↔Java type mappings by delegating to the generated `TrimmableTypeMap`. Both are selected through the existing `RuntimeFeature.TrimmableTypeMap` switch and wired into `JNIEnvInit`. The trimmable path is **opt-in**; the NativeAOT default remains `managed`, and Mono/CoreCLR defaults are unchanged. Also included is the `JavaConvert` collection-factory refactor and a generator fix that the trimmable managers depend on. ## Background The classic Java.Interop runtime resolves the managed `Type` for a Java instance (and vice-versa) using `Type.GetType`, `MakeGenericType`, and `Activator`-style construction. That is incompatible with trimming and NativeAOT: the trimmer can't see which types are reachable, and the types can't be created at runtime under AOT. The **trimmable typemap** replaces those reflection calls with a build-time-generated map of `JavaPeerProxy` objects (produced by `Microsoft.Android.Sdk.TrimmableTypeMap`). Each proxy knows how to construct a specific peer and describes its JNI ↔ managed association statically. Earlier PRs landed the generator, the proxy shapes (array / generic-base / interface / unresolvable peers), and the CoreCLR `JavaMarshal` value-manager split. **This PR adds the runtime managers that consume that generated map.** ## What's in this PR ### 1. `TrimmableTypeMapValueManager` (new) A `JniRuntime.JniValueManager` that performs peer creation with **no reflection**: - **Peer lifetime** (`AddPeer` / `PeekPeer` / `RemovePeer` / `FinalizePeer` / `CollectPeers` / `GetSurfacedPeers`) is delegated to `JavaMarshalRegisteredPeers`, i.e. the CoreCLR `JavaMarshal` GC-bridge machinery. `WaitForGCBridgeProcessing` is intentionally a no-op (documented: the wait can't close the bridge race on CoreCLR, where JNI wrapper threads hold their own `JniObjectReference` copies). - **`CreatePeer`** resolves the requested target type (mapping `object`/`IJavaPeerable` → `Java.Interop.JavaObject`, `Exception` → `JavaException`) and asks `TrimmableTypeMap.Instance.CreateInstance (handle, resolvedType)` to build the peer from a generated proxy. - **`ActivatePeer`** throws `PlatformNotSupportedException` — reflection-based activation is not part of this path. - **`NotFoundFallback`** carefully reproduces the base `JniValueManager.CreatePeer` contract so `JavaCast`/`JavaAs` still surface the correct outcome when no proxy is found: - target type has no Java mapping → `ArgumentException` - Java instance not assignable to the target's Java class → `null` (so `JavaAs` returns null / `JavaCast` throws `InvalidCastException`) - compatible classes but no proxy → `NotSupportedException` (a genuine generator gap, with a message pointing at the missing proxy) The assignability check honors `RuntimeFeature.IsAssignableFromCheck` and mirrors the legacy cast diagnostic when assembly logging is enabled. ### 2. `TrimmableTypeMapTypeManager` (new) A `JniRuntime.JniTypeManager` that has exactly two live responsibilities and throws for everything else it doesn't need: - **Managed → Java** via `GetTypeSignatureCore`, backed by a `ConcurrentDictionary<Type, JniTypeSignature>` cache. - **Java → managed** via `GetTypes` / `GetTypeForSimpleReference`, delegating to `TrimmableTypeMap`. - **Array handling** diverges by runtime: NativeAOT reads a pre-generated array-proxy map (types can't be built at runtime), while CoreCLR builds array/generic types dynamically to save app size (suppressions are scoped to the CoreCLR-only branch). ### 3. `JNIEnvInit` wiring `CreateValueManager` / `CreateTypeManager` now return the trimmable managers when `RuntimeFeature.TrimmableTypeMap` is set. The existing Mono / CoreCLR / managed selection is preserved, and the manager constructions were refactored into small local helpers so trimming suppressions (`IL2026` / `IL3050`) apply only to the exact branch that needs them. `RegisterJniNatives` is likewise gated so the reflection-based JNI registration path isn't emitted for the trimmable typemap. ### 4. `JavaConvert` collection-factory refactor Generic collection marshalling (`IDictionary<,>` → `JavaDictionary<,>`, `IList<>` → `JavaList<>`, `ICollection<>` → `JavaCollection<>`) is split into two branches: a **factory-based converter** on the trimmable path (no `MakeGenericType`) and the classic `MakeGenericType` path elsewhere, with the reflection-requiring code isolated behind narrowly-scoped suppressions. Also adds `Nullable<T>` converter handling (null reference → null value). ### 5. Generator fix (`ModelBuilder`) Emits a managed→Java typemap entry for **self-peer types** (`[JniTypeSignature(GenerateJavaPeer=false)]` or MCW bindings with no activation ctor). These are constructed managed-side with `new`, but their JNI name must still resolve so the correct Java class is instantiated; without the association they fell back to the generic `mono.android.runtime.JavaObject` peer and threw `ArrayStoreException` when placed into a typed Java array. ### 6. Cleanup Removes the dead `TrimmableTypeMap` branch from `JavaMarshalValueManager` (that logic now lives in the dedicated `TrimmableTypeMapValueManager`). ## Behavioral impact - **Opt-in only.** With `RuntimeFeature.TrimmableTypeMap` unset, behavior is unchanged. NativeAOT still defaults to `managed`. - No new user-facing / localized strings; error messages point at the generator when a proxy is genuinely missing. ## Status All earlier prerequisites have **merged into `main`**, and this PR is **rebased on latest `main`**, so it is no longer stacked or blocked — it contains only the value/type-manager implementations (plus the supporting `JavaConvert` and generator changes) on top of them: - #11799 — CoreCLR `JavaMarshal` split + Java.Interop bump - #11753 array proxies · #11749 generic base · #11751 unresolvable peers · #11769 interface proxies - #11794 multidex/manifest base · #11796 manifest parity · #11798 R8 keep ## Testing - `TrimmableTypeMapTypeManagerTests` and `TypeMapModelBuilderTests` updated/extended for the new type resolution and the self-peer generator fix. - Export tests enabled for the trimmable typemap; `TrimmableTypeMapUnsupported` cases excluded. - NativeAOT warning-count and CoreCLR `apkdesc` baselines updated to match.
1 parent 8851832 commit d4d88e2

29 files changed

Lines changed: 1113 additions & 489 deletions

File tree

external/Java.Interop/src/Java.Interop/Java.Interop/JniRuntime.JniValueManager.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -310,7 +310,7 @@ protected virtual bool TryUnboxPeerObject (IJavaPeerable value, [NotNullWhen (tr
310310
public JniValueMarshaler<T> GetValueMarshaler<T> () => GetValueMarshalerCore<T> ();
311311
protected abstract JniValueMarshaler<T> GetValueMarshalerCore<T> ();
312312

313-
internal JniObjectReference CreateLocalObjectReferenceArgument (Type type, object? value)
313+
public JniObjectReference CreateLocalObjectReferenceArgument (Type type, object? value)
314314
{
315315
EnsureNotDisposed ();
316316

external/Java.Interop/src/Java.Interop/PublicAPI.Unshipped.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ virtual Java.Interop.JniRuntime.JniTypeManager.GetTypeForSimpleReference(string!
88
virtual Java.Interop.JniRuntime.JniTypeManager.GetTypeSignatureCore(System.Type! type) -> Java.Interop.JniTypeSignature
99
virtual Java.Interop.JniRuntime.JniTypeManager.GetTypeSignaturesCore(System.Type! type) -> System.Collections.Generic.IEnumerable<Java.Interop.JniTypeSignature>!
1010
abstract Java.Interop.JniRuntime.JniValueManager.CreateLocalObjectReferenceArgumentCore(System.Type! type, object? value) -> Java.Interop.JniObjectReference
11+
Java.Interop.JniRuntime.JniValueManager.CreateLocalObjectReferenceArgument(System.Type! type, object? value) -> Java.Interop.JniObjectReference
1112
Java.Interop.JavaException.JavaException(ref Java.Interop.JniObjectReference reference, Java.Interop.JniObjectReferenceOptions transfer, Java.Interop.JniObjectReference throwableOverride) -> void
1213
Java.Interop.JavaException.SetJavaStackTrace(Java.Interop.JniObjectReference peerReferenceOverride = default(Java.Interop.JniObjectReference)) -> void
1314
Java.Interop.JniRuntime.ReflectionJniTypeManager

src/Microsoft.Android.Runtime.NativeAOT/Java.Interop/JreRuntime.cs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,11 @@ static JniRuntime.JniTypeManager CreateDefaultTypeManager ()
8181
return new TrimmableTypeMapTypeManager ();
8282
}
8383

84-
return new ManagedTypeManager ();
84+
return CreateManagedTypeManager ();
85+
86+
[UnconditionalSuppressMessage ("Trimming", "IL2026", Justification = "Managed type manager is preserved by the MarkJavaObjects trimmer step.")]
87+
[UnconditionalSuppressMessage ("Trimming", "IL3050", Justification = "This type manager won't be used in Native AOT builds in the future.")]
88+
static JniRuntime.JniTypeManager CreateManagedTypeManager () => new ManagedTypeManager ();
8589
}
8690

8791
[UnconditionalSuppressMessage ("Trimming", "IL2026", Justification = "CoreCLR value manager is preserved by the MarkJavaObjects trimmer step.")]

src/Microsoft.Android.Sdk.TrimmableTypeMap/Generator/ModelBuilder.cs

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -145,7 +145,14 @@ static void EmitPeers (TypeMapAssemblyData model, string jniName,
145145
if (!isAliasGroup) {
146146
// Single peer — no aliases needed, emit directly with the base JNI name
147147
var peer = peersForName [0];
148-
bool hasProxy = peer.ActivationCtor != null || peer.InvokerTypeName != null;
148+
// A concrete type that supplies its own Java peer ([JniTypeSignature(GenerateJavaPeer=false)]
149+
// or an MCW binding without an activation ctor) is constructed managed-side via `new`, so its
150+
// managed→Java JNI name must still be resolvable in order to instantiate the correct Java class.
151+
// Such types have neither an activation ctor nor an invoker; without a proxy + association they
152+
// fall back to the generic mono.android.runtime.JavaObject peer and throw ArrayStoreException
153+
// when stored into a typed Java array (e.g. CrossReferenceBridge[]).
154+
bool needsManagedToJavaName = peer.DoNotGenerateAcw && !peer.IsInterface && !peer.IsAbstract;
155+
bool hasProxy = peer.ActivationCtor != null || peer.InvokerTypeName != null || needsManagedToJavaName;
149156
bool isAcw = !peer.DoNotGenerateAcw && !peer.IsInterface && peer.MarshalMethods.Count > 0;
150157

151158
JavaPeerProxyData? proxy = null;

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: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -304,10 +304,7 @@ public static IntPtr FindClass (System.Type type)
304304
}
305305
sig = sig.AddArrayRank (rank);
306306

307-
JniObjectReference local_ref = JniEnvironment.Types.FindClass (sig.Name);
308-
IntPtr global_ref = local_ref.NewGlobalRef ().Handle;
309-
JniObjectReference.Dispose (ref local_ref);
310-
return global_ref;
307+
return FindClass (sig.Name);
311308
} catch (Java.Lang.Throwable e) {
312309
if (!((e is Java.Lang.NoClassDefFoundError) || (e is Java.Lang.ClassNotFoundException)))
313310
throw;

src/Mono.Android/Android.Runtime/JNIEnvInit.cs

Lines changed: 27 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -58,16 +58,11 @@ static void PropagateUncaughtException (IntPtr env, IntPtr javaThread, IntPtr ja
5858
}
5959

6060
[UnmanagedCallersOnly]
61+
[RequiresUnreferencedCode ("Uses reflection to access System.StartupHookProvider.")]
6162
static unsafe void RegisterJniNatives (IntPtr typeName_ptr, int typeName_len, IntPtr jniClass, IntPtr methods_ptr, int methods_len)
6263
{
63-
// FIXME: https://github.com/xamarin/xamarin-android/issues/8724
64-
[UnconditionalSuppressMessage ("Trimming", "IL2057", Justification = "Type should be preserved by the MarkJavaObjects trimmer step.")]
65-
[return: DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods | DynamicallyAccessedMemberTypes.NonPublicMethods | DynamicallyAccessedMemberTypes.NonPublicNestedTypes)]
66-
static Type TypeGetType (string typeName) =>
67-
Type.GetType (typeName, throwOnError: false);
68-
6964
string typeName = new string ((char*) typeName_ptr, 0, typeName_len);
70-
var type = TypeGetType (typeName);
65+
var type = Type.GetType (typeName, throwOnError: false);
7166
if (type == null) {
7267
RuntimeNativeMethods.monodroid_log (LogLevel.Error,
7368
LogCategories.Default,
@@ -158,10 +153,14 @@ internal static unsafe void Initialize (JnienvInitializeArgs* args)
158153
args->propagateUncaughtExceptionFn = (IntPtr)(delegate* unmanaged<IntPtr, IntPtr, IntPtr, void>)&PropagateUncaughtException;
159154

160155
if (!RuntimeFeature.TrimmableTypeMap) {
161-
args->registerJniNativesFn = (IntPtr)(delegate* unmanaged<IntPtr, int, IntPtr, IntPtr, int, void>)&RegisterJniNatives;
156+
args->registerJniNativesFn = GetRegisterJniNativesFnPtr ();
162157
}
163158
RunStartupHooksIfNeeded ();
164159
SetSynchronizationContext ();
160+
161+
[UnconditionalSuppressMessage ("Trimming", "IL2026", Justification = "This method is never used with the trimmable type map.")]
162+
IntPtr GetRegisterJniNativesFnPtr () =>
163+
(IntPtr)(delegate* unmanaged<IntPtr, int, IntPtr, IntPtr, int, void>)&RegisterJniNatives;
165164
}
166165

167166
[LibraryImport (RuntimeConstants.InternalDllName)]
@@ -175,16 +174,28 @@ internal static JniRuntime.JniTypeManager CreateTypeManager (JnienvInitializeArg
175174
}
176175

177176
if (RuntimeFeature.IsNativeAotRuntime || RuntimeFeature.ManagedTypeMap) {
178-
return new ManagedTypeManager ();
177+
return CreateManagedTypeManager ();
179178
}
180179

181-
return new AndroidTypeManager (args.jniAddNativeMethodRegistrationAttributePresent != 0);
180+
return CreateAndroidTypeManager (args);
181+
182+
[UnconditionalSuppressMessage ("Trimming", "IL2026", Justification = "Managed type manager is preserved by the MarkJavaObjects trimmer step.")]
183+
[UnconditionalSuppressMessage ("Trimming", "IL3050", Justification = "This type manager won't be used in Native AOT builds in the future.")]
184+
static JniRuntime.JniTypeManager CreateManagedTypeManager () => new ManagedTypeManager ();
185+
186+
[UnconditionalSuppressMessage ("Trimming", "IL2026", Justification = "This type manager won't be used in Native AOT builds.")]
187+
[UnconditionalSuppressMessage ("Trimming", "IL3050", Justification = "This type manager won't be used in Native AOT builds.")]
188+
static JniRuntime.JniTypeManager CreateAndroidTypeManager (JnienvInitializeArgs args) => new AndroidTypeManager (args.jniAddNativeMethodRegistrationAttributePresent != 0);
182189
}
183190

184191
internal static JniRuntime.JniValueManager CreateValueManager ()
185192
{
193+
if (RuntimeFeature.TrimmableTypeMap) {
194+
return new TrimmableTypeMapValueManager ();
195+
}
196+
186197
if (RuntimeFeature.IsMonoRuntime) {
187-
return new AndroidValueManager ();
198+
return CreateAndroidValueManager ();
188199
}
189200

190201
if (RuntimeFeature.IsCoreClrRuntime) {
@@ -199,10 +210,11 @@ internal static JniRuntime.JniValueManager CreateValueManager ()
199210

200211
[UnconditionalSuppressMessage ("Trimming", "IL2026", Justification = "CoreCLR value manager is preserved by the MarkJavaObjects trimmer step.")]
201212
[UnconditionalSuppressMessage ("Trimming", "IL3050", Justification = "This value manager won't be used in Native AOT builds in the future.")]
202-
JniRuntime.JniValueManager CreateJavaMarshalValueManager ()
203-
{
204-
return new JavaMarshalValueManager ();
205-
}
213+
JniRuntime.JniValueManager CreateJavaMarshalValueManager () => new JavaMarshalValueManager ();
214+
215+
[UnconditionalSuppressMessage ("Trimming", "IL2026", Justification = "Mono value manager is preserved by the MarkJavaObjects trimmer step.")]
216+
[UnconditionalSuppressMessage ("Trimming", "IL3050", Justification = "This value manager won't be used in Native AOT builds in the future.")]
217+
JniRuntime.JniValueManager CreateAndroidValueManager () => new AndroidValueManager ();
206218
}
207219

208220
static void InitializeCommonState (JnienvInitializeArgs args)

src/Mono.Android/Android.Runtime/JNINativeWrapper.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ static void get_runtime_types ()
2626
AndroidEnvironment.FailFast ("Cannot find AndroidRuntimeInternal.WaitForBridgeProcessing");
2727
}
2828

29+
[RequiresDynamicCode ("This method uses System.Reflection.Emit to create a delegate at runtime.")]
2930
public static Delegate CreateDelegate (Delegate dlg)
3031
{
3132
if (dlg == null)

src/Mono.Android/Android.Runtime/JavaCollection.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -179,7 +179,7 @@ public IEnumerator GetEnumerator ()
179179
if (handle == IntPtr.Zero)
180180
return null;
181181

182-
var inst = (IJavaObject?) Java.Lang.Object.PeekObject (handle);
182+
var inst = (IJavaObject?) Java.Lang.Object.PeekObject (handle, typeof (ICollection));
183183
if (inst == null)
184184
inst = new JavaCollection (handle, transfer);
185185
else
@@ -399,7 +399,7 @@ System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator ()
399399
if (handle == IntPtr.Zero)
400400
return null;
401401

402-
var inst = (IJavaObject?) Java.Lang.Object.PeekObject (handle);
402+
var inst = (IJavaObject?) Java.Lang.Object.PeekObject (handle, typeof (ICollection<T>));
403403
if (inst == null)
404404
inst = new JavaCollection<T> (handle, transfer);
405405
else

0 commit comments

Comments
 (0)