private void Sequence(QilList ndSeq) {
LocalBuilder locIdx, locList;
Label lblStart, lblNext, lblOnEnd = new Label();
Label[] arrSwitchLabels;
int i;
Type itemStorageType = GetItemStorageType(ndSeq);
Debug.Assert(XmlILConstructInfo.Read(ndSeq).ConstructMethod == XmlILConstructMethod.Iterator, "This method should only be called if items in list are pulled from a code iterator.");
// Singleton list is a special case if in addition to the singleton there are warnings or errors which should be executed
if (ndSeq.XmlType.IsSingleton) {
foreach (QilNode nd in ndSeq) {
// Generate nested iterator's code
if (nd.XmlType.IsSingleton) {
NestedVisitEnsureStack(nd);
}
else {
lblOnEnd = this.helper.DefineLabel();
NestedVisit(nd, lblOnEnd);
this.iterCurr.DiscardStack();
this.helper.MarkLabel(lblOnEnd);
}
}
this.iterCurr.Storage = StorageDescriptor.Stack(itemStorageType, false);
}
else {
// Type itemList;
// int idxList;
locList = this.helper.DeclareLocal("$$$itemList", itemStorageType);
locIdx = this.helper.DeclareLocal("$$$idxList", typeof(int));
arrSwitchLabels = new Label[ndSeq.Count];
lblStart = this.helper.DefineLabel();
for (i = 0; i < ndSeq.Count; i++) {
// LabelOnEnd[i - 1]:
// When previous nested iterator is exhausted, it should jump to this (the next) iterator
if (i != 0)
this.helper.MarkLabel(lblOnEnd);
// Create new LabelOnEnd for all but the last iterator, which jumps back to parent iterator when exhausted
if (i == ndSeq.Count - 1)
lblOnEnd = this.iterCurr.GetLabelNext();
else
lblOnEnd = this.helper.DefineLabel();
// idxList = [i];
this.helper.LoadInteger(i);
this.helper.Emit(OpCodes.Stloc, locIdx);
// Generate nested iterator's code
NestedVisit(ndSeq[i], lblOnEnd);
// Result of list should be saved to a common type and location
this.iterCurr.EnsureItemStorageType(ndSeq[i].XmlType, itemStorageType);
this.iterCurr.EnsureLocalNoCache(locList);
// Switch statement will jump to nested iterator's LabelNext
arrSwitchLabels[i] = this.iterNested.GetLabelNext();
// IL's rules prevent OpCodes.Br here
// goto LabelStart;
this.helper.EmitUnconditionalBranch(OpCodes.Brtrue, lblStart);
}
// LabelNext:
lblNext = this.helper.DefineLabel();
this.helper.MarkLabel(lblNext);
// switch (idxList)
// case 0: goto LabelNext1;
// ...
// case N-1: goto LabelNext[N];
this.helper.Emit(OpCodes.Ldloc, locIdx);
this.helper.Emit(OpCodes.Switch, arrSwitchLabels);
// LabelStart:
this.helper.MarkLabel(lblStart);
this.iterCurr.SetIterator(lblNext, StorageDescriptor.Local(locList, itemStorageType, false));
}
}