Skip to content
This repository was archived by the owner on Jul 6, 2026. It is now read-only.

Commit 01139ed

Browse files
[TrimmableTypeMap] adjust base JniValueManager and JniTypeManager for "trimmable type map" (#1454)
Follow-up to #1441. Prerequisite for dotnet/android#11617. This keeps the Java.Interop changes focused on the small base hook dotnet/android needs for the trimmable type-map integration. The reflection value manager continues to use value marshalers internally, while Android's generated/trimmable value manager can provide the only production `JavaObjectArray<T>` element-assignment object-reference path without implementing `GetValueMarshaler*()`. ## Changes - Add minimal `JniValueManager` object-reference API for `JavaObjectArray<T>.SetElementAt()`: - `CreateLocalObjectReferenceArgument(Type type, object? value)` returns an owned local `JniObjectReference` for element assignment. Callers must dispose the returned reference. - Make the matching core method abstract so non-reflection value managers can implement this path directly. - Keep value marshalers as a `ReflectionJniValueManager` implementation detail: reflection creates marshaler state, copies out an independent local reference, then destroys the state immediately. - Update `JavaObjectArray<T>` to call the value manager directly instead of calling `GetValueMarshaler<T>()` in production paths. - Simplify `JavaObjectArray<T>.Clear()` to set array slots to Java null directly; it no longer needs value-manager or value-marshaler state. - Remove the earlier exposed proxy/peerable marshaler accessors, broad/generic state overloads, default-value state API, destroy-state API, and `ParameterAttributes synchronize` from this value-manager object-reference path. - Keep ManagedPeer-dependent tests categorized as unsupported for the Android trimmable configuration rather than carrying Android-specific Java fixture workarounds in this PR. - Include the small type-manager/test cleanups needed by the dotnet/android integration branch. ## Non-goals - This PR does not make value marshalers public trimmable API. - This PR does not require trimmable Android value managers to implement or use `GetValueMarshaler*()`. - This PR does not remove or replace ManagedPeer-dependent Java.Interop test fixtures. ## Validation - `dotnet build external/Java.Interop/src/Java.Interop/Java.Interop.csproj -p:Configuration=Debug -m:1 -nodeReuse:false --no-restore -v:minimal` - From the dotnet/android integration branch: - `dotnet test tests/Microsoft.Android.Sdk.TrimmableTypeMap.Tests/Microsoft.Android.Sdk.TrimmableTypeMap.Tests.csproj -v minimal --no-restore` (`562` passed) - `dotnet build src/Mono.Android/Mono.Android.csproj -p:Configuration=Debug -p:AndroidSdkDirectory=/Users/simonrozsival/android-toolchain/sdk -m:1 -nodeReuse:false --no-restore -v:minimal` compiled `Mono.Android.Runtime.dll`; the remaining local failure is Android SDK provisioning (`extras/android/m2repository.staging` and `docs.staging` missing), not C# or trim-analyzer errors. Co-authored-by: Copilot <[email protected]>
1 parent b3f0182 commit 01139ed

14 files changed

Lines changed: 65 additions & 19 deletions

external/xamarin-android-tools

src/Java.Interop/Java.Interop/JavaObjectArray.cs

Lines changed: 9 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -89,10 +89,13 @@ T GetElementAt (int index)
8989

9090
void SetElementAt (int index, T value)
9191
{
92-
var vm = JniEnvironment.Runtime.ValueManager.GetValueMarshaler<T> ();
93-
var s = vm.CreateGenericObjectReferenceArgumentState (value);
94-
JniEnvironment.Arrays.SetObjectArrayElement (PeerReference, index, s.ReferenceValue);
95-
vm.DestroyGenericArgumentState (value, ref s, 0);
92+
var vm = JniEnvironment.Runtime.ValueManager;
93+
var r = vm.CreateLocalObjectReferenceArgument (typeof (T), value);
94+
try {
95+
JniEnvironment.Arrays.SetObjectArrayElement (PeerReference, index, r);
96+
} finally {
97+
JniObjectReference.Dispose (ref r);
98+
}
9699
}
97100

98101
public override IEnumerator<T> GetEnumerator ()
@@ -108,14 +111,10 @@ public override IEnumerator<T> GetEnumerator ()
108111
public override void Clear ()
109112
{
110113
int len = Length;
111-
var vm = JniEnvironment.Runtime.ValueManager.GetValueMarshaler<T> ();
112-
#pragma warning disable 8653
113-
var s = vm.CreateArgumentState (default (T));
114+
var nullReference = new JniObjectReference ();
114115
for (int i = 0; i < len; i++) {
115-
JniEnvironment.Arrays.SetObjectArrayElement (PeerReference, i, s.ReferenceValue);
116+
JniEnvironment.Arrays.SetObjectArrayElement (PeerReference, i, nullReference);
116117
}
117-
vm.DestroyGenericArgumentState (default (T), ref s, 0);
118-
#pragma warning restore 8653
119118
}
120119

121120
public override int IndexOf (T item)
@@ -222,4 +221,3 @@ public static JavaObjectArray<T>? CreateMarshalObjectArray<
222221
}
223222
}
224223
}
225-

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

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -222,6 +222,8 @@ protected virtual bool TryUnboxPeerObject (IJavaPeerable value, [NotNullWhen (tr
222222
[DynamicallyAccessedMembers (Constructors)]
223223
Type? targetType = null)
224224
{
225+
EnsureNotDisposed ();
226+
225227
return CreateValueCore (ref reference, options, targetType);
226228
}
227229

@@ -232,6 +234,8 @@ protected virtual bool TryUnboxPeerObject (IJavaPeerable value, [NotNullWhen (tr
232234
[DynamicallyAccessedMembers (Constructors)]
233235
Type? targetType = null)
234236
{
237+
EnsureNotDisposed ();
238+
235239
return CreateValueCore<T> (ref reference, options, targetType);
236240
}
237241

@@ -254,6 +258,8 @@ protected virtual bool TryUnboxPeerObject (IJavaPeerable value, [NotNullWhen (tr
254258
[DynamicallyAccessedMembers (Constructors)]
255259
Type? targetType = null)
256260
{
261+
EnsureNotDisposed ();
262+
257263
return GetValueCore (ref reference, options, targetType);
258264
}
259265

@@ -271,6 +277,8 @@ protected virtual bool TryUnboxPeerObject (IJavaPeerable value, [NotNullWhen (tr
271277
[DynamicallyAccessedMembers (Constructors)]
272278
Type? targetType = null)
273279
{
280+
EnsureNotDisposed ();
281+
274282
return GetValueCore<T> (ref reference, options, targetType);
275283
}
276284

@@ -303,6 +311,23 @@ protected virtual bool TryUnboxPeerObject (IJavaPeerable value, [NotNullWhen (tr
303311

304312
public JniValueMarshaler<T> GetValueMarshaler<[DynamicallyAccessedMembers (Constructors)] T> () => GetValueMarshalerCore<T> ();
305313
protected abstract JniValueMarshaler<T> GetValueMarshalerCore<[DynamicallyAccessedMembers (Constructors)] T> ();
314+
315+
internal JniObjectReference CreateLocalObjectReferenceArgument (
316+
[DynamicallyAccessedMembers (Constructors)]
317+
Type type,
318+
object? value)
319+
{
320+
EnsureNotDisposed ();
321+
322+
if (type == null)
323+
throw new ArgumentNullException (nameof (type));
324+
return CreateLocalObjectReferenceArgumentCore (type, value);
325+
}
326+
327+
protected abstract JniObjectReference CreateLocalObjectReferenceArgumentCore (
328+
[DynamicallyAccessedMembers (Constructors)]
329+
Type type,
330+
object? value);
306331
}
307332
}
308333
}

src/Java.Interop/Java.Interop/JniRuntime.ReflectionJniValueManager.cs

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -471,6 +471,24 @@ protected override JniValueMarshaler GetValueMarshalerCore (Type type)
471471
return ProxyValueMarshaler.Instance;
472472
}
473473

474+
protected override JniObjectReference CreateLocalObjectReferenceArgumentCore (
475+
[DynamicallyAccessedMembers (Constructors)]
476+
Type type,
477+
object? value)
478+
{
479+
EnsureNotDisposed ();
480+
var marshaler = GetValueMarshaler (type);
481+
var state = marshaler.CreateObjectReferenceArgumentState (value);
482+
try {
483+
if (!state.ReferenceValue.IsValid) {
484+
return new JniObjectReference ();
485+
}
486+
return state.ReferenceValue.NewLocalRef ();
487+
} finally {
488+
marshaler.DestroyArgumentState (value, ref state);
489+
}
490+
}
491+
474492
static Type? GetListType (Type type)
475493
{
476494
foreach (var iface in type.GetInterfaces ().Concat (new [] { type })) {

src/Java.Interop/PublicAPI.Unshipped.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ virtual Java.Interop.JniRuntime.JniTypeManager.GetReflectionConstructibleTypes(J
88
virtual Java.Interop.JniRuntime.JniTypeManager.GetTypeForSimpleReference(string! jniSimpleReference) -> System.Type?
99
virtual Java.Interop.JniRuntime.JniTypeManager.GetTypeSignatureCore(System.Type! type) -> Java.Interop.JniTypeSignature
1010
virtual Java.Interop.JniRuntime.JniTypeManager.GetTypeSignaturesCore(System.Type! type) -> System.Collections.Generic.IEnumerable<Java.Interop.JniTypeSignature>!
11+
abstract Java.Interop.JniRuntime.JniValueManager.CreateLocalObjectReferenceArgumentCore(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
@@ -64,6 +65,7 @@ abstract Java.Interop.JniRuntime.JniValueManager.GetValueMarshalerCore(System.Ty
6465
override Java.Interop.JniRuntime.ReflectionJniValueManager.ActivatePeer(Java.Interop.JniObjectReference reference, System.Type! type, System.Reflection.ConstructorInfo! cinfo, object?[]? argumentValues) -> void
6566
override Java.Interop.JniRuntime.ReflectionJniValueManager.ConstructPeerCore(Java.Interop.IJavaPeerable! peer, ref Java.Interop.JniObjectReference reference, Java.Interop.JniObjectReferenceOptions options) -> void
6667
override Java.Interop.JniRuntime.ReflectionJniValueManager.CreatePeer(ref Java.Interop.JniObjectReference reference, Java.Interop.JniObjectReferenceOptions transfer, System.Type? targetType) -> Java.Interop.IJavaPeerable?
68+
override Java.Interop.JniRuntime.ReflectionJniValueManager.CreateLocalObjectReferenceArgumentCore(System.Type! type, object? value) -> Java.Interop.JniObjectReference
6769
override Java.Interop.JniRuntime.ReflectionJniValueManager.CreateValueCore(ref Java.Interop.JniObjectReference reference, Java.Interop.JniObjectReferenceOptions options, System.Type? targetType = null) -> object?
6870
override Java.Interop.JniRuntime.ReflectionJniValueManager.CreateValueCore<T>(ref Java.Interop.JniObjectReference reference, Java.Interop.JniObjectReferenceOptions options, System.Type? targetType = null) -> T
6971
override Java.Interop.JniRuntime.ReflectionJniValueManager.GetValueCore(ref Java.Interop.JniObjectReference reference, Java.Interop.JniObjectReferenceOptions options, System.Type? targetType = null) -> object?

tests/Java.Interop-Tests/Java.Interop/InvokeVirtualFromConstructorTests.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
namespace Java.InteropTests {
99

1010
[TestFixture]
11+
[Category ("TrimmableTypeMapUnsupported")]
1112
#if !__ANDROID__
1213
// We want stability around the CallVirtualFromConstructorDerived static fields
1314
[NonParallelizable]
@@ -153,4 +154,3 @@ public void CreateJavaInstanceFirst ()
153154
}
154155
}
155156
}
156-

tests/Java.Interop-Tests/Java.Interop/JavaManagedGCBridgeTests.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
namespace Java.InteropTests {
1010

1111
[TestFixture]
12+
[Category ("TrimmableTypeMapUnsupported")]
1213
public class JavaManagedGCBridgeTests : JavaVMFixture {
1314

1415
#if !NO_GC_BRIDGE_SUPPORT
@@ -66,4 +67,3 @@ protected override void Dispose (bool disposing)
6667
}
6768
}
6869
}
69-

tests/Java.Interop-Tests/Java.Interop/JniInstanceMethodIDTest.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ public class JniInstanceMethodIDTest : JavaVMFixture
1111
{
1212
// https://code.google.com/p/android/issues/detail?id=65710
1313
[Test]
14+
[Category ("TrimmableTypeMapUnsupported")]
1415
public unsafe void CallNonvirtualVoidMethod_WithBaseMethodIDAndDerivedType ()
1516
{
1617
using (var b = new JniType ("net/dot/jni/test/CallNonvirtualBase"))
@@ -38,4 +39,3 @@ public unsafe void CallNonvirtualVoidMethod_WithBaseMethodIDAndDerivedType ()
3839
}
3940
}
4041
}
41-

tests/Java.Interop-Tests/Java.Interop/JniPeerMembersTests.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,7 @@ public void MethodLookupForNonexistentStaticMethodWillTryFallbacks ()
7676

7777
[Test]
7878
[Category ("NativeAOTIgnore")]
79+
[Category ("TrimmableTypeMapUnsupported")]
7980
public void ReplacementTypeUsedForMethodLookup ()
8081
{
8182
using var o = new RenameClassDerived ();

tests/Java.Interop-Tests/Java.Interop/JniRuntimeJniValueManagerContract.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ namespace Java.InteropTests {
1919
// Modifies JniRuntime.valueManager instance field; can't be done in parallel
2020
[NonParallelizable]
2121
#endif // !__ANDROID__
22+
[Category ("TrimmableTypeMapUnsupported")]
2223
public abstract class JniRuntimeJniValueManagerContract : JavaVMFixture {
2324

2425
[DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)]

0 commit comments

Comments
 (0)