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

[Bug]: Possible issue with Fody IL #3100

Closed
J-Swift opened this issue Nov 17, 2022 · 7 comments
Closed

[Bug]: Possible issue with Fody IL #3100

J-Swift opened this issue Nov 17, 2022 · 7 comments
Assignees
Labels
O-Community Waiting-For-Reporter Waiting for more information from the reporter before we can proceed

Comments

@J-Swift
Copy link

J-Swift commented Nov 17, 2022

What happened?

Getting a crash in release build

Attempting to JIT compile method '(wrapper delegate-invoke) TeamApp.Realm.TeamId :invoke_callvirt_TeamId_Team (TeamApp.Realm.Team)' while running in aot-only mode

I got this once previously when using ReactiveUI's Fody weaver attributes, and was able to resolve by removing them. I started down the path of verifying if moving to SG would fix it here, but the current SG limitations make it hard to move over (e.g. #3085).

Repro steps

Similar to my issues with ReactiveUI, this came about pretty randomly after having done several release builds with Realm. The only thing I can think of that I added as an EmbeddedObject for the first time. Seemingly random, however.

Version

10.18.0

What SDK flavour are you using?

Local Database only

What type of application is this?

Other

Client OS and version

iOS 15.6.1

Code snippets

No response

Stacktrace of the exception/crash you're getting

This is somewhat obfuscated by being the poorly symbolicated stacktrace in Crashlytics:

> Fatal Exception: System.ExecutionEngineException
Attempting to JIT compile method '(wrapper delegate-invoke) TeamApp.Realm.TeamId <Module>:invoke_callvirt_TeamId_Team (TeamApp.Realm.Team)' while running in aot-only mode. See https://docs.microsoft.com/xamarin/ios/internals/limitations for more information. (System.ExecutionEngineException) at System.Linq.Expressions.Interpreter.FuncCallInstruction`2[[TeamApp.Realm.Team, TeamApp, Version=0.1.0.0, Culture=neutral, PublicKeyToken=null],[TeamApp.Realm.TeamId, TeamApp, Version=0.1.0.0, Culture=neutral, PublicKeyToken=null]].Run(InterpretedFrame ) at System.Linq.Expressions.Interpreter.Interpreter.Run(InterpretedFrame ) at System.Linq.Expressions.Interpreter.LightLambda.Run(Object[] ) at System.Dynamic.Utils.DelegateHelpers.FuncThunk1[EventGame,Boolean](Func`2 handler, EventGame t1) at System.Linq.Enumerable.WhereListIterator`1[[TeamApp.Realm.EventGame, TeamApp, Version=0.1.0.0, Culture=neutral, PublicKeyToken=null]].MoveNext() at System.Collections.Generic.EnumerableHelpers.ToArray[EventGame](IEnumerable`1 , Int32& ) at System.Linq.Buffer`1[[TeamApp.Realm.EventGame, TeamApp, Version=0.1.0.0, Culture=neutral, PublicKeyToken=null]]..ctor(IEnumerable`1 ) at System.Linq.OrderedEnumerable`1.<GetEnumerator>d__4[[TeamApp.Realm.EventGame, TeamApp, Version=0.1.0.0, Culture=neutral, PublicKeyToken=null]].MoveNext() at System.Collections.Generic.List`1[[TeamApp.Realm.EventGame, TeamApp, Version=0.1.0.0, Culture=neutral, PublicKeyToken=null]]..ctor(IEnumerable`1 ) at System.Linq.Enumerable.ToList[EventGame](IEnumerable`1 source) at TeamApp.GameCenterViewModel..ctor(IEventsRepository eventsRepository, IAppStateService appStateService, EventId eventId, TeamId teamId) at TeamApp.GameCenterView.LoadView()

Relevant log output

No response

@nirinchev
Copy link
Member

nirinchev commented Nov 17, 2022

Can you share your models - particularly the TeamId and Team ones. Also, the ctor of GameCenterViewModel seems to be calling *something*.ToList() - can you share the code around that as well? I've also found a similar issue being reported here: dotnet/runtime#69410 - the proposed workaround there is to add <UseInterpreter>true</UseInterpreter> to your csproj - not sure if that'll help, but may be worth a try.

@J-Swift
Copy link
Author

J-Swift commented Nov 18, 2022

I'll try, this is part of my prod app so lots of stuff going on:

Models:

public abstract record StrongId(long Value);
public record TeamId(long Value) : StrongId(Value);

public interface StrongIdable<T> where T : StrongId
{
    public T StrongId { get; }
}

public class Team : RealmObject, StrongIdable<TeamId>
{
    [PrimaryKey]
    public long Id { get; set; }
    public TeamId StrongId => new(Id);

    [Required]
    public string Name { get; set; }
    public string? Location { get; set; }
    public string? ImgUrl { get; set; }

    public IList<Player> Players { get; }
    public IList<Event> Events { get; }
}

Some GameCenterViewModel code:

public GameCenterViewModel(IEventsRepository eventsRepository, IAppStateService appStateService, EventId eventId, TeamId teamId)
    {
        _teamId = teamId;
        _eventsRepository = eventsRepository;

        _rosterEntries = _eventsRepository.GetRosterEntriesFor(teamId, eventId)
            .OrderBy(it => it.Player.Name)
            .AsRealmCollection();

        _games = _eventsRepository.GetGamesFor(eventId)
            .Where(game => game.Team1.StrongId == teamId || game.Team2.StrongId == teamId)
            .OrderBy(it => it.GameStartDate)
            .ThenBy(it => it.GameStartTime)
            .ToList();
        var idx = 0;
        ScheduledGames = new LoadedMVCModel<IList<TeamId>>(_games.Select(it => new TeamId(idx++)).ToList());

        _rosterEntries
            .SubscribeForNotifications((sender, changes, error) =>
            {
                if (error != null)
                {
                    Roster = new ErrorMVCModel<IList<PlayerId>>(error);
                    return;
                }
                Roster = new LoadedMVCModel<IList<PlayerId>>(sender.Select(it => it.Player.StrongId).ToList());
            })
            .DisposeWith(Subscriptions);
    }

I came across the UseInterpreter suggestion when trying to fix the mentioned ReactiveUI issue and it had no effect. I'll have to see if it does anything for this scenario.

@J-Swift
Copy link
Author

J-Swift commented Nov 18, 2022

Initial test seems like UseInterpreter could work here. I'll assume this is fixed for now (and am following that other issue now), but this is the 2nd time I've run into something with Fody and new .net versions / Maui. I'm guessing its going to be more prevalent as people migrate over. The new SG is really promising, glad to see that option being provided for the future!

@nirinchev
Copy link
Member

Why do you think this is a Fody issue though? As far as I can tell from the stacktrace, it's something related to TeamId, which is a computed property that is not processed by the Realm.Fody weaver. It's possible I'm not seeing something, but it looks like it's caused by the LINQ expression that compares the team ids of Team1 and Team2 - it does look perfectly legal, but also not something that directly involves Realm. It's possible it's due to the way Team.StrongId is constructed from the Team.Id property, but that's not obvious at least from the stacktrace.

@J-Swift
Copy link
Author

J-Swift commented Nov 18, 2022

Well nothing actually : D

I just know it (likely*) ended up being Fody in my other case where I ran into this, and saw similar stack traces in that scenario as this one. This was the runtime error for that one:

2022-10-11 12:43:44.033 TeamApp[62223:13071381] *** Terminating app due to uncaught exception 'System.ExecutionEngineException', reason: 'Attempting to JIT compile method 'void TeamApp.EventViewModel:.ctor (TeamApp.Realm.Event)' while running in aot-only mode. See https://docs.microsoft.com/xamarin/ios/internals/limitations for more information.

The Microsoft team indicated this is a result of reflection stuff, and I guess I assumed Fody was my only exposure to that sort of code currently (https://discord.com/channels/732297728826277939/732297808148824115/1029472609303269386). It also was the same "everything has been working fine, then I shuffled some code around slightly and it now crashes at runtime" behavior. I also assume if not many have run into it, its the same other circumstances of not many people moved to Maui / net6.0 yet, and that having different AOT rules/defaults.

But yeah the StrongId stuff has been in place for ~3 months now, but only just got this crash this week. The models have been mostly static as well. I have many other instances of StrongId models, and those all use similar access patterns.

@nirinchev
Copy link
Member

Yeah, the thing is Fody doesn't really do any reflection. At least what our weaver does right now is to replace the property implementation which looks like:

class Foo : RealmObject
{
    private string <backing_field_bar>;

    public string Bar
    {
        get => <backing_field_bar>;
        set => <backing_field_bar> = value;
    }
}

to make it look like:

class Foo : RealmObject
{
    private string <backing_field_bar>;

    public string Bar
    {
        get
        {
             if (IsManaged)
             {
                 return base.GetValue<string>("Bar");
             }
             return <backing_field_bar>;
        }
        set
        {
            if (IsManaged)
            {
                base.SetValue("Bar", value);
            }
            else
            {
                <backing_field_bar> = value;
            }
        }
    }
}

So not doing any reflection, just making sure for managed objects we read/write data via the base class (which goes through the database) rather than via the backing field. It's certainly possible that there's some weird interaction which breaks the compiler somehow, but I'd be more inclined to attribute that to a bug with the compiler than the code generated by Fody. That being said, we recently released support for source generators, which almost entirely eliminates the need to synthesize IL at compile time, so might be worth giving it a shot.

@sync-by-unito sync-by-unito bot added the Waiting-For-Reporter Waiting for more information from the reporter before we can proceed label Nov 22, 2022
@nirinchev
Copy link
Member

Closing this as the workaround seems to be sufficient. Feel free to reopen if you want us to investigate deeper.

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
O-Community Waiting-For-Reporter Waiting for more information from the reporter before we can proceed
Projects
None yet
Development

No branches or pull requests

2 participants