Bug 3902 - AOT bug with LINQ orderby on multiple value type fields with anon class from join - System.ExecutionEngineException - Attempting to JIT compile method
Summary: AOT bug with LINQ orderby on multiple value type fields with anon class from ...
Status: RESOLVED FIXED
Alias: None
Product: iOS
Classification: Xamarin
Component: Tools ()
Version: 5.2
Hardware: Macintosh Mac OS
: --- normal
Target Milestone: Untriaged
Assignee: Sebastien Pouliot
URL:
: 5561 ()
Depends on:
Blocks:
 
Reported: 2012-03-14 17:04 UTC by Felix Collins
Modified: 2012-06-07 11:12 UTC (History)
3 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 Felix Collins 2012-03-14 17:04:19 UTC
Judging by the number of hits on a web search for variations on this problem there are still a few things to improve with the AOT compilation of LINQ. 

In this example I am using Sqlite-net as the ORM. 

The exception...

Attempting to JIT compile method 'System.Linq.OrderedEnumerable`1<<>__AnonType0`2<BulletService.DataClasses.Question,BulletService.DataClasses.Section>>:CreateOrderedEnumerable<int> (System.Func`2<<>__AnonType0`2<BulletService.DataClasses.Question, BulletService.DataClasses.Section>,int>,System.Collections.Generic.IComparer`1<int>,bool)' while running with --aot-only.

This is the code that doesn't work...

var ret =
 from q in m_db.Table<Question>()
 where q.Deleted == false
 join s in m_db.Table<Section>() 
  on q.SectionId equals s.Id
 where s.BoardId == boardId
 orderby q.IsAnswered, s.Position, q.Position
 select q;

This is the workaround (this works) - converting the value types to strings to do the sorting

var ret =
 from q in m_db.Table<Question>()
 where q.Deleted == false
 join s in m_db.Table<Section>() 
  on q.SectionId equals s.Id
 where s.BoardId == boardId
 orderby q.IsAnswered.ToString(), s.Position.ToString(), q.Position.ToString()
 select q;
Comment 1 Sebastien Pouliot 2012-03-14 17:13:21 UTC
Similar issues where fixed recently. However without more details I cannot tell if your case is covered. Can you please attach a test case that will show us the definition of `Question` and `Section` ?
Comment 2 Felix Collins 2012-03-14 18:12:17 UTC
I don't have a separate test project. Does my work around give any clue?

The definitions of the classes involved are... (excuse the length as they are generated WCF web service data classes with partials to add the ORM attributes etc)

   public partial class Question : BulletService.DataClasses.BaseMobileData
    {
        
        private BulletService.DataClasses.AccessTypes AccessTypeIdField;
        
        private bool AllowMultiSubmissionField;
        
        private BulletService.DataClasses.AnswerTypes AnswerTypeIdField;
        
        private int AuthorIdField;
        
        private bool AutoAdvanceField;
        
        private string BodyField;
        
        private System.DateTime EndDateField;
        
        private bool HiddenWhenEndedField;
        
        private bool IsAnsweredField;
        
        private int PositionField;
        
        private BulletService.DataClasses.QuestionTypes QuestionTypeIdField;
        
        private int SectionIdField;
        
        private System.DateTime StartDateField;
        
        private string TitleField;
        
        [System.Runtime.Serialization.DataMemberAttribute()]
        public BulletService.DataClasses.AccessTypes AccessTypeId
        {
            get
            {
                return this.AccessTypeIdField;
            }
            set
            {
                this.AccessTypeIdField = value;
            }
        }
        
        [System.Runtime.Serialization.DataMemberAttribute()]
        public bool AllowMultiSubmission
        {
            get
            {
                return this.AllowMultiSubmissionField;
            }
            set
            {
                this.AllowMultiSubmissionField = value;
            }
        }
        
        [System.Runtime.Serialization.DataMemberAttribute()]
        public BulletService.DataClasses.AnswerTypes AnswerTypeId
        {
            get
            {
                return this.AnswerTypeIdField;
            }
            set
            {
                this.AnswerTypeIdField = value;
            }
        }
        
        [System.Runtime.Serialization.DataMemberAttribute()]
        public int AuthorId
        {
            get
            {
                return this.AuthorIdField;
            }
            set
            {
                this.AuthorIdField = value;
            }
        }
        
        [System.Runtime.Serialization.DataMemberAttribute()]
        public bool AutoAdvance
        {
            get
            {
                return this.AutoAdvanceField;
            }
            set
            {
                this.AutoAdvanceField = value;
            }
        }
        
        [System.Runtime.Serialization.DataMemberAttribute()]
        public string Body
        {
            get
            {
                return this.BodyField;
            }
            set
            {
                this.BodyField = value;
            }
        }
        
        [System.Runtime.Serialization.DataMemberAttribute()]
        public System.DateTime EndDate
        {
            get
            {
                return this.EndDateField;
            }
            set
            {
                this.EndDateField = value;
            }
        }
        
        [System.Runtime.Serialization.DataMemberAttribute()]
        public bool HiddenWhenEnded
        {
            get
            {
                return this.HiddenWhenEndedField;
            }
            set
            {
                this.HiddenWhenEndedField = value;
            }
        }
        
        [System.Runtime.Serialization.DataMemberAttribute()]
        public bool IsAnswered
        {
            get
            {
                return this.IsAnsweredField;
            }
            set
            {
                this.IsAnsweredField = value;
            }
        }
        
        [System.Runtime.Serialization.DataMemberAttribute()]
        public int Position
        {
            get
            {
                return this.PositionField;
            }
            set
            {
                this.PositionField = value;
            }
        }
        
        [System.Runtime.Serialization.DataMemberAttribute()]
        public BulletService.DataClasses.QuestionTypes QuestionTypeId
        {
            get
            {
                return this.QuestionTypeIdField;
            }
            set
            {
                this.QuestionTypeIdField = value;
            }
        }
        
        [System.Runtime.Serialization.DataMemberAttribute()]
        public int SectionId
        {
            get
            {
                return this.SectionIdField;
            }
            set
            {
                this.SectionIdField = value;
            }
        }
        
        [System.Runtime.Serialization.DataMemberAttribute()]
        public System.DateTime StartDate
        {
            get
            {
                return this.StartDateField;
            }
            set
            {
                this.StartDateField = value;
            }
        }
        
        [System.Runtime.Serialization.DataMemberAttribute()]
        public string Title
        {
            get
            {
                return this.TitleField;
            }
            set
            {
                this.TitleField = value;
            }
        }
    }

	/// <summary>Data class for the Question</summary>
	[PrimaryKeyNames("Id")]
	public partial class Question
	{
		public Question()
		{
			Id                   = -1;
			Deleted              = false;
			AccessTypeId         = AccessTypes.Open;
			AllowMultiSubmission = false;
			AnswerTypeId         = AnswerTypes.Text;
			AuthorId             = -1;
			AutoAdvance          = false;
			Body                 = "";
			StartDate            = AppConstants.MinTime;
			EndDate              = AppConstants.MaxTime;
			HiddenWhenEnded      = false;
			LastUpdatedMessages  = DomainObjectService.NeverUpdatedDate;
			Position             = 0;
			QuestionTypeId       = QuestionTypes.Text;
			SectionId            = -1;
			Title                = "unknown";
			IsAnswered           = false;
		}
		
		// The last time Messages were updated for this question
		public DateTime LastUpdatedMessages { get; set; }
		public const string LastUpdatedMessagesName = "LastUpdatedMessages";
		
		
	}

	/// <summary>Data class for the Section</summary>
	[PrimaryKeyNames("Id")]
	public partial class Section
	{
		public Section()
		{
			Id              = -1;
			Deleted         = false;
			BoardId = -1;
			IsSequential    = true;
			Position        = 0;
			SectionHeader   = "";
			Title           = "unknown";
		}
	}


    public partial class Section : BulletService.DataClasses.BaseMobileData
    {
        
        private int BoardIdField;
        
        private bool IsSequentialField;
        
        private int PositionField;
        
        private string SectionHeaderField;
        
        private string TitleField;
        
        [System.Runtime.Serialization.DataMemberAttribute()]
        public int BoardId
        {
            get
            {
                return this.BoardIdField;
            }
            set
            {
                this.BoardIdField = value;
            }
        }
        
        [System.Runtime.Serialization.DataMemberAttribute()]
        public bool IsSequential
        {
            get
            {
                return this.IsSequentialField;
            }
            set
            {
                this.IsSequentialField = value;
            }
        }
        
        [System.Runtime.Serialization.DataMemberAttribute()]
        public int Position
        {
            get
            {
                return this.PositionField;
            }
            set
            {
                this.PositionField = value;
            }
        }
        
        [System.Runtime.Serialization.DataMemberAttribute()]
        public string SectionHeader
        {
            get
            {
                return this.SectionHeaderField;
            }
            set
            {
                this.SectionHeaderField = value;
            }
        }
        
        [System.Runtime.Serialization.DataMemberAttribute()]
        public string Title
        {
            get
            {
                return this.TitleField;
            }
            set
            {
                this.TitleField = value;
            }
        }
    }
Comment 3 Sebastien Pouliot 2012-03-14 20:22:28 UTC
Thanks. I'll see if I can duplicate the issue with 5.2 using this and, if I can, look if 5.3.x already fixed this.
Comment 4 Felix Collins 2012-03-14 21:34:42 UTC
Actually I'm not sure that the workaround works. If I comment out the orderby I get a crash later on (probably a different bug really). If I ToString() the join properties. Then it works.

ie: 
join s in m_db.Table<Section>() 
  on q.SectionId.ToString() equals s.Id.ToString()

at System.Linq.Enumerable.ToLookup[Section,Int32,Section] (IEnumerable`1 source, System.Func`2 keySelector, System.Func`2 elementSelector, IEqualityComparer`1 comparer) [0x00079] in /Developer/MonoTouch/Source/mono/mcs/class/System.Core/System.Linq/Enumerable.cs:2915 
  at System.Linq.Enumerable.ToLookup[Section,Int32] (IEnumerable`1 source, System.Func`2 keySelector, IEqualityComparer`1 comparer) [0x00000] in /Developer/MonoTouch/Source/mono/mcs/class/System.Core/System.Linq/Enumerable.cs:2883 
  at System.Linq.Enumerable+<CreateJoinIterator>c__Iterator18`4[BulletService.DataClasses.Question,BulletService.DataClasses.Section,System.Int32,<>__AnonType0`2[BulletService.DataClasses.Question,BulletService.DataClasses.Section]].MoveNext () [0x00023] in /Developer/MonoTouch/Source/mono/mcs/class/System.Core/System.Linq/Enumerable.cs:1153 
  at System.Linq.Enumerable+<CreateWhereIterator>c__Iterator2B`1[<>__AnonType0`2[BulletService.DataClasses.Question,BulletService.DataClasses.Section]].MoveNext () [0x00000] in <filename unknown>:0 
  at System.Linq.Enumerable+<CreateSelectIterator>c__Iterator1D`2[<>__AnonType0`2[BulletService.DataClasses.Question,BulletService.DataClasses.Section],BulletService.DataClasses.Question].MoveNext () [0x00000] in <filename unknown>:0 
  at System.Collections.Generic.List`1[BulletService.DataClasses.Question].AddEnumerable (IEnumerable`1 enumerable) [0x00000] in <filename unknown>:0 
  at System.Collections.Generic.List`1[BulletService.DataClasses.Question].AddRange (IEnumerable`1 collection) [0x00000] in <filename unknown>:0 
  at Bullet.BoardModel.EndUpdateInternal[Question] () [0x00000] in <filename unknown>:0 
  at Bullet.ModelBase.HandleUpdates[Question] (System.Object sender, Bullet.DomainObjectsUpdateEventArgs`1 args) [0x00000] in <filename unknown>:0 
  at Bullet.DomainObjectService+<GetItemsImplementation>c__AnonStorey7`2+<GetItemsImplementation>c__AnonStorey9`2[BulletService.DataClasses.Question,System.Collections.Generic.List`1[System.String]].<>m__36 () [0x00000] in <filename unknown>:0 
  at Intranel..Touch.NavigatingTouchAppBase`1+<RunOnMainThread>c__AnonStorey0[Bullet.GlobalState].<>m__0 () [0x00000] in <filename unknown>:0 
  at MonoTouch.Foundation.NSActionDispatcher.Apply () [0x00000] in /Developer/MonoTouch/Source/monotouch/src/shared/Foundation/NSAction.cs:45 
  at MonoTouch.UIKit.UIApplication.Main (System.String[] args, System.String principalClassName, System.String delegateClassName) [0x00042] in /Developer/MonoTouch/Source/monotouch/src/UIKit/UIApplication.cs:29 
  at Touch.Container.Application.Main (System.String[] args) [0x00000] in /Users/felix/VLM/source/Bullet/Touch.Container/Main.cs:19
Comment 5 Felix Collins 2012-03-14 22:52:42 UTC
In further developments…

This works:
	List<Tuple<Question, Section>> tpl =
	new List<Tuple<Question, Section>>
	(
 	from q in m_db.Table<Question>() where q.Deleted == false
	from s in m_db.Table<Section>()
	where q.SectionId == s.Id && s.BulletinBoardId == boardId
	select new Tuple<Question, Section>(q,s)
	);

	tpl.Sort((t1,t2)=>
	        {
		if     (t1.Item1.IsAnswered != t2.Item1.IsAnswered) return t1.Item1.IsAnswered.CompareTo(t2.Item1.IsAnswered);
		else if(t1.Item2.Position != t2.Item2.Position) return t1.Item2.Position.CompareTo(t2.Item2.Position);
		else return t1.Item1.Position.CompareTo(t2.Item1.Position);
	});

	return tpl.Select<Tuple<Question, Section>, Question>((t)=>t.Item1);
			
This does not:
	return
	from q in m_db.Table<Question>() where q.Deleted == false
	from s in m_db.Table<Section>()
	where q.SectionId == s.Id && s.BulletinBoardId == boardId
	orderby q.IsAnswered, s.Position, q.Position
	select q;

This does:
	return
	from q in m_db.Table<Question>() where q.Deleted == false
	from s in m_db.Table<Section>()
	where q.SectionId == s.Id && s.BulletinBoardId == boardId
	select q;
Comment 6 Sebastien Pouliot 2012-03-15 09:23:25 UTC
The `BulletinBoardId` field from comment #5 is not part of your earlier definition for `Section`. I'm assuming an `int` (like others).

The stack traces are also important when you report such issues since it's the only we can be sure we're looking at the same thing (since we're not using your own complete app).
Comment 7 Sebastien Pouliot 2012-03-15 11:30:22 UTC
The test case from comment #5 (using int) is identical to the one from the original description.

I added the original test case (orderby) and the comment #4 (join) to our test suite. They both fail in 5.2.x (stable) but works on 5.3.x (alpha).

Do not hesitate to fill other bug reports in you find them (so we can confirm them) or to try the next 5.3 alpha (the current/first one does not have all the fixes).
Comment 8 Felix Collins 2012-03-15 17:34:14 UTC
Thanks for that.  It is great to have such responsive people on the end of a bug report. I've learned something about the AOT! In the end I've worked around the problem but we'll revert to the tidier linq code when we get the next version. Should we be using the alpha version given that we are in early days of our development anyway?
Comment 9 Sebastien Pouliot 2012-03-16 08:49:35 UTC
Thanks :-)

> Should we be using the alpha version given that we are in early days of our development anyway?

I would say yes for a 'beta' since the code is branched and only fixes goes in, so beta X+1 should be better than X... until release.

But an 'alpha' quality could go downward from X to X+1, e.g. when a new feature is enabled. So it's best used as a preview, for testing (or for developing with a good fallback plan).

It's possible (if not painless) to install multiple MonoTouch versions by renaming the /Developer/MonoTouch installation directory. So you can try things on the alpha (e.g. your LINQ queries) and keep developing on the stable release.

#if ALPHA
#else
#endif

where you define ALPHA symbol when using the alpha (i.e. it's not predefined).
Comment 10 Sebastien Pouliot 2012-06-07 11:12:38 UTC
*** Bug 5561 has been marked as a duplicate of this bug. ***