Skip to content

Commit

Permalink
[NativeAOT] Add support for conditional [Preserve] attributes (#18803)
Browse files Browse the repository at this point in the history
This is a follow-up to #18666

The idea is to transform `[Preserve(Conditional = true)]` into
`[DynamicDependency(signature, type)]` on the static constructor of the
given type:

```c#
class MyClass {
    [Preserve (Conditional = true)]
    public void MyMethod () { }

    // transformed into ...

    [DynamicDependency ("MyMethod", typeof (MyClass))]
    static MyClass() { /* either the existing cctor or a new empty cctor */ }

    public void MyMethod () { }
}
```
  • Loading branch information
simonrozsival authored Aug 25, 2023
1 parent 2f89e5b commit 4ddee61
Show file tree
Hide file tree
Showing 3 changed files with 78 additions and 33 deletions.
97 changes: 78 additions & 19 deletions tools/dotnet-linker/ApplyPreserveAttributeBase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ namespace Mono.Tuner {
public abstract class ApplyPreserveAttributeBase : ConfigurationAwareSubStep {

AppBundleRewriter? abr;
Queue<Action> deferredActions = new ();

protected override string Name { get => "Apply Preserve Attribute"; }

Expand All @@ -32,16 +33,7 @@ public abstract class ApplyPreserveAttributeBase : ConfigurationAwareSubStep {
// set 'removeAttribute' to true if you want the preserved attribute to be removed from the final assembly
protected abstract bool IsPreservedAttribute (ICustomAttributeProvider provider, CustomAttribute attribute, out bool removeAttribute);

public override SubStepTargets Targets {
get {
return SubStepTargets.Type
| SubStepTargets.Field
| SubStepTargets.Method
| SubStepTargets.Property
| SubStepTargets.Event
| SubStepTargets.Assembly;
}
}
public override SubStepTargets Targets => SubStepTargets.Assembly;

public override void Initialize (LinkContext context)
{
Expand All @@ -51,6 +43,51 @@ public override void Initialize (LinkContext context)
abr = Configuration.AppBundleRewriter;
}

protected override void Process (AssemblyDefinition assembly)
{
BrowseTypes (assembly.MainModule.Types);
ProcessDeferredActions ();
}

void BrowseTypes (IEnumerable<TypeDefinition> types)
{
foreach (TypeDefinition type in types) {
ProcessType (type);

if (type.HasFields) {
foreach (FieldDefinition field in type.Fields)
ProcessField (field);
}

if (type.HasMethods) {
foreach (MethodDefinition method in type.Methods)
ProcessMethod (method);
}

if (type.HasProperties) {
foreach (PropertyDefinition property in type.Properties)
ProcessProperty (property);
}

if (type.HasEvents) {
foreach (EventDefinition @event in type.Events)
ProcessEvent (@event);
}

if (type.HasNestedTypes) {
BrowseTypes (type.NestedTypes);
}
}
}

void ProcessDeferredActions ()
{
while (deferredActions.Count > 0) {
var action = deferredActions.Dequeue ();
action.Invoke ();
}
}

public override bool IsActiveFor (AssemblyDefinition assembly)
{
return Annotations.GetAction (assembly) == AssemblyAction.Link;
Expand Down Expand Up @@ -205,13 +242,7 @@ protected void PreserveType (TypeDefinition type, bool allMembers)
MethodDefinition GetOrCreateModuleConstructor (ModuleDefinition @module)
{
var moduleType = @module.GetModuleType ();
var moduleConstructor = moduleType.GetTypeConstructor ();
if (moduleConstructor is null) {
moduleConstructor = moduleType.AddMethod (".cctor", MethodAttributes.Private | MethodAttributes.HideBySig | MethodAttributes.RTSpecialName | MethodAttributes.SpecialName | MethodAttributes.Static, abr!.System_Void);
moduleConstructor.CreateBody (out var il);
il.Emit (OpCodes.Ret);
}
return moduleConstructor;
return GetOrCreateStaticConstructor (moduleType);
}

void AddDynamicDependencyAttribute (TypeDefinition type, bool allMembers)
Expand All @@ -234,8 +265,7 @@ void AddConditionalDynamicDependencyAttribute (TypeDefinition onType, MethodDefi
if (abr is null)
return;

// I haven't found a way to express a conditional Preserve attribute using DynamicDependencyAttribute :/
ErrorHelper.Warning (2112, Errors.MX2112 /* Unable to apply the conditional [Preserve] attribute on the member {0} */, forMethod.FullName);
deferredActions.Enqueue (() => AddDynamicDependencyAttributeToStaticConstructor (onType, forMethod));
}

void AddDynamicDependencyAttribute (IMetadataTokenProvider provider)
Expand All @@ -258,5 +288,34 @@ void AddDynamicDependencyAttribute (IMetadataTokenProvider provider)

abr.ClearCurrentAssembly ();
}

void AddDynamicDependencyAttributeToStaticConstructor (TypeDefinition onType, MethodDefinition forMethod)
{
if (abr is null)
return;

abr.ClearCurrentAssembly ();
abr.SetCurrentAssembly (onType.Module.Assembly);

var cctor = GetOrCreateStaticConstructor (onType);
var signature = DocumentationComments.GetSignature (forMethod);
var attrib = abr.CreateDynamicDependencyAttribute (signature, onType);
cctor.CustomAttributes.Add (attrib);
Annotations.AddPreservedMethod (onType, cctor);

abr.ClearCurrentAssembly ();
}

MethodDefinition GetOrCreateStaticConstructor (TypeDefinition type)
{
var staticCtor = type.GetTypeConstructor ();
if (staticCtor is null) {
staticCtor = type.AddMethod (".cctor", MethodAttributes.Private | MethodAttributes.HideBySig | MethodAttributes.RTSpecialName | MethodAttributes.SpecialName | MethodAttributes.Static, abr!.System_Void);
staticCtor.CreateBody (out var il);
il.Emit (OpCodes.Ret);
}

return staticCtor;
}
}
}
9 changes: 0 additions & 9 deletions tools/mtouch/Errors.designer.cs

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

5 changes: 0 additions & 5 deletions tools/mtouch/Errors.resx
Original file line number Diff line number Diff line change
Expand Up @@ -1346,11 +1346,6 @@
</value>
</data>

<data name="MX2112" xml:space="preserve">
<!-- This is a warning -->
<value>Unable to apply the conditional [Preserve] attribute on the member {0}.</value>
</data>

<!-- 2200 -> 2299 is used by/reserved for ExceptionalSubStep subclasses in the linker -->
<!-- 220x: PreserveSmartEnumConversionsSubStep -->
<!-- 221x: RemoveBitcodeIncompatibleCodeStep -->
Expand Down

6 comments on commit 4ddee61

@vs-mobiletools-engineering-service2

This comment was marked as outdated.

@vs-mobiletools-engineering-service2

This comment was marked as outdated.

@vs-mobiletools-engineering-service2

This comment was marked as outdated.

@vs-mobiletools-engineering-service2

This comment was marked as outdated.

@vs-mobiletools-engineering-service2

This comment was marked as outdated.

@vs-mobiletools-engineering-service2

This comment was marked as outdated.

Please sign in to comment.