8000 [bgen] Don't emit/use [Preserve] attributes anymore. Fixes #19524. by rolfbjarne · Pull Request #22860 · dotnet/macios · GitHub
[go: up one dir, main page]
More Web Proxy on the site http://driver.im/
Skip to content

[bgen] Don't emit/use [Preserve] attributes anymore. Fixes #19524. #22860

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 13 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
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
14 changes: 14 additions & 0 deletions src/Foundation/NSTimer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
// Copyright 2011-2014 Xamarin Inc.
//
using System;
using System.Diagnostics.CodeAnalysis;
using System.Reflection;
using System.Collections;
using System.Runtime.InteropServices;
Expand Down Expand Up @@ -124,5 +125,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);
}
}
}
33 changes: 18 additions & 15 deletions src/bgen/Generator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1597,11 +1597,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 @@ -1674,7 +1669,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 @@ -1812,13 +1812,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 @@ -4986,6 +4984,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 @@ -4996,10 +4995,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 @@ -5112,7 +5115,6 @@ 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 ("{");
Expand Down Expand Up @@ -5330,6 +5332,9 @@ public void PrintPreserveAttribute (ICustomAttributeProvider mi)
if (p is null)
return;

if (!BindThirdPartyLibrary)
throw new InvalidOperationException ($"Found [Preserve] on {FormatProvider (mi)}: [Preserve] is deprecated, so don't use it.");
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe point to the new way of doing this here?


if (p.AllMembers)
print ("[Preserve (AllMembers = true)]");
else if (p.Conditional)
Expand Down Expand Up @@ -6522,7 +6527,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 @@ -6629,7 +6633,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 @@ -7154,8 +7154,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
16 changes: 8 additions & 8 deletions tests/common/shared-dotnet.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -61,14 +61,6 @@
<CustomBeforeMicrosoftCommonTargets>$(CustomBeforeMicrosoftCommonTargets);$(MSBuildThisFileDirectory)SupportedOSPlatformVersions.targets</CustomBeforeMicrosoftCommonTargets>
</PropertyGroup>

<!-- Logic for NativeAOT -->
<PropertyGroup Condition="'$(PublishAot)' == 'true' And '$(_IsPublishing)' == 'true'">
<!-- Define NATIVEAOT when using NativeAOT -->
<DefineConstants>$(DefineConstants);NATIVEAOT</DefineConstants>
<!-- We're enabling warnaserror by default, but we're not warning-free for ILC (especially for NUnit), so disable warnaserror for ILC - https://github.com/dotnet/macios/issues/19911 -->
<IlcTreatWarningsAsErrors>false</IlcTreatWarningsAsErrors>
</PropertyGroup>

<!-- Trimmer options -->
<!-- We want to ignore any trimmer warnings from NUnit. We do this by:
* Enable all warnings
Expand Down Expand Up @@ -102,6 +94,14 @@

<Import Project="$(GeneratedProjectFile)" Condition="'$(GeneratedProjectFile)' != ''" />

<!-- Logic for NativeAOT -->
<PropertyGroup Condition="'$(PublishAot)' == 'true' And '$(_IsPublishing)' == 'true'">
<!-- Define NATIVEAOT when using NativeAOT -->
<DefineConstants>$(DefineConstants);NATIVEAOT</DefineConstants>
<!-- We're enabling warnaserror by default, but we're not warning-free for ILC (especially for NUnit), so disable warnaserror for ILC - https://github.com/dotnet/macios/issues/19911 -->
<IlcTreatWarningsAsErrors>false</IlcTreatWarningsAsErrors>
</PropertyGroup>

<!--
This is a helper target for our tests to zip up the app bundle.
This is useful when building remotely, and we want to inspect the results on Windows:
Expand Down
2 changes: 1 addition & 1 deletion tests/common/shared-dotnet.mk
Original file line number Diff line number Diff line change
Expand Up @@ -208,7 +208,7 @@ diag: prepare
select binlog in $(BINLOGS); do $(DOTNET) build /v:diag $$binlog; break; done \
fi

list-variations listvariations show-variations showvariations variations:
list-variations listvariations show-variations showvariations variations help:
$(Q) if ! command -v jq > /dev/null; then echo "$(shell tput setaf 9)jq isn't installed. Install by doing 'brew install jq'$(shell tput sgr0)"; exit 1; fi
$(Q) echo "Test variations for $(shell tput setaf 6)$(TESTNAME)$(shell tput sgr0):"
$(Q) echo ""
Expand Down
20 changes: 19 additions & 1 deletion tests/common/test-variations.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,31 @@
<ItemGroup>
<TestVariations Include="interpreter" Description="Run with the interpreter ('UseInterpreter=true')" />
<TestVariations Include="bundle-original-resources" Description="Bundle original resources ('BundleOriginalResources=true')" />
<TestVariations Include="do-not-bundle-original-resources" Description="Do not original resources ('BundleOriginalResources=false')" />
<TestVariations Include="do-not-bundle-original-resources" Description="Do not bundle original resources ('BundleOriginalResources=false')" />
<TestVariations Include="nativeaot" Description="Run with NativeAOT" />
<TestVariations Include="linksdk" Description="Run with trimmer (MtouchLink/LinkMode) set to 'SdkOnly'" />
<TestVariations Include="linkall" Description="Run with trimmer (MtouchLink/LinkMode) set to 'Full'" />
</ItemGroup>

<PropertyGroup Condition="'$(TestVariation)' == 'interpreter'">
<UseInterpreter>true</UseInterpreter>
</PropertyGroup>

<PropertyGroup Condition="'$(TestVariation)' == 'nativeaot'">
<PublishAot>true</PublishAot>
<_IsPublishing>true</_IsPublishing>
</PropertyGroup>

<PropertyGroup Condition="'$(TestVariation)' == 'linksdk'">
<MtouchLink>SdkOnly</MtouchLink>
<LinkMode>$(MtouchLink)</LinkMode>
</PropertyGroup>

<PropertyGroup Condition="'$(TestVariation)' == 'linkall'">
<MtouchLink>Full</MtouchLink>
<LinkMode>$(MtouchLink)</LinkMode>
</PropertyGroup>

<PropertyGroup Condition="'$(TestVariation)' == 'bundle-original-resources'">
<BundleOriginalResources>true</BundleOriginalResources>
</PropertyGroup>
Expand Down
11 changes: 11 additions & 0 deletions tests/monotouch-test/Photos/LivePhotoEditingContextTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,17 @@ public void Linker ()
[UnconditionalSuppressMessage ("Trimming", "IL2026", Justification = "This test pokes at internals, so it's expected to not be trimmer safe. It works though, so unless something changes, we're going to assume it's trimmer-compatible.")]
[UnconditionalSuppressMessage ("Trimming", "IL2075", Justification = "This test pokes at internals, so it's expected to not be trimmer safe. It works though, so unless something changes, we're going to assume it's trimmer-compatible.")]
[Test]
#if __MACOS__
[DynamicDependency ("Invoke", "ObjCRuntime.Trampolines.SDPHLivePhotoFrameProcessingBlock", "Microsoft.macOS")]
#elif __TVOS__
[DynamicDependency ("Invoke", "ObjCRuntime.Trampolines.SDPHLivePhotoFrameProcessingBlock", "Microsoft.tvOS")]
#elif __MACCATALYST__
[DynamicDependency ("Invoke", "ObjCRuntime.Trampolines.SDPHLivePhotoFrameProcessingBlock", "Microsoft.MacCatalyst")]
#elif __IOS__
[DynamicDependency ("Invoke", "ObjCRuntime.Trampolines.SDPHLivePhotoFrameProcessingBlock", "Microsoft.iOS")]
#else
#error Unknown platform
#endif
public unsafe void FrameProcessingBlock2 ()
{
if (!Runtime.DynamicRegistrationSupported)
Expand Down
10 changes: 8 additions & 2 deletions tools/dotnet-linker/Steps/ManagedRegistrarLookupTablesStep.cs
Original file line number Diff line number Diff line change
Expand Up @@ -393,8 +393,14 @@ void GenerateConstructINativeObject (TypeDefinition registrarType)
var ctor = abr.CurrentAssembly.MainModule.ImportReference (ctorRef);

// we need to preserve the constructor because it might not be used anywhere else
if (IsTrimmed (ctor))
Annotations.Mark (ctor.Resolve ());
if (IsTrimmed (ctor)) {
var ctorDefinition = ctor.Resolve ();
Annotations.Mark (ctorDefinition);
foreach (var instr in ctorDefinition.Body.Instructions) {
if (instr.Operand is MethodReference mr)
Annotations.Mark (mr.Resolve ());
}
}

if (!ManagedRegistrarStep.IsOpenType (type)) {
EnsureVisible (createInstanceMethod, ctor);
Expand Down
6 changes: 6 additions & 0 deletions tools/dotnet-linker/Steps/SetBeforeFieldInitStep.cs
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,12 @@ protected override void Process (TypeDefinition type)
if (Configuration.DerivedLinkContext.App.Optimizations.RegisterProtocols != true)
return;

if (Configuration.DerivedLinkContext.App.XamarinRuntime == Bundler.XamarinRuntime.NativeAOT) {
// We can't remove the static constructor in the trimmer if we're using NativeAOT,
// because NativeAOT needs it for its own trimming logic.
return;
}

if (!type.IsBeforeFieldInit && type.IsInterface && type.HasMethods) {
var cctor = type.GetTypeConstructor ();
if (cctor is not null && cctor.IsBindingImplOptimizableCode (LinkContext))
Expand Down
22 changes: 21 additions & 1 deletion tools/linker/CoreOptimizeGeneratedCode.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1413,7 +1413,27 @@ bool ProcessProtocolInterfaceStaticConstructor (MethodDefinition method)
Driver.Log (4, "Optimized static constructor in the protocol interface {0} (static constructor was cleared and custom attributes removed)", method.DeclaringType.FullName);
method.Body.Instructions.Clear ();
method.Body.Instructions.Add (Instruction.Create (OpCodes.Ret));
method.CustomAttributes.Clear ();

// Only remove DynamicDependency attributes that takes a single string argument.
// The generator generates other DynamicDependency attributes, and we don't want to remove those.
for (var i = method.CustomAttributes.Count - 1; i >= 0; i--) {
var ca = method.CustomAttributes [i];

if (!ca.AttributeType.Is ("System.Diagnostics.CodeAnalysis", "DynamicDependencyAttribute"))
continue;

if (!ca.HasConstructorArguments)
continue;

if (ca.ConstructorArguments.Count != 1)
continue;

if (!ca.ConstructorArguments [0].Type.Is ("System", "String"))
continue;

method.CustomAttributes.RemoveAt (i);
}

return true;
}
}
Expand Down
Loading
0