Skip to content

Commit 18f02dc

Browse files
authored
[bgen] Don't emit/use [Preserve] attributes anymore. Fixes #19524. (#22860)
Use [DynamicDependency] instead! Fixes #19524.
1 parent 4799269 commit 18f02dc

17 files changed

+529
-23
lines changed

src/Foundation/NSTimer.cs

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,10 @@
2020
//
2121
// Copyright 2011-2014 Xamarin Inc.
2222
//
23+
2324
using System.Reflection;
2425
using System.Collections;
26+
using System.Diagnostics.CodeAnalysis;
2527

2628
namespace Foundation {
2729

@@ -120,5 +122,18 @@ public NSTimer (NSDate date, TimeSpan when, Action<NSTimer> action, System.Boole
120122
: this (date, when.TotalSeconds, new NSTimerActionDispatcher (action), NSTimerActionDispatcher.Selector, null, repeats)
121123
{
122124
}
125+
126+
// The dependency attribute is required because:
127+
// * We inject a call to Invalidate in the generated Dispose method
128+
// * We optimize Dispose methods to not touch fields that aren't otherwise used, which we do by
129+
// removing the contents of the Dispose method before the linker runs, then after the linker runs
130+
// we determine which fields the Dispose method touches have been linked away and remove any such code.
131+
// We won't remove the call to Invalidate, but the linker may have trimmed away the Invalidate method
132+
/// itself (because we temporarly removed the call to it). So make sure the linker doesn't remove it.
133+
[DynamicDependency ("Invalidate()")]
134+
static NSTimer ()
135+
{
136+
GC.KeepAlive (null);
137+
}
123138
}
124139
}

src/Resources.Designer.cs

Lines changed: 9 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/Resources.resx

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -637,4 +637,7 @@
637637
<value>The type {0} has a [Protocol] and a [BaseType] attribute, but no [Model] attribute. This is likely incorrect; either remove the [BaseType] attribute, or add a [Model] attribute.</value>
638638
</data>
639639

640+
<data name="BI1124" xml:space="preserve">
641+
<value>Found a [Preserve] attribute on {0}: [Preserve] is deprecated; use [DynamicDependency] instead.</value>
642+
</data>
640643
</root>

src/bgen/Generator.cs

Lines changed: 26 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1595,11 +1595,6 @@ void GenerateTrampolinesForQueue (TrampolineInfo [] queue)
15951595
print ("//\n// This class bridges native block invocations that call into C#\n//");
15961596
PrintExperimentalAttribute (ti.Type);
15971597
print ("static internal class {0} {{", ti.StaticName); indent++;
1598-
// it can't be conditional without fixing https://github.com/mono/linker/issues/516
1599-
// but we have a workaround in place because we can't fix old, binary bindings so...
1600-
// print ("[Preserve (Conditional=true)]");
1601-
// For .NET we fix it using the DynamicDependency attribute below
1602-
print ("[Preserve (Conditional = true)]");
16031598
print ("[UnmanagedCallersOnly]");
16041599
print ("[UserDelegateType (typeof ({0}))]", ti.UserDelegate);
16051600
print ("internal static unsafe {0} Invoke ({1}) {{", ti.ReturnType, ti.Parameters);
@@ -1672,7 +1667,12 @@ void GenerateTrampolinesForQueue (TrampolineInfo [] queue)
16721667
print ("invoker = block->GetDelegateForBlock<{0}> ();", ti.DelegateName);
16731668
indent--; print ("}");
16741669
print ("");
1675-
print ("[Preserve (Conditional=true)]");
1670+
print ("[DynamicDependency (nameof (Create))]");
1671+
print ($"static {ti.NativeInvokerName} ()");
1672+
print ("{");
1673+
print ("\tGC.KeepAlive (null);"); // need to do _something_ (doesn't seem to matter what), otherwise the static cctor (and the DynamicDependency attributes) are trimmed away.
1674+
print ("}");
1675+
print ("");
16761676
print_generated_code ();
16771677
print ("public unsafe static {0}? Create (IntPtr block)\n{{", ti.UserDelegate); indent++;
16781678
print ("if (block == IntPtr.Zero)"); indent++;
@@ -1900,13 +1900,11 @@ void GenerateStrongDictionaryTypes ()
19001900
if (BindingTouch.SupportsXmlDocumentation) {
19011901
print ($"/// <summary>Creates a new <see cref=\"{typeName}\" /> with default (empty) values.</summary>");
19021902
}
1903-
print ("[Preserve (Conditional = true)]");
19041903
print ("public {0} () : base (new NSMutableDictionary ()) {{}}\n", typeName);
19051904
if (BindingTouch.SupportsXmlDocumentation) {
19061905
print ($"/// <summary>Creates a new <see cref=\"{typeName}\" /> from the values that are specified in <paramref name=\"dictionary\" />.</summary>");
19071906
print ($"/// <param name=\"dictionary\">The dictionary to use to populate the properties of this type.</param>");
19081907
}
1909-
print ("[Preserve (Conditional = true)]");
19101908
print ("public {0} (NSDictionary? dictionary) : base (dictionary) {{}}\n", typeName);
19111909

19121910
foreach (var pi in dictType.GatherProperties (this)) {
@@ -5107,6 +5105,7 @@ void GenerateProtocolTypes (Type type, string class_visibility, string TypeName,
51075105
}
51085106
}
51095107

5108+
var dynamicDependencies = new List<string> ();
51105109
if (instanceMethods.Any () || instanceProperties.Any ()) {
51115110
// Tell the trimmer to not remove any instance method/property if the interface itself isn't trimmed away.
51125111
// These members are required for the registrar to determine if a particular implementing method
@@ -5117,10 +5116,14 @@ void GenerateProtocolTypes (Type type, string class_visibility, string TypeName,
51175116
var docIds = instanceMethods
51185117
.Select (mi => DocumentationManager.GetDocId (mi, includeDeclaringType: false, alwaysIncludeParenthesis: true))
51195118
.Concat (instanceProperties.Select (v => v.Name))
5120-
.OrderBy (name => name);
5121-
foreach (var docId in docIds) {
5122-
print ($"[DynamicDependencyAttribute (\"{docId}\")]");
5123-
}
5119+
.Select (v => $"\"{v}\"");
5120+
dynamicDependencies.AddRange (docIds);
5121+
}
5122+
// Tell the trimmer to not remove the wrapper type if the interface itself isn't trimmed away
5123+
dynamicDependencies.Add ($"DynamicallyAccessedMemberTypes.Interfaces | DynamicallyAccessedMemberTypes.PublicConstructors, typeof ({TypeName}Wrapper)");
5124+
if (dynamicDependencies.Count > 0) {
5125+
foreach (var dd in dynamicDependencies.OrderBy (v => v))
5126+
print ($"[DynamicDependencyAttribute ({dd})]");
51245127
print ("[BindingImpl (BindingImplOptions.GeneratedCode | BindingImplOptions.Optimizable)]");
51255128
print ($"static I{TypeName} ()");
51265129
print ("{");
@@ -5233,12 +5236,19 @@ void GenerateProtocolTypes (Type type, string class_visibility, string TypeName,
52335236
indent++;
52345237
// ctor (IntPtr, bool)
52355238
PrintExperimentalAttribute (type);
5236-
print ("[Preserve (Conditional = true)]");
52375239
print ("public {0}Wrapper ({1} handle, bool owns)", TypeName, NativeHandleType);
52385240
print ("\t: base (handle, owns)");
52395241
print ("{");
52405242
print ("}");
52415243
print ("");
5244+
5245+
print ($"[DynamicDependencyAttribute (DynamicallyAccessedMemberTypes.PublicConstructors, typeof ({TypeName}Wrapper))]");
5246+
print ($"static {TypeName}Wrapper ()");
5247+
print ("{");
5248+
print ("\tGC.KeepAlive (null);"); // need to do _something_ (doesn't seem to matter what), otherwise the static cctor (and the DynamicDependency attribute) is trimmed away.
5249+
print ("}");
5250+
print ("");
5251+
52425252
// Methods
52435253
// First find duplicates and select the best one. We use the selector to determine what's a duplicate.
52445254
var methodData = requiredInstanceMethods.Select ((v) => {
@@ -5451,6 +5461,9 @@ public void PrintPreserveAttribute (ICustomAttributeProvider mi)
54515461
if (p is null)
54525462
return;
54535463

5464+
if (!BindThirdPartyLibrary)
5465+
exceptions.Add (ErrorHelper.CreateError (1124 /* Found a [Preserve] attribute on {0}: [Preserve] is deprecated; use [DynamicDependency] instead. */, FormatProvider (mi)));
5466+
54545467
if (p.AllMembers)
54555468
print ("[Preserve (AllMembers = true)]");
54565469
else if (p.Conditional)
@@ -6743,7 +6756,6 @@ public void Generate (Type type)
67436756
} else
67446757
print ("internal {0}? {1};", Nomenclator.GetDelegateName (mi), miname);
67456758

6746-
print ("[Preserve (Conditional = true)]");
67476759
if (isProtocolEventBacked)
67486760
print ("[Export (\"{0}\")]", FindSelector (dtype, mi));
67496761

@@ -6850,7 +6862,6 @@ public void Generate (Type type)
68506862
selRespondsToSelector = "selRespondsToSelector";
68516863
}
68526864

6853-
print ("[Preserve (Conditional = true)]");
68546865
print ("public override bool RespondsToSelector (Selector? sel)");
68556866
print ("{");
68566867
++indent;

src/foundation.cs

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7378,8 +7378,6 @@ interface NSTimer {
73787378
[Export ("fireDate", ArgumentSemantic.Copy)]
73797379
NSDate FireDate { get; set; }
73807380

7381-
// Note: preserving this member allows us to re-enable the `Optimizable` binding flag
7382-
[Preserve (Conditional = true)]
73837381
[Export ("invalidate")]
73847382
void Invalidate ();
73857383

0 commit comments

Comments
 (0)