Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
f6062a0
[bgen] Don't emit/use [Preserve] attributes anymore. Fixes #19524.
rolfbjarne May 20, 2025
cc8c22a
Merge remote-tracking branch 'origin/main' into dev/rolf/bgen-remove-…
rolfbjarne May 21, 2025
be87853
Use different DDA overload.
rolfbjarne May 22, 2025
e0c2d78
Merge remote-tracking branch 'origin/main' into dev/rolf/bgen-remove-…
rolfbjarne May 22, 2025
670f60b
[dotnet-linker] Preserve a bit more.
rolfbjarne May 26, 2025
ac90a69
NSTimer fix
rolfbjarne May 26, 2025
1a77795
Merge remote-tracking branch 'origin/main' into dev/rolf/bgen-remove-…
rolfbjarne May 26, 2025
15d6197
Preserve the Create method
rolfbjarne May 27, 2025
7917209
Merge remote-tracking branch 'origin/main' into dev/rolf/bgen-remove-…
rolfbjarne May 27, 2025
d4ca89e
Fix trimming away cctors.
rolfbjarne May 29, 2025
e677f64
Test fix
rolfbjarne May 29, 2025
fb0023a
A few more test variations.
rolfbjarne May 29, 2025
5d0b19b
Merge remote-tracking branch 'origin/main' into dev/rolf/bgen-remove-…
rolfbjarne May 29, 2025
51541a8
Merge branch 'main' into dev/rolf/bgen-remove-some-preserve-attributes
rolfbjarne Jul 18, 2025
e17e908
Merge branch 'main' into dev/rolf/bgen-remove-some-preserve-attributes
rolfbjarne Sep 9, 2025
db7631b
Try this.
rolfbjarne Sep 23, 2025
a6b100d
Merge remote-tracking branch 'origin/main' into dev/rolf/bgen-remove-…
rolfbjarne Sep 23, 2025
1ba3c72
Merge remote-tracking branch 'origin/main' into dev/rolf/bgen-remove-…
rolfbjarne Sep 24, 2025
11a00c5
[tests] Update bgen tests.
rolfbjarne Sep 24, 2025
9553017
[cecil-tests] Fix
rolfbjarne Sep 24, 2025
6fdc598
Merge remote-tracking branch 'origin/main' into dev/rolf/bgen-remove-…
rolfbjarne Sep 24, 2025
1cc92cd
Merge branch 'main' into dev/rolf/bgen-remove-some-preserve-attributes
rolfbjarne Oct 10, 2025
ef4ab43
Merge remote-tracking branch 'origin/main' into dev/rolf/bgen-remove-…
rolfbjarne Oct 16, 2025
eb31a29
Fix build.
rolfbjarne Oct 20, 2025
4ce0765
Merge remote-tracking branch 'origin/main' into dev/rolf/bgen-remove-…
rolfbjarne Oct 22, 2025
f426347
Add test.
rolfbjarne Oct 23, 2025
8cc34bd
Merge remote-tracking branch 'origin/main' into dev/rolf/bgen-remove-…
rolfbjarne Oct 23, 2025
8c3b60b
Say what to do.
rolfbjarne Oct 28, 2025
246e348
Merge remote-tracking branch 'origin/main' into dev/rolf/bgen-remove-…
rolfbjarne Oct 28, 2025
88c5c63
Update generated file.
rolfbjarne Oct 30, 2025
168c90b
Merge remote-tracking branch 'origin/main' into dev/rolf/bgen-remove-…
rolfbjarne Oct 30, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 15 additions & 0 deletions src/Foundation/NSTimer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,10 @@
//
// Copyright 2011-2014 Xamarin Inc.
//

using System.Reflection;
using System.Collections;
using System.Diagnostics.CodeAnalysis;

namespace Foundation {

Expand Down Expand Up @@ -120,5 +122,18 @@ public NSTimer (NSDate date, TimeSpan when, Action<NSTimer> action, System.Boole
: this (date, when.TotalSeconds, new NSTimerActionDispatcher (action), NSTimerActionDispatcher.Selector, null, repeats)
{
}

// The dependency attribute is required because:
// * We inject a call to Invalidate in the generated Dispose method
// * We optimize Dispose methods to not touch fields that aren't otherwise used, which we do by
// removing the contents of the Dispose method before the linker runs, then after the linker runs
// we determine which fields the Dispose method touches have been linked away and remove any such code.
// We won't remove the call to Invalidate, but the linker may have trimmed away the Invalidate method
/// itself (because we temporarly removed the call to it). So make sure the linker doesn't remove it.
[DynamicDependency ("Invalidate()")]
static NSTimer ()
{
GC.KeepAlive (null);
}
}
}
9 changes: 9 additions & 0 deletions src/Resources.Designer.cs

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 3 additions & 0 deletions src/Resources.resx
Original file line number Diff line number Diff line change
Expand Up @@ -637,4 +637,7 @@
<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>
</data>

<data name="BI1124" xml:space="preserve">
<value>Found a [Preserve] attribute on {0}: [Preserve] is deprecated; use [DynamicDependency] instead.</value>
</data>
</root>
41 changes: 26 additions & 15 deletions src/bgen/Generator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1595,11 +1595,6 @@ void GenerateTrampolinesForQueue (TrampolineInfo [] queue)
print ("//\n// This class bridges native block invocations that call into C#\n//");
PrintExperimentalAttribute (ti.Type);
print ("static internal class {0} {{", ti.StaticName); indent++;
// it can't be conditional without fixing https://github.com/mono/linker/issues/516
// but we have a workaround in place because we can't fix old, binary bindings so...
// print ("[Preserve (Conditional=true)]");
// For .NET we fix it using the DynamicDependency attribute below
print ("[Preserve (Conditional = true)]");
print ("[UnmanagedCallersOnly]");
print ("[UserDelegateType (typeof ({0}))]", ti.UserDelegate);
print ("internal static unsafe {0} Invoke ({1}) {{", ti.ReturnType, ti.Parameters);
Expand Down Expand Up @@ -1672,7 +1667,12 @@ void GenerateTrampolinesForQueue (TrampolineInfo [] queue)
print ("invoker = block->GetDelegateForBlock<{0}> ();", ti.DelegateName);
indent--; print ("}");
print ("");
print ("[Preserve (Conditional=true)]");
print ("[DynamicDependency (nameof (Create))]");
print ($"static {ti.NativeInvokerName} ()");
print ("{");
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.
print ("}");
print ("");
print_generated_code ();
print ("public unsafe static {0}? Create (IntPtr block)\n{{", ti.UserDelegate); indent++;
print ("if (block == IntPtr.Zero)"); indent++;
Expand Down Expand Up @@ -1900,13 +1900,11 @@ void GenerateStrongDictionaryTypes ()
if (BindingTouch.SupportsXmlDocumentation) {
print ($"/// <summary>Creates a new <see cref=\"{typeName}\" /> with default (empty) values.</summary>");
}
print ("[Preserve (Conditional = true)]");
print ("public {0} () : base (new NSMutableDictionary ()) {{}}\n", typeName);
if (BindingTouch.SupportsXmlDocumentation) {
print ($"/// <summary>Creates a new <see cref=\"{typeName}\" /> from the values that are specified in <paramref name=\"dictionary\" />.</summary>");
print ($"/// <param name=\"dictionary\">The dictionary to use to populate the properties of this type.</param>");
}
print ("[Preserve (Conditional = true)]");
print ("public {0} (NSDictionary? dictionary) : base (dictionary) {{}}\n", typeName);

foreach (var pi in dictType.GatherProperties (this)) {
Expand Down Expand Up @@ -5107,6 +5105,7 @@ void GenerateProtocolTypes (Type type, string class_visibility, string TypeName,
}
}

var dynamicDependencies = new List<string> ();
if (instanceMethods.Any () || instanceProperties.Any ()) {
// Tell the trimmer to not remove any instance method/property if the interface itself isn't trimmed away.
// These members are required for the registrar to determine if a particular implementing method
Expand All @@ -5117,10 +5116,14 @@ void GenerateProtocolTypes (Type type, string class_visibility, string TypeName,
var docIds = instanceMethods
.Select (mi => DocumentationManager.GetDocId (mi, includeDeclaringType: false, alwaysIncludeParenthesis: true))
.Concat (instanceProperties.Select (v => v.Name))
.OrderBy (name => name);
foreach (var docId in docIds) {
print ($"[DynamicDependencyAttribute (\"{docId}\")]");
}
.Select (v => $"\"{v}\"");
dynamicDependencies.AddRange (docIds);
}
// Tell the trimmer to not remove the wrapper type if the interface itself isn't trimmed away
dynamicDependencies.Add ($"DynamicallyAccessedMemberTypes.Interfaces | DynamicallyAccessedMemberTypes.PublicConstructors, typeof ({TypeName}Wrapper)");
if (dynamicDependencies.Count > 0) {
foreach (var dd in dynamicDependencies.OrderBy (v => v))
print ($"[DynamicDependencyAttribute ({dd})]");
print ("[BindingImpl (BindingImplOptions.GeneratedCode | BindingImplOptions.Optimizable)]");
print ($"static I{TypeName} ()");
print ("{");
Expand Down Expand Up @@ -5233,12 +5236,19 @@ void GenerateProtocolTypes (Type type, string class_visibility, string TypeName,
indent++;
// ctor (IntPtr, bool)
PrintExperimentalAttribute (type);
print ("[Preserve (Conditional = true)]");
print ("public {0}Wrapper ({1} handle, bool owns)", TypeName, NativeHandleType);
print ("\t: base (handle, owns)");
print ("{");
print ("}");
print ("");

print ($"[DynamicDependencyAttribute (DynamicallyAccessedMemberTypes.PublicConstructors, typeof ({TypeName}Wrapper))]");
print ($"static {TypeName}Wrapper ()");
print ("{");
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.
print ("}");
print ("");

// Methods
// First find duplicates and select the best one. We use the selector to determine what's a duplicate.
var methodData = requiredInstanceMethods.Select ((v) => {
Expand Down Expand Up @@ -5451,6 +5461,9 @@ public void PrintPreserveAttribute (ICustomAttributeProvider mi)
if (p is null)
return;

if (!BindThirdPartyLibrary)
exceptions.Add (ErrorHelper.CreateError (1124 /* Found a [Preserve] attribute on {0}: [Preserve] is deprecated; use [DynamicDependency] instead. */, FormatProvider (mi)));

if (p.AllMembers)
print ("[Preserve (AllMembers = true)]");
else if (p.Conditional)
Expand Down Expand Up @@ -6743,7 +6756,6 @@ public void Generate (Type type)
} else
print ("internal {0}? {1};", Nomenclator.GetDelegateName (mi), miname);

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

Expand Down Expand Up @@ -6850,7 +6862,6 @@ public void Generate (Type type)
selRespondsToSelector = "selRespondsToSelector";
}

print ("[Preserve (Conditional = true)]");
print ("public override bool RespondsToSelector (Selector? sel)");
print ("{");
++indent;
Expand Down
2 changes: 0 additions & 2 deletions src/foundation.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7378,8 +7378,6 @@ interface NSTimer {
[Export ("fireDate", ArgumentSemantic.Copy)]
NSDate FireDate { get; set; }

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

Expand Down
Loading