@@ -34,41 +34,17 @@ unsafe class BridgeProcessing
3434 static JniMethodInfo ? s_Runtime_gc ;
3535
3636 // Is NativeAOT mode (uses GCUserPeerable instead of IGCUserPeer)
37- static bool s_initialized ;
38- static bool s_isNativeAOT ;
37+ static readonly bool s_isNativeAOT ;
3938
40- public BridgeProcessing ( MarkCrossReferencesArgs * args )
41- {
42- if ( args == null ) {
43- throw new ArgumentNullException ( nameof ( args ) , "Cross references argument is a NULL pointer" ) ;
44- }
45-
46- if ( args ->ComponentCount > 0 && args ->Components == null ) {
47- throw new InvalidOperationException ( "Components member of the cross references arguments structure is NULL" ) ;
48- }
49-
50- if ( args ->CrossReferenceCount > 0 && args ->CrossReferences == null ) {
51- throw new InvalidOperationException ( "CrossReferences member of the cross references arguments structure is NULL" ) ;
52- }
53-
54- crossRefs = args ;
55- }
56-
57- /// <summary>
58- /// Initialize cached Java references. Must be called once during runtime initialization.
59- /// </summary>
60- public static void Initialize ( bool isNativeAOT )
39+ static BridgeProcessing ( )
6140 {
62- if ( s_initialized )
63- return ;
64-
65- s_isNativeAOT = isNativeAOT ;
41+ s_isNativeAOT = ! RuntimeFeature . IsMonoRuntime ;
6642
6743 // Initialize GCUserPeer class for creating temporary peers
6844 s_GCUserPeerClass = new JniType ( "mono/android/GCUserPeer" ) ;
6945 s_GCUserPeerCtor = s_GCUserPeerClass . GetConstructor ( "()V" ) ;
7046
71- if ( isNativeAOT ) {
47+ if ( s_isNativeAOT ) {
7248 // For NativeAOT, we also use GCUserPeerable interface for optimized method calls
7349 s_GCUserPeerableClass = new JniType ( "net/dot/jni/GCUserPeerable" ) ;
7450 s_GCUserPeerable_jiAddManagedReference = s_GCUserPeerableClass . GetInstanceMethod ( "jiAddManagedReference" , "(Ljava/lang/Object;)V" ) ;
@@ -82,8 +58,23 @@ public static void Initialize (bool isNativeAOT)
8258 var runtimeLocal = JniEnvironment . StaticMethods . CallStaticObjectMethod ( runtimeClass . PeerReference , getRuntimeMethod , null ) ;
8359 s_RuntimeInstance = runtimeLocal . NewGlobalRef ( ) ;
8460 JniObjectReference . Dispose ( ref runtimeLocal ) ;
61+ }
8562
86- s_initialized = true ;
63+ public BridgeProcessing ( MarkCrossReferencesArgs * args )
64+ {
65+ if ( args == null ) {
66+ throw new ArgumentNullException ( nameof ( args ) , "Cross references argument is a NULL pointer" ) ;
67+ }
68+
69+ if ( args ->ComponentCount > 0 && args ->Components == null ) {
70+ throw new InvalidOperationException ( "Components member of the cross references arguments structure is NULL" ) ;
71+ }
72+
73+ if ( args ->CrossReferenceCount > 0 && args ->CrossReferences == null ) {
74+ throw new InvalidOperationException ( "CrossReferences member of the cross references arguments structure is NULL" ) ;
75+ }
76+
77+ crossRefs = args ;
8778 }
8879
8980 /// <summary>
@@ -117,8 +108,8 @@ void PrepareForJavaCollection ()
117108
118109 // With cross references processed, the temporary peer list can be released
119110 foreach ( var ( _, temporaryPeer ) in temporaryPeers ) {
120- var peer = temporaryPeer ;
121- JniObjectReference . Dispose ( ref peer ) ;
111+ var peerToDispose = temporaryPeer ;
112+ JniObjectReference . Dispose ( ref peerToDispose ) ;
122113 }
123114
124115 // Switch global to weak references
@@ -177,52 +168,35 @@ void AddCircularReferences (ref StronglyConnectedComponent scc)
177168
178169 void AddCrossReference ( nuint sourceIndex , nuint destIndex )
179170 {
180- CrossReferenceTarget from = SelectCrossReferenceTarget ( sourceIndex ) ;
181- CrossReferenceTarget to = SelectCrossReferenceTarget ( destIndex ) ;
171+ var ( fromRef , fromContextPtr ) = SelectCrossReferenceTarget ( sourceIndex ) ;
172+ var ( toRef , _ ) = SelectCrossReferenceTarget ( destIndex ) ;
182173
183- if ( AddReference ( from . Reference , to . Reference ) ) {
184- from . MarkRefsAddedIfNeeded ( ) ;
174+ if ( AddReference ( fromRef , toRef ) && fromContextPtr != IntPtr . Zero ) {
175+ HandleContext * fromContext = ( HandleContext * ) fromContextPtr ;
176+ fromContext ->ControlBlock ->RefsAdded = 1 ;
185177 }
186178 }
187179
188- readonly struct CrossReferenceTarget
189- {
190- public readonly JniObjectReference Reference ;
191- public readonly HandleContext * Context ;
192- public readonly bool IsTemporaryPeer ;
193-
194- public CrossReferenceTarget ( JniObjectReference reference , HandleContext * context , bool isTemporaryPeer )
195- {
196- Reference = reference ;
197- Context = context ;
198- IsTemporaryPeer = isTemporaryPeer ;
199- }
200-
201- public void MarkRefsAddedIfNeeded ( )
202- {
203- if ( IsTemporaryPeer || Context == null ) {
204- return ;
205- }
206- Context ->ControlBlock ->RefsAdded = 1 ;
207- }
208- }
209-
210- CrossReferenceTarget SelectCrossReferenceTarget ( nuint sccIndex )
180+ /// <summary>
181+ /// Selects the target for a cross-reference from an SCC.
182+ /// Returns the JNI reference and the handle context pointer (IntPtr.Zero for temporary peers).
183+ /// </summary>
184+ ( JniObjectReference Reference , IntPtr ContextPtr ) SelectCrossReferenceTarget ( nuint sccIndex )
211185 {
212186 ref StronglyConnectedComponent scc = ref crossRefs ->Components [ sccIndex ] ;
213187
214188 if ( scc . Count == 0 ) {
215189 if ( ! temporaryPeers . TryGetValue ( sccIndex , out var tempPeer ) ) {
216190 throw new InvalidOperationException ( "Temporary peer must be found in the map" ) ;
217191 }
218- return new CrossReferenceTarget ( tempPeer , null , isTemporaryPeer : true ) ;
192+ return ( tempPeer , IntPtr . Zero ) ;
219193 }
220194
221195 HandleContext * context = ( HandleContext * ) scc . Contexts [ 0 ] ;
222196 Debug . Assert ( context != null && context ->ControlBlock != null , "SCC must have at least one valid context" ) ;
223197
224198 var reference = new JniObjectReference ( context ->ControlBlock ->Handle , JniObjectReferenceType . Global ) ;
225- return new CrossReferenceTarget ( reference , context , isTemporaryPeer : false ) ;
199+ return ( reference , ( IntPtr ) context ) ;
226200 }
227201
228202 bool AddReference ( JniObjectReference from , JniObjectReference to )
@@ -238,23 +212,20 @@ bool AddReference (JniObjectReference from, JniObjectReference to)
238212
239213 // Fall back to reflection-based approach
240214 var fromClassRef = JniEnvironment . Types . GetObjectClass ( from ) ;
241- try {
242- using var fromClass = new JniType ( ref fromClassRef , JniObjectReferenceOptions . CopyAndDispose ) ;
243- JniMethodInfo addMethod ;
244- try {
245- addMethod = fromClass . GetInstanceMethod ( "monodroidAddReference" , "(Ljava/lang/Object;)V" ) ;
246- } catch ( Java . Lang . NoSuchMethodError ) {
247- Logger . Log ( LogLevel . Error , "monodroid-gc" , "Failed to find monodroidAddReference method" ) ;
248- return false ;
249- }
215+ using var fromClass = new JniType ( ref fromClassRef , JniObjectReferenceOptions . CopyAndDispose ) ;
250216
251- JniArgumentValue * args = stackalloc JniArgumentValue [ 1 ] ;
252- args [ 0 ] = new JniArgumentValue ( to ) ;
253- JniEnvironment . InstanceMethods . CallVoidMethod ( from , addMethod , args ) ;
254- return true ;
255- } catch {
217+ JniMethodInfo addMethod ;
218+ try {
219+ addMethod = fromClass . GetInstanceMethod ( "monodroidAddReference" , "(Ljava/lang/Object;)V" ) ;
220+ } catch ( Java . Lang . NoSuchMethodError ) {
221+ Logger . Log ( LogLevel . Error , "monodroid-gc" , "Failed to find monodroidAddReference method" ) ;
256222 return false ;
257223 }
224+
225+ JniArgumentValue * args = stackalloc JniArgumentValue [ 1 ] ;
226+ args [ 0 ] = new JniArgumentValue ( to ) ;
227+ JniEnvironment . InstanceMethods . CallVoidMethod ( from , addMethod , args ) ;
228+ return true ;
258229 }
259230
260231 bool TryCallGCUserPeerableAddManagedReference ( JniObjectReference from , JniObjectReference to )
@@ -279,7 +250,7 @@ bool TryCallGCUserPeerableAddManagedReference (JniObjectReference from, JniObjec
279250 static void TriggerJavaGC ( )
280251 {
281252 if ( s_Runtime_gc == null ) {
282- throw new InvalidOperationException ( "BridgeProcessing.Initialize must be called before TriggerJavaGC" ) ;
253+ throw new InvalidOperationException ( "BridgeProcessing static constructor must run before TriggerJavaGC" ) ;
283254 }
284255
285256 try {
@@ -370,20 +341,17 @@ void ClearReferences (JniObjectReference handle)
370341
371342 // Fall back to reflection-based approach
372343 var javaClassRef = JniEnvironment . Types . GetObjectClass ( handle ) ;
373- try {
374- using var javaClass = new JniType ( ref javaClassRef , JniObjectReferenceOptions . CopyAndDispose ) ;
375- JniMethodInfo clearMethod ;
376- try {
377- clearMethod = javaClass . GetInstanceMethod ( "monodroidClearReferences" , "()V" ) ;
378- } catch ( Java . Lang . NoSuchMethodError ) {
379- Logger . Log ( LogLevel . Error , "monodroid-gc" , "Failed to find monodroidClearReferences method" ) ;
380- return ;
381- }
344+ using var javaClass = new JniType ( ref javaClassRef , JniObjectReferenceOptions . CopyAndDispose ) ;
382345
383- JniEnvironment . InstanceMethods . CallVoidMethod ( handle , clearMethod , null ) ;
384- } catch {
385- // Ignore exceptions during cleanup
346+ JniMethodInfo clearMethod ;
347+ try {
348+ clearMethod = javaClass . GetInstanceMethod ( "monodroidClearReferences" , "()V" ) ;
349+ } catch ( Java . Lang . NoSuchMethodError ) {
350+ Logger . Log ( LogLevel . Error , "monodroid-gc" , "Failed to find monodroidClearReferences method" ) ;
351+ return ;
386352 }
353+
354+ JniEnvironment . InstanceMethods . CallVoidMethod ( handle , clearMethod , null ) ;
387355 }
388356
389357 bool TryCallGCUserPeerableClearManagedReferences ( JniObjectReference handle )
0 commit comments