Skip to content

Commit 1f21f38

Browse files
authored
[generator] Use GC.KeepAlive for reference type method parameters. (#725)
Fixes: #719 @brendanzagaeski has been investigating a Xamarin.Android app crash: JNI DETECTED ERROR IN APPLICATION: use of deleted global reference 0x3d86 from android.view.View crc64720bb2db43a66fe9.FragmentContainer.n_onCreateView(android.view.LayoutInflater, android.view.ViewGroup, android.os.Bundle) … at crc64720bb2db43a66fe9.FragmentContainer.n_onCreateView(Native method) at crc64720bb2db43a66fe9.FragmentContainer.onCreateView(FragmentContainer.java:41) This had been a head-scratcher, but we had a GREF log, so all should be clear, right? 09-10 17:56:48.280 10123 10123 I monodroid-gref: +g+ grefc 1141 gwrefc 0 obj-handle 0x79/I -> new-handle 0x3d86/G from thread '(null)'(1) 09-10 17:56:48.294 10123 10123 I monodroid-gref: +w+ grefc 1140 gwrefc 2 obj-handle 0x3d86/G -> new-handle 0x1e3/W from thread 'finalizer'(10123) 09-10 17:56:48.294 10123 10123 I monodroid-gref: -g- grefc 1139 gwrefc 2 handle 0x3d86/G from thread 'finalizer'(10123) The GREF log *wasn't* immediately clear: sure, the GREF was turned into a Weak-GREF, and the Weak-GREF was then collected, but none of this explained *how* were were using this deleted GREF. (We were at this state of affairs for months: we "know" we're using a deleted GREF, but we don't know *how* or *why*. It was a very hard to hit bug.) Eventually we had a ["that's funny"][0] event: *sure*, the GREF is deleted, but: 1. Why is it being deleted by the finalizer? 2. …14ms *after construction*? A [Garbage Collection][1] refresher may be in order, but in short: 1. All `Java.Lang.Object` subclasses are "bridged" objects. 2. During a GC, all "collectable" bridged objects are gathered. A collectable object is one in which nothing in the managed GC references the object. 3. Once the GC is complete, all gathered collectable bridged objects are passed to a "cross references" callback. The callback is called *outside* the "scope" of a GC; "the world" is *not* frozen, other threads may be executing. 4. The "cross references" callback is the `MonoGCBridgeCallbacks::cross_references` field provided provided to [`mono_gc_register_bridge_callbacks()`][2]. In a Xamarin.Android app, the "cross references" callback will "toggle" a JNI Global Reference to a JNI Weak Global Reference, invoke a Java-side GC, and then try to obtain a JNI Global Reference from the JNI Weak Global Reference. If a non-`NULL` pointer is returned, the bridged object is kept alive. If a `NULL` pointer is returned, the bridged object will be considered dead, and will be added to the Finalization Queue (as `Java.Lang.Object` has a finalizer). Thus, it seemed "funny" that within 14ms an instance was created, GC'd, and determined to be garbage, *especially* when we *knew* that this instance was being passed to Java, which we expected to retain the instance. (Yet wasn't…?) After much banging of heads, and the yeoman's work of creating a simplified and consistently reproducible test case, we *think* we know the cause of the crash. Consider our normal Managed-to-Java marshaling code, e.g. partial class MaterialButton { public unsafe MaterialButton (global::Android.Content.Context context) : base (IntPtr.Zero, JniHandleOwnership.DoNotTransfer) { const string __id = "(Landroid/content/Context;)V"; if (((global::Java.Lang.Object) this).Handle != IntPtr.Zero) return; try { JniArgumentValue* __args = stackalloc JniArgumentValue [1]; /* 1 */ __args [0] = new JniArgumentValue ((context == null) ? IntPtr.Zero : ((global::Java.Lang.Object) context).Handle); /* 2 */ var __r = _members.InstanceMethods.StartCreateInstance (__id, ((object) this).GetType (), __args); SetHandle (__r.Handle, JniHandleOwnership.TransferLocalRef); /* 3 */ _members.InstanceMethods.FinishCreateInstance (__id, this, __args); } finally { } } } At (1), `context.Handle` is -- a JNI GREF -- is stored into a `JniArgumentValue* __args` value, and at (3) `__args` is passed into JNI, which will presumably "Do Something" with that handle. However, nothing ties `context.Handle` to `context`, so from (2) onward, `context` *may* be eligible for garbage collection. See also Chris Brumme's [Lifetime, GC.KeepAlive, handle recycling][3] blog article. It's about .NET Framework, but the same fundamental multithreading concepts apply. The fix is to *ensure* that `context` is kept alive *for as long as* `context.Handle` will be used, i.e. across the JNI `_members.InstanceMethods.FinishCreateInstance()` call: partial class MaterialButton { public unsafe MaterialButton (global::Android.Content.Context context) : base (IntPtr.Zero, JniHandleOwnership.DoNotTransfer) { const string __id = "(Landroid/content/Context;)V"; if (((global::Java.Lang.Object) this).Handle != IntPtr.Zero) return; try { JniArgumentValue* __args = stackalloc JniArgumentValue [1]; /* 1 */ __args [0] = new JniArgumentValue ((context == null) ? IntPtr.Zero : ((global::Java.Lang.Object) context).Handle); /* 2 */ var __r = _members.InstanceMethods.StartCreateInstance (__id, ((object) this).GetType (), __args); SetHandle (__r.Handle, JniHandleOwnership.TransferLocalRef); /* 3 */ _members.InstanceMethods.FinishCreateInstance (__id, this, __args); } finally { /* 4 */ global::System.GC.KeepAlive (context); } } } This should prevent e.g. `context` from being prematurely GC'd, which in turn should prevent the `JNI DETECTED ERROR` message. Update `tools/generator` to emit `GC.KeepAlive()` statements for every parameter type which isn't a value type (`enum`, `int`, `string`, etc.). `string` is considered a value type because we always send a "deep copy" of the string contents, so it won't matter if the `string` instance is GC'd immediately. [0]: https://quoteinvestigator.com/2015/03/02/eureka-funny/ [1]: https://docs.microsoft.com/xamarin/android/internals/garbage-collection [2]: http://docs.go-mono.com/?link=xhtml%3adeploy%2fmono-api-gc.html [3]: https://docs.microsoft.com/archive/blogs/cbrumme/lifetime-gc-keepalive-handle-recycling
1 parent 5136ef9 commit 1f21f38

24 files changed

+74
-0
lines changed

tests/generator-Tests/Unit-Tests/CodeGeneratorExpectedResults/JavaInterop1/WriteKotlinUnsignedArrayTypeMethodsClass.txt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ public partial class MyClass {
2727
JNIEnv.CopyArray (native_value, value);
2828
JNIEnv.DeleteLocalRef (native_value);
2929
}
30+
global::System.GC.KeepAlive (value);
3031
}
3132
}
3233

@@ -46,6 +47,7 @@ public partial class MyClass {
4647
JNIEnv.CopyArray (native_value, value);
4748
JNIEnv.DeleteLocalRef (native_value);
4849
}
50+
global::System.GC.KeepAlive (value);
4951
}
5052
}
5153

@@ -65,6 +67,7 @@ public partial class MyClass {
6567
JNIEnv.CopyArray (native_value, value);
6668
JNIEnv.DeleteLocalRef (native_value);
6769
}
70+
global::System.GC.KeepAlive (value);
6871
}
6972
}
7073

@@ -84,6 +87,7 @@ public partial class MyClass {
8487
JNIEnv.CopyArray (native_value, value);
8588
JNIEnv.DeleteLocalRef (native_value);
8689
}
90+
global::System.GC.KeepAlive (value);
8791
}
8892
}
8993

tests/generator-Tests/Unit-Tests/CodeGeneratorExpectedResults/JavaInterop1/WriteKotlinUnsignedArrayTypePropertiesClass.txt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ public partial class MyClass {
3636
JNIEnv.CopyArray (native_value, value);
3737
JNIEnv.DeleteLocalRef (native_value);
3838
}
39+
global::System.GC.KeepAlive (value);
3940
}
4041
}
4142
}
@@ -65,6 +66,7 @@ public partial class MyClass {
6566
JNIEnv.CopyArray (native_value, value);
6667
JNIEnv.DeleteLocalRef (native_value);
6768
}
69+
global::System.GC.KeepAlive (value);
6870
}
6971
}
7072
}
@@ -94,6 +96,7 @@ public partial class MyClass {
9496
JNIEnv.CopyArray (native_value, value);
9597
JNIEnv.DeleteLocalRef (native_value);
9698
}
99+
global::System.GC.KeepAlive (value);
97100
}
98101
}
99102
}
@@ -123,6 +126,7 @@ public partial class MyClass {
123126
JNIEnv.CopyArray (native_value, value);
124127
JNIEnv.DeleteLocalRef (native_value);
125128
}
129+
global::System.GC.KeepAlive (value);
126130
}
127131
}
128132
}

tests/generator-Tests/expected.ji/Adapters/Xamarin.Test.AbsSpinner.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,7 @@ static void n_SetAdapter_Lxamarin_test_SpinnerAdapter_ (IntPtr jnienv, IntPtr na
8989
__args [0] = new JniArgumentValue ((value == null) ? IntPtr.Zero : ((global::Java.Lang.Object) value).Handle);
9090
_members.InstanceMethods.InvokeVirtualVoidMethod (__id, this, __args);
9191
} finally {
92+
global::System.GC.KeepAlive (value);
9293
}
9394
}
9495
}
@@ -137,6 +138,7 @@ public AbsSpinnerInvoker (IntPtr handle, JniHandleOwnership transfer) : base (ha
137138
_members.InstanceMethods.InvokeAbstractVoidMethod (__id, this, __args);
138139
} finally {
139140
JNIEnv.DeleteLocalRef (native_value);
141+
global::System.GC.KeepAlive (value);
140142
}
141143
}
142144
}

tests/generator-Tests/expected.ji/Adapters/Xamarin.Test.AdapterView.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -124,6 +124,7 @@ public AdapterViewInvoker (IntPtr handle, JniHandleOwnership transfer) : base (h
124124
_members.InstanceMethods.InvokeAbstractVoidMethod (__id, this, __args);
125125
} finally {
126126
JNIEnv.DeleteLocalRef (native_value);
127+
global::System.GC.KeepAlive (value);
127128
}
128129
}
129130
}

tests/generator-Tests/expected.ji/Core_Jar2Xml/Android.Text.SpannableString.cs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@ public unsafe SpannableString (global::Java.Lang.ICharSequence source) : base (I
5454
_members.InstanceMethods.FinishCreateInstance (__id, this, __args);
5555
} finally {
5656
JNIEnv.DeleteLocalRef (native_source);
57+
global::System.GC.KeepAlive (source);
5758
}
5859
}
5960

@@ -74,6 +75,7 @@ public unsafe SpannableString (string source) : base (IntPtr.Zero, JniHandleOwne
7475
_members.InstanceMethods.FinishCreateInstance (__id, this, __args);
7576
} finally {
7677
JNIEnv.DeleteLocalRef (native_source);
78+
global::System.GC.KeepAlive (source);
7779
}
7880
}
7981

@@ -106,6 +108,7 @@ static int n_GetSpanFlags_Ljava_lang_Object_ (IntPtr jnienv, IntPtr native__this
106108
var __rm = _members.InstanceMethods.InvokeVirtualInt32Method (__id, this, __args);
107109
return (global::Android.Text.SpanTypes) __rm;
108110
} finally {
111+
global::System.GC.KeepAlive (what);
109112
}
110113
}
111114

tests/generator-Tests/expected.ji/Core_Jar2Xml/Android.Text.SpannableStringInternal.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,7 @@ static int n_GetSpanFlags_Ljava_lang_Object_ (IntPtr jnienv, IntPtr native__this
6666
var __rm = _members.InstanceMethods.InvokeVirtualInt32Method (__id, this, __args);
6767
return (global::Android.Text.SpanTypes) __rm;
6868
} finally {
69+
global::System.GC.KeepAlive (p0);
6970
}
7071
}
7172

tests/generator-Tests/expected.ji/Core_Jar2Xml/Android.Views.View.cs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -181,6 +181,7 @@ public virtual unsafe void SetOnClickListener (global::Android.Views.View.IOnCli
181181
__args [0] = new JniArgumentValue ((l == null) ? IntPtr.Zero : ((global::Java.Lang.Object) l).Handle);
182182
_members.InstanceMethods.InvokeVirtualVoidMethod (__id, this, __args);
183183
} finally {
184+
global::System.GC.KeepAlive (l);
184185
}
185186
}
186187

@@ -211,6 +212,7 @@ public virtual unsafe void SetOn123Listener (global::Android.Views.View.IOnClick
211212
__args [0] = new JniArgumentValue ((l == null) ? IntPtr.Zero : ((global::Java.Lang.Object) l).Handle);
212213
_members.InstanceMethods.InvokeVirtualVoidMethod (__id, this, __args);
213214
} finally {
215+
global::System.GC.KeepAlive (l);
214216
}
215217
}
216218

@@ -243,6 +245,7 @@ public virtual unsafe void AddTouchables (global::System.Collections.Generic.ILi
243245
_members.InstanceMethods.InvokeVirtualVoidMethod (__id, this, __args);
244246
} finally {
245247
JNIEnv.DeleteLocalRef (native_views);
248+
global::System.GC.KeepAlive (views);
246249
}
247250
}
248251

tests/generator-Tests/expected.ji/GenericArguments/Com.Google.Android.Exoplayer.Drm.FrameworkMediaDrm.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,7 @@ public unsafe void SetOnEventListener (global::Com.Google.Android.Exoplayer.Drm.
6363
__args [0] = new JniArgumentValue ((p0 == null) ? IntPtr.Zero : ((global::Java.Lang.Object) p0).Handle);
6464
_members.InstanceMethods.InvokeAbstractVoidMethod (__id, this, __args);
6565
} finally {
66+
global::System.GC.KeepAlive (p0);
6667
}
6768
}
6869

tests/generator-Tests/expected.ji/NestedTypes/Xamarin.Test.NotificationCompatBase.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -202,6 +202,7 @@ public unsafe InstanceInner (global::Xamarin.Test.NotificationCompatBase __self)
202202
SetHandle (__r.Handle, JniHandleOwnership.TransferLocalRef);
203203
_members.InstanceMethods.FinishCreateInstance (__id, this, __args);
204204
} finally {
205+
global::System.GC.KeepAlive (__self);
205206
}
206207
}
207208

tests/generator-Tests/expected.ji/NormalMethods/Xamarin.Test.SomeObject.cs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@ public unsafe SomeObject (global::Java.Lang.Class c) : base (IntPtr.Zero, JniHan
5252
SetHandle (__r.Handle, JniHandleOwnership.TransferLocalRef);
5353
_members.InstanceMethods.FinishCreateInstance (__id, this, __args);
5454
} finally {
55+
global::System.GC.KeepAlive (c);
5556
}
5657
}
5758

@@ -114,6 +115,8 @@ static int n_Handle_Ljava_lang_Object_Ljava_lang_Throwable_ (IntPtr jnienv, IntP
114115
var __rm = _members.InstanceMethods.InvokeVirtualInt32Method (__id, this, __args);
115116
return __rm;
116117
} finally {
118+
global::System.GC.KeepAlive (o);
119+
global::System.GC.KeepAlive (t);
117120
}
118121
}
119122

@@ -260,6 +263,7 @@ public virtual unsafe void VoidMethodWithParams (string astring, int anint, glob
260263
_members.InstanceMethods.InvokeVirtualVoidMethod (__id, this, __args);
261264
} finally {
262265
JNIEnv.DeleteLocalRef (native_astring);
266+
global::System.GC.KeepAlive (anObject);
263267
}
264268
}
265269

@@ -323,6 +327,7 @@ public virtual unsafe void ArrayListTest (global::System.Collections.Generic.ILi
323327
_members.InstanceMethods.InvokeVirtualVoidMethod (__id, this, __args);
324328
} finally {
325329
JNIEnv.DeleteLocalRef (native_p0);
330+
global::System.GC.KeepAlive (p0);
326331
}
327332
}
328333

tests/generator-Tests/expected.ji/NormalProperties/Xamarin.Test.SomeObject.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -230,6 +230,7 @@ public override unsafe int SomeInteger {
230230
__args [0] = new JniArgumentValue ((value == null) ? IntPtr.Zero : ((global::Java.Lang.Object) value).Handle);
231231
_members.InstanceMethods.InvokeAbstractVoidMethod (__id, this, __args);
232232
} finally {
233+
global::System.GC.KeepAlive (value);
233234
}
234235
}
235236
}

tests/generator-Tests/expected.ji/ParameterXPath/Xamarin.Test.A.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,7 @@ public virtual unsafe void SetA (global::Java.Lang.Object adapter)
6666
_members.InstanceMethods.InvokeVirtualVoidMethod (__id, this, __args);
6767
} finally {
6868
JNIEnv.DeleteLocalRef (native_adapter);
69+
global::System.GC.KeepAlive (adapter);
6970
}
7071
}
7172

@@ -98,6 +99,7 @@ public virtual unsafe void ListTest (global::System.Collections.Generic.IList<gl
9899
_members.InstanceMethods.InvokeVirtualVoidMethod (__id, this, __args);
99100
} finally {
100101
JNIEnv.DeleteLocalRef (native_p0);
102+
global::System.GC.KeepAlive (p0);
101103
}
102104
}
103105

tests/generator-Tests/expected.ji/StaticProperties/Xamarin.Test.SomeObject.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,7 @@ public static unsafe void SetSomeObject (global::Java.Lang.Object newvalue)
108108
__args [0] = new JniArgumentValue ((newvalue == null) ? IntPtr.Zero : ((global::Java.Lang.Object) newvalue).Handle);
109109
_members.StaticMethods.InvokeVoidMethod (__id, __args);
110110
} finally {
111+
global::System.GC.KeepAlive (newvalue);
111112
}
112113
}
113114

tests/generator-Tests/expected.ji/Streams/Java.IO.FilterOutputStream.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@ public unsafe FilterOutputStream (global::System.IO.Stream @out) : base (IntPtr.
5454
_members.InstanceMethods.FinishCreateInstance (__id, this, __args);
5555
} finally {
5656
JNIEnv.DeleteLocalRef (native__out);
57+
global::System.GC.KeepAlive (@out);
5758
}
5859
}
5960

tests/generator-Tests/expected.ji/Streams/Java.IO.InputStream.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -221,6 +221,7 @@ public virtual unsafe int Read (byte[] buffer)
221221
JNIEnv.CopyArray (native_buffer, buffer);
222222
JNIEnv.DeleteLocalRef (native_buffer);
223223
}
224+
global::System.GC.KeepAlive (buffer);
224225
}
225226
}
226227

@@ -262,6 +263,7 @@ public virtual unsafe int Read (byte[] buffer, int byteOffset, int byteCount)
262263
JNIEnv.CopyArray (native_buffer, buffer);
263264
JNIEnv.DeleteLocalRef (native_buffer);
264265
}
266+
global::System.GC.KeepAlive (buffer);
265267
}
266268
}
267269

tests/generator-Tests/expected.ji/Streams/Java.IO.OutputStream.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -141,6 +141,7 @@ public virtual unsafe void Write (byte[] buffer)
141141
JNIEnv.CopyArray (native_buffer, buffer);
142142
JNIEnv.DeleteLocalRef (native_buffer);
143143
}
144+
global::System.GC.KeepAlive (buffer);
144145
}
145146
}
146147

@@ -180,6 +181,7 @@ public virtual unsafe void Write (byte[] buffer, int offset, int count)
180181
JNIEnv.CopyArray (native_buffer, buffer);
181182
JNIEnv.DeleteLocalRef (native_buffer);
182183
}
184+
global::System.GC.KeepAlive (buffer);
183185
}
184186
}
185187

tests/generator-Tests/expected.ji/TestInterface/Test.ME.GenericImplementation.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,7 @@ public virtual unsafe void SetObject (byte[] value)
8787
JNIEnv.CopyArray (native_value, value);
8888
JNIEnv.DeleteLocalRef (native_value);
8989
}
90+
global::System.GC.KeepAlive (value);
9091
}
9192
}
9293

tests/generator-Tests/expected.ji/TestInterface/Test.ME.GenericObjectPropertyImplementation.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,7 @@ static void n_SetObject_Ljava_lang_Object_ (IntPtr jnienv, IntPtr native__this,
106106
__args [0] = new JniArgumentValue ((value == null) ? IntPtr.Zero : ((global::Java.Lang.Object) value).Handle);
107107
_members.InstanceMethods.InvokeVirtualVoidMethod (__id, this, __args);
108108
} finally {
109+
global::System.GC.KeepAlive (value);
109110
}
110111
}
111112
}

tests/generator-Tests/expected.ji/TestInterface/Test.ME.GenericStringImplementation.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,7 @@ public virtual unsafe void SetObject (string[] value)
8787
JNIEnv.CopyArray (native_value, value);
8888
JNIEnv.DeleteLocalRef (native_value);
8989
}
90+
global::System.GC.KeepAlive (value);
9091
}
9192
}
9293

tests/generator-Tests/expected.ji/TestInterface/Test.ME.TestInterfaceImplementation.cs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -188,6 +188,7 @@ public override unsafe int GetSpanFlags (global::Java.Lang.Object tag)
188188
var __rm = _members.InstanceMethods.InvokeAbstractInt32Method (__id, this, __args);
189189
return __rm;
190190
} finally {
191+
global::System.GC.KeepAlive (tag);
191192
}
192193
}
193194

@@ -203,6 +204,7 @@ public override unsafe void Append (global::Java.Lang.ICharSequence value)
203204
_members.InstanceMethods.InvokeAbstractVoidMethod (__id, this, __args);
204205
} finally {
205206
JNIEnv.DeleteLocalRef (native_value);
207+
global::System.GC.KeepAlive (value);
206208
}
207209
}
208210

@@ -219,6 +221,7 @@ public override unsafe void Append (global::Java.Lang.ICharSequence value)
219221
return global::Java.Lang.Object.GetObject<Java.Lang.ICharSequence> (__rm.Handle, JniHandleOwnership.TransferLocalRef);
220222
} finally {
221223
JNIEnv.DeleteLocalRef (native_value);
224+
global::System.GC.KeepAlive (value);
222225
}
223226
}
224227

tests/generator-Tests/expected.ji/java.lang.Enum/Java.Lang.Enum.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@ public unsafe int CompareTo (global::Java.Lang.Object o)
5050
return __rm;
5151
} finally {
5252
JNIEnv.DeleteLocalRef (native_o);
53+
global::System.GC.KeepAlive (o);
5354
}
5455
}
5556

tools/generator/Java.Interop.Tools.Generator.ObjectModel/Parameter.cs

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -276,6 +276,32 @@ public bool Validate (CodeGenerationOptions opt, GenericParameterDefinitionList
276276
return true;
277277
}
278278

279+
public bool ShouldGenerateKeepAlive ()
280+
{
281+
if (Symbol.IsEnum)
282+
return false;
283+
284+
return Type switch {
285+
"bool" => false,
286+
"sbyte" => false,
287+
"char" => false,
288+
"double" => false,
289+
"float" => false,
290+
"int" => false,
291+
"long" => false,
292+
"short" => false,
293+
"uint" => false,
294+
"ushort" => false,
295+
"ulong" => false,
296+
"byte" => false,
297+
"ubyte" => false, // Not a C# type, but we will see it from Kotlin unsigned types support
298+
"string" => false,
299+
"java.lang.String" => false,
300+
"Android.Graphics.Color" => false,
301+
_ => true
302+
};
303+
}
304+
279305
public ISymbol Symbol => sym;
280306
}
281307
}

tools/generator/SourceWriters/BoundConstructor.cs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,10 @@ protected override void WriteBody (CodeWriter writer)
7676
var call_cleanup = constructor.Parameters.GetCallCleanup (opt);
7777
foreach (string cleanup in call_cleanup)
7878
writer.WriteLine (cleanup);
79+
80+
foreach (var p in constructor.Parameters.Where (para => para.ShouldGenerateKeepAlive ()))
81+
writer.WriteLine ($"global::System.GC.KeepAlive ({opt.GetSafeIdentifier (p.Name)});");
82+
7983
writer.Unindent ();
8084

8185
writer.WriteLine ("}");

tools/generator/SourceWriters/Extensions/SourceWriterExtensions.cs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -264,6 +264,9 @@ public static void AddMethodBody (List<string> body, Method method, CodeGenerati
264264
foreach (string cleanup in method.Parameters.GetCallCleanup (opt))
265265
body.Add ("\t" + cleanup);
266266

267+
foreach (var p in method.Parameters.Where (para => para.ShouldGenerateKeepAlive ()))
268+
body.Add ($"\tglobal::System.GC.KeepAlive ({opt.GetSafeIdentifier (p.Name)});");
269+
267270
body.Add ("}");
268271
}
269272

0 commit comments

Comments
 (0)