Bug 3862 - Linker removes certain attributes on members with [Preserve]
Summary: Linker removes certain attributes on members with [Preserve]
Status: RESOLVED FIXED
Alias: None
Product: iOS
Classification: Xamarin
Component: Tools ()
Version: 5.2
Hardware: PC Mac OS
: --- normal
Target Milestone: Untriaged
Assignee: Sebastien Pouliot
URL:
Depends on:
Blocks:
 
Reported: 2012-03-12 15:45 UTC by David Merriman
Modified: 2012-03-13 08:42 UTC (History)
2 users (show)

Tags:
Is this bug a regression?: ---
Last known good build:

Notice (2018-05-24): bugzilla.xamarin.com is now in read-only mode.

Please join us on Visual Studio Developer Community and in the Xamarin and Mono organizations on GitHub to continue tracking issues. Bugzilla will remain available for reference in read-only mode. We will continue to work on open Bugzilla bugs, copy them to the new locations as needed for follow-up, and add the new items under Related Links.

Our sincere thanks to everyone who has contributed on this bug tracker over the years. Thanks also for your understanding as we make these adjustments and improvements for the future.


Please create a new report on Developer Community or GitHub with your current version information, steps to reproduce, and relevant error messages or log files if you are hitting an issue that looks similar to this resolved bug and you do not yet see a matching new report.

Related Links:
Status:
RESOLVED FIXED

Description David Merriman 2012-03-12 15:45:41 UTC
Consider the following code snippet:

using System;
using System.ComponentModel;
using System.Drawing;
using System.Reflection;

using MonoTouch.Foundation;
using MonoTouch.UIKit;

namespace AttributeRemovalBug
{

    public partial class AttributeRemovalBugViewController : UIViewController
    {
        public AttributeRemovalBugViewController(IntPtr handle) : base (handle)
        {
        }

        public override void ViewDidLoad()
        {
            base.ViewDidLoad();

            Type t = GetType().Assembly.GetType("AttributeRemovalBug.IAmNotStaticallyReferenced");
            Console.WriteLine("typeof(IAmNotStaticallyReferenced): {0}", t);
            var props = t.GetProperties();
            foreach (var prop in props)
            {
                var attrs = prop.GetCustomAttributes(true);
                Console.WriteLine("{0} has {1} attributes: ", prop.Name, attrs.Length);
                foreach (var att in attrs)
                {
                    Console.WriteLine("\t{0}", att.GetType());
                }
            }
    }

    [Preserve(AllMembers=true)]
    public interface IAmNotStaticallyReferenced
    {
        [DefaultValue(null)]
        string Property { get; }
    }
}

The interface has no static references to it anywhere in code, but it is used via reflection - therefore, it has a [Preserve] attribute.  Without it, the code snippet above would crash with a NullReferenceException in ViewDidLoad().  With linking off, I get the output I would expect:

typeof(IAmNotStaticallyReferenced): AttributeRemovalBug.IAmNotStaticallyReferenced
Property has 1 attributes:
    System.ComponentModel.DefaultValueAttribute

With the linker on, I would expected the same output, but instead I get:

typeof(IAmNotStaticallyReferenced): AttributeRemovalBug.IAmNotStaticallyReferenced
Property has 0 attributes:

It seems as though the linker is removing this attribute, despite the [Preserve] on the interface.  The problem is not limited to [DefaultValue]; it occurs with [Preserve] itself and [ReadOnly], but not [Description], which is quite confusing because [ReadOnly] and [DefaultValue] are in the same namespace as [Description], System.ComponentModel.  

The reason this matters especially to me is that MEF (http://mef.codeplex.com/) depends on the [DefaultValue] attribute to signal whether certain metadata attributes are optional or required.  This is preventing the project I am working on from turning on the linker for all code and reducing our application size significantly.  Everything works with the linker off, but turning it on breaks everything.
Comment 1 Sebastien Pouliot 2012-03-12 16:14:01 UTC
The linker removes some attributes [1] that are not generally used at runtime. That includes System.ComponentModel attributes that are often used for design-time.

The best, immediate workaround is to skip linking (--linkskip) for the specific assemblies where the attribute is present (and needed) and continue to use --linkall (for all others).

[1] https://github.com/mono/mono/blob/master/mcs/tools/tuner/Mono.Tuner/RemoveAttributesBase.cs
Comment 2 David Merriman 2012-03-12 18:08:43 UTC
Using --linkskip is not going to be a viable option for us, as it would increase our application size by 50%, and subsequently push us over the 3G download size cap.

My larger concern is that MEF (which is now part of the .NET 4.0 runtime) relies on [DefaultValue] being available at runtime.  Regardless of its usage at design time, it is not expected behavior for *any* attribute to disappear at runtime - with the possible exception of linker specific attributes such as [Preserve].  This could potentially affect many other reflection-based frameworks as well; so long as they rely on any of the attributes that get removed by the linker (something which is not yet easily deduced), they will be off-limits for MonoTouch.
Comment 3 Sebastien Pouliot 2012-03-13 08:42:57 UTC
one step at the time :)

- "immediate workaround" means what you can do, today, using the released (stable) releases of MonoTouch;

- [DefaultValue] won't be removed in future releases (but not the ones already being built/tested);

- All the linker gains are made by twisting the "expected behaviour" and it works fairly well, even if it can't be perfect wrt reflection-usage. To offset the "unexpected" we offers several ways to workaround issues (up to "don't link"). A new one is planned that should allow even more control from users (for cases such as this one).


master: 8ee7ac571edcb82b8b26cf0d21441e09e4b42abb
5.2-series: d0677892124705a2e27a05e6f6ee2d4fde0c7367
QA: unit tests added in the 'linkall' project