private void NestedVisitEnsureCache(QilNode nd, Type itemStorageType) {
Debug.Assert(!XmlILConstructInfo.Read(nd).PushToWriterLast);
bool cachesResult = CachesResult(nd);
LocalBuilder locCache;
Label lblOnEnd = this.helper.DefineLabel();
Type cacheType;
XmlILStorageMethods methods;
// If bound expression will already be cached correctly, then don't create an XmlQuerySequence
if (cachesResult) {
StartNestedIterator(nd);
Visit(nd);
EndNestedIterator(nd);
this.iterCurr.Storage = this.iterNested.Storage;
Debug.Assert(this.iterCurr.Storage.IsCached, "Expression result should be cached. CachesResult() might have a bug in it.");
// If type of items in the cache matches "itemStorageType", then done
if (this.iterCurr.Storage.ItemStorageType == itemStorageType)
return;
// If the cache has navigators in it, or if converting to a cache of navigators, then EnsureItemStorageType
// can directly convert without needing to create a new cache.
if (this.iterCurr.Storage.ItemStorageType == typeof(XPathNavigator) || itemStorageType == typeof(XPathNavigator)) {
this.iterCurr.EnsureItemStorageType(nd.XmlType, itemStorageType);
return;
}
this.iterCurr.EnsureNoStack("$$$cacheResult");
}
// Always store navigators in XmlQueryNodeSequence (which implements IList<XPathItem>)
cacheType = (GetItemStorageType(nd) == typeof(XPathNavigator)) ? typeof(XPathNavigator) : itemStorageType;
// XmlQuerySequence<T> cache;
methods = XmlILMethods.StorageMethods[cacheType];
locCache = this.helper.DeclareLocal("$$$cache", methods.SeqType);
this.helper.Emit(OpCodes.Ldloc, locCache);
// Special case non-navigator singletons to use overload of CreateOrReuse
if (nd.XmlType.IsSingleton) {
// cache = XmlQuerySequence.CreateOrReuse(cache, item);
NestedVisitEnsureStack(nd, cacheType, false);
this.helper.CallToken(methods.SeqReuseSgl);
this.helper.Emit(OpCodes.Stloc, locCache);
}
else {
// XmlQuerySequence<T> cache;
// cache = XmlQuerySequence.CreateOrReuse(cache);
this.helper.CallToken(methods.SeqReuse);
this.helper.Emit(OpCodes.Stloc, locCache);
this.helper.Emit(OpCodes.Ldloc, locCache);
StartNestedIterator(nd, lblOnEnd);
if (cachesResult)
this.iterCurr.Storage = this.iterCurr.ParentIterator.Storage;
else
Visit(nd);
// cache.Add(item);
this.iterCurr.EnsureItemStorageType(nd.XmlType, cacheType);
this.iterCurr.EnsureStackNoCache();
this.helper.Call(methods.SeqAdd);
this.helper.Emit(OpCodes.Ldloc, locCache);
// }
this.iterCurr.LoopToEnd(lblOnEnd);
EndNestedIterator(nd);
// Remove cache reference from stack
this.helper.Emit(OpCodes.Pop);
}
this.iterCurr.Storage = StorageDescriptor.Local(locCache, itemStorageType, true);
}