Skip to content
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

Different results with Reflection GetFields call on an Int32 TypeInfo on iOS 14.5 and iOS 14.7.1 #12508

Closed
winstonpang opened this issue Aug 23, 2021 · 4 comments
Labels
iOS Issues affecting Xamarin.iOS support The issue is related to support
Milestone

Comments

@winstonpang
Copy link

winstonpang commented Aug 23, 2021

Steps to Reproduce

  1. Create a single view Xamarin iOS App
  2. In the View Controller add a Button and wire up the TouchUpInside event.
  3. Place the following code in the event handler and set a breakpoint at the end of method's curly brace.
var testInt = 123; 
var testIntTypeInfo = testInt.GetType(); 
var testIntFields = testIntTypeInfo.GetFields();

Expected Behavior

iPhone Simulator running iOS 14.5:

  • testIntFields should yield 2 FieldInfo objects -- MinValue and MaxValue

iPhone Device running iOS 14.7.1:

  • testIntFields should yield 2 FieldInfo objects -- MinValue and MaxValue

Actual Behavior

iPhone Simulator running iOS 14.5:

  • 2 Field Info's returned -- MinValue and MaxValue.

iPhone Device running iOS 14.7.1:

  • 0 Field Info's returned.

Environment

Test done on Visual Studio for Mac - 8.10.7 (build 17).

Xamarin.iOS SDK Version - 14.20.0.25

Build Logs

VS Build Log.txt

How this issue was discovered

We use a 3rd party package called iCal.NET, the git hub repo is located here:

https://github.com/rianjs/ical.net

Recently we've had some clients complain about not seeing some calendar events that we use iCal.NET to generate by supplying the RRULE pattern to their classes.

We observed that there was conflicting behaviours and after a lot of elimination noticed it was iOS Specific.

Someone already noticed this issue but never managed to find an answer as to what causes it:

rianjs/ical.net#471

We checked out the code and stepped through the library and this was basically the method in their code which caused the glitch:

https://github.com/rianjs/ical.net/blob/6c03c42bd9e040622ffaa240be856dc531a20823/src/Ical.Net/Serialization/DataTypes/RecurrencePatternSerializer.cs

public virtual void CheckMutuallyExclusive<T, TU>(string name1, string name2, T obj1, TU obj2)
        {
            if (Equals(obj1, default(T)) || Equals(obj2, default(TU)))
            {
                return;
            }
            // If the object is MinValue instead of its default, consider
            // that to be unassigned.

            var t1 = obj1.GetType();

            var fi1 = t1.GetField("MinValue");
            var fi2 = t1.GetField("MinValue");

            var isMin1 = fi1 != null && obj1.Equals(fi1.GetValue(null));
            var isMin2 = fi2 != null && obj2.Equals(fi2.GetValue(null));
            if (isMin1 || isMin2)
            {
                return;
            }

            throw new ArgumentException("Both " + name1 + " and " + name2 + " cannot be supplied together; they are mutually exclusive.");
        }
@spouliot spouliot added support The issue is related to support iOS Issues affecting Xamarin.iOS labels Aug 23, 2021
@spouliot spouliot added this to the Future milestone Aug 23, 2021
@spouliot
Copy link
Contributor

iPhone Simulator running iOS 14.5:

By default the simulator builds runs with the linker disabled. This makes the builds faster.

iPhone Device running iOS 14.7.1:

By default the device builds with the linker enabled. This also makes the build faster (since AOT is required).

The linker remove unused code using static analysis. Dynamic usage, like reflection, is not detected.

In such cases the code (ideally the library itself if it supports any of the Xamarin profiles) should preserve the extra members. There are several ways to do this (extra code, xml files...).

E.g. somewhere in your app add

if (int.MinValue == int.MaxValue)
   Console.WriteLine ("uho");

This will make the symbols visible to the linker and they will be preserved inside the linked application.

@winstonpang
Copy link
Author

@spouliot I added the snippet so that it will call int.MinValue and int.MaxValue in the ViewDidLoad, made sure it hit the line of code as well.

However GetFields still returns an empty result. Calling GetField("MinValue") returns null as well.

@spouliot
Copy link
Contributor

oh, yeah, sorry :| I forgot those were const [1] and that the C# compiler inlines the value, not a reference to the field (that the linker would see).

You'll need an XML file to your project to preserve the fields.

Add a LinkerHelper.xml to your project. Change the new file's "Build Action" to "LinkDescription". Copy/paste the following into the new file.

<linker>
    <assembly fullname="mscorlib">
        <type fullname="System.Int32">
            <field name="MinValue" />
            <field name="MaxValue" />
        </type>
    </assembly>
</linker>

[1] https://docs.microsoft.com/en-us/dotnet/api/system.int32.minvalue?view=net-5.0

@winstonpang
Copy link
Author

@spouliot thanks for the speedy response. That did the trick.

Although, it certainly isn't obvious, are there plans to improve on this or make the tooling better in some way?

Thank you again for your help.

@ghost ghost locked as resolved and limited conversation to collaborators Apr 27, 2022
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
iOS Issues affecting Xamarin.iOS support The issue is related to support
Projects
None yet
Development

No branches or pull requests

2 participants