XmlIlVisitor.cs source code in C# .NET

Source code for the .NET framework in C#

                        

Code:

/ 4.0 / 4.0 / untmp / DEVDIV_TFS / Dev10 / Releases / RTMRel / ndp / fx / src / XmlUtils / System / Xml / Xsl / IlGen / XmlIlVisitor.cs / 1305376 / XmlIlVisitor.cs

                            //------------------------------------------------------------------------------ 
// 
//     Copyright (c) Microsoft Corporation.  All rights reserved.
// 
// [....] 
//-----------------------------------------------------------------------------
using System; 
using System.Xml; 
using System.Xml.XPath;
using System.Xml.Schema; 
using System.Globalization;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics; 
using System.Reflection;
using System.Reflection.Emit; 
using System.Xml.Xsl; 
using System.Xml.Xsl.Qil;
using System.Xml.Xsl.Runtime; 

namespace System.Xml.Xsl.IlGen {
    using TypeFactory   = System.Xml.Xsl.XmlQueryTypeFactory;
    using Res           = System.Xml.Utils.Res; 

    ///  
    /// Creates Msil code for an entire QilExpression graph.  Code is generated in one of two modes: push or 
    /// pull.  In push mode, code is generated to push the values in an iterator to the XmlWriter
    /// interface.  In pull mode, the values in an iterator are stored in a physical location such as 
    /// the stack or a local variable by an iterator.  The iterator is passive, and will just wait for
    /// a caller to pull the data and/or instruct the iterator to enumerate the next value.
    /// 
    internal class XmlILVisitor : QilVisitor { 
        private QilExpression qil;
        private GenerateHelper helper; 
        private IteratorDescriptor iterCurr, iterNested; 
        private int indexId;
 

        //-----------------------------------------------
        // Entry
        //----------------------------------------------- 

        ///  
        /// Visits the specified QilExpression graph and generates MSIL code. 
        /// 
        public void Visit(QilExpression qil, GenerateHelper helper, MethodInfo methRoot) { 
            this.qil = qil;
            this.helper = helper;
            this.iterNested = null;
            this.indexId = 0; 

            // Prepare each global parameter and global variable to be visited 
            PrepareGlobalValues(qil.GlobalParameterList); 
            PrepareGlobalValues(qil.GlobalVariableList);
 
            // Visit each global parameter and global variable
            VisitGlobalValues(qil.GlobalParameterList);
            VisitGlobalValues(qil.GlobalVariableList);
 
            // Build each function
            foreach (QilFunction ndFunc in qil.FunctionList) { 
                // Visit each parameter and the function body 
                Function(ndFunc);
            } 

            // Build the root expression
            this.helper.MethodBegin(methRoot, null, true);
            StartNestedIterator(qil.Root); 
            Visit(qil.Root);
            Debug.Assert(this.iterCurr.Storage.Location == ItemLocation.None, "Root expression should have been pushed to the writer."); 
            EndNestedIterator(qil.Root); 
            this.helper.MethodEnd();
        } 

        /// 
        /// Create IteratorDescriptor for each global value.  This pre-visit is necessary because a global early
        /// in the list may reference a global later in the list and therefore expect its IteratorDescriptor to already 
        /// be initialized.
        ///  
        private void PrepareGlobalValues(QilList globalIterators) { 
            MethodInfo methGlobal;
            IteratorDescriptor iterInfo; 

            foreach (QilIterator iter in globalIterators) {
                Debug.Assert(iter.NodeType == QilNodeType.Let || iter.NodeType == QilNodeType.Parameter);
 
                // Get metadata for method which computes this global's value
                methGlobal = XmlILAnnotation.Write(iter).FunctionBinding; 
                Debug.Assert(methGlobal != null, "Metadata for global value should have already been computed"); 

                // Create an IteratorDescriptor for this global value 
                iterInfo = new IteratorDescriptor(this.helper);

                // Iterator items will be stored in a global location
                iterInfo.Storage = StorageDescriptor.Global(methGlobal, GetItemStorageType(iter), !iter.XmlType.IsSingleton); 

                // Associate IteratorDescriptor with parameter 
                XmlILAnnotation.Write(iter).CachedIteratorDescriptor = iterInfo; 
            }
        } 

        /// 
        /// Visit each global variable or parameter.  Create a IteratorDescriptor for each global value.  Generate code for
        /// default values. 
        /// 
        private void VisitGlobalValues(QilList globalIterators) { 
            MethodInfo methGlobal; 
            Label lblGetGlobal, lblComputeGlobal;
            bool isCached; 
            int idxValue;

            foreach (QilIterator iter in globalIterators) {
                QilParameter param = iter as QilParameter; 

                // Get MethodInfo for method that computes the value of this global 
                methGlobal = XmlILAnnotation.Write(iter).CachedIteratorDescriptor.Storage.GlobalLocation; 
                isCached = !iter.XmlType.IsSingleton;
 
                // Notify the StaticDataManager of the new global value
                idxValue = this.helper.StaticData.DeclareGlobalValue(iter.DebugName);

                // Generate code for this method 
                this.helper.MethodBegin(methGlobal, iter.SourceLine, false);
 
                lblGetGlobal = this.helper.DefineLabel(); 
                lblComputeGlobal = this.helper.DefineLabel();
 
                // if (runtime.IsGlobalComputed(idx)) goto LabelGetGlobal;
                this.helper.LoadQueryRuntime();
                this.helper.LoadInteger(idxValue);
                this.helper.Call(XmlILMethods.GlobalComputed); 
                this.helper.Emit(OpCodes.Brtrue, lblGetGlobal);
 
                // Compute value of global value 
                StartNestedIterator(iter);
 
                if (param != null) {
                    Debug.Assert(iter.XmlType == TypeFactory.ItemS, "IlGen currently only supports parameters of type item*.");

                    // param = runtime.ExternalContext.GetParameter(localName, namespaceUri); 
                    // if (param == null) goto LabelComputeGlobal;
                    LocalBuilder locParam = this.helper.DeclareLocal("$$$param", typeof(object)); 
                    this.helper.CallGetParameter(param.Name.LocalName, param.Name.NamespaceUri); 
                    this.helper.Emit(OpCodes.Stloc, locParam);
                    this.helper.Emit(OpCodes.Ldloc, locParam); 
                    this.helper.Emit(OpCodes.Brfalse, lblComputeGlobal);

                    // runtime.SetGlobalValue(idxValue, runtime.ChangeTypeXsltResult(idxType, value));
                    // Ensure that the storage type of the parameter corresponds to static type 
                    this.helper.LoadQueryRuntime();
                    this.helper.LoadInteger(idxValue); 
 
                    this.helper.LoadQueryRuntime();
                    this.helper.LoadInteger(this.helper.StaticData.DeclareXmlType(XmlQueryTypeFactory.ItemS)); 
                    this.helper.Emit(OpCodes.Ldloc, locParam);
                    this.helper.Call(XmlILMethods.ChangeTypeXsltResult);

                    this.helper.CallSetGlobalValue(typeof(object)); 

                    // goto LabelGetGlobal; 
                    this.helper.EmitUnconditionalBranch(OpCodes.Br, lblGetGlobal); 
                }
 
                // LabelComputeGlobal:
                this.helper.MarkLabel(lblComputeGlobal);

                if (iter.Binding != null) { 
                    // runtime.SetGlobalValue(idxValue, (object) value);
                    this.helper.LoadQueryRuntime(); 
                    this.helper.LoadInteger(idxValue); 

                    // Compute value of global value 
                    NestedVisitEnsureStack(iter.Binding, GetItemStorageType(iter), isCached);

                    this.helper.CallSetGlobalValue(GetStorageType(iter));
                } 
                else {
                    // Throw exception, as there is no default value for this parameter 
                    // XmlQueryRuntime.ThrowException("..."); 
                    Debug.Assert(iter.NodeType == QilNodeType.Parameter, "Only parameters may not have a default value");
                    this.helper.LoadQueryRuntime(); 
                    this.helper.Emit(OpCodes.Ldstr, Res.GetString(Res.XmlIl_UnknownParam, new string[] {param.Name.LocalName, param.Name.NamespaceUri}));
                    this.helper.Call(XmlILMethods.ThrowException);
                }
 
                EndNestedIterator(iter);
 
                // LabelGetGlobal: 
                // return (T) runtime.GetGlobalValue(idxValue);
                this.helper.MarkLabel(lblGetGlobal); 
                this.helper.CallGetGlobalValue(idxValue, GetStorageType(iter));

                this.helper.MethodEnd();
            } 
        }
 
        ///  
        /// Generate code for the specified function.
        ///  
        private void Function(QilFunction ndFunc) {
            MethodInfo methFunc;
            int paramId;
            IteratorDescriptor iterInfo; 
            bool useWriter;
 
            // Annotate each function parameter with a IteratorDescriptor 
            foreach (QilIterator iter in ndFunc.Arguments) {
                Debug.Assert(iter.NodeType == QilNodeType.Parameter); 

                // Create an IteratorDescriptor for this parameter
                iterInfo = new IteratorDescriptor(this.helper);
 
                // Add one to parameter index, as 0th parameter is always "this"
                paramId = XmlILAnnotation.Write(iter).ArgumentPosition + 1; 
 
                // The ParameterInfo for each argument should be set as its location
                iterInfo.Storage = StorageDescriptor.Parameter(paramId, GetItemStorageType(iter), !iter.XmlType.IsSingleton); 

                // Associate IteratorDescriptor with Let iterator
                XmlILAnnotation.Write(iter).CachedIteratorDescriptor = iterInfo;
            } 

            methFunc = XmlILAnnotation.Write(ndFunc).FunctionBinding; 
            useWriter = (XmlILConstructInfo.Read(ndFunc).ConstructMethod == XmlILConstructMethod.Writer); 

            // Generate query code from QilExpression tree 
            this.helper.MethodBegin(methFunc, ndFunc.SourceLine, useWriter);

            foreach (QilIterator iter in ndFunc.Arguments) {
                // DebugInfo: Sequence point just before generating code for the bound expression 
                if (this.qil.IsDebug && iter.SourceLine != null)
                    this.helper.DebugSequencePoint(iter.SourceLine); 
 
                // Calculate default value of this parameter
                if (iter.Binding != null) { 
                    Debug.Assert(iter.XmlType == TypeFactory.ItemS, "IlGen currently only supports default values in parameters of type item*.");
                    paramId = (iter.Annotation as XmlILAnnotation).ArgumentPosition + 1;

                    // runtime.MatchesXmlType(param, XmlTypeCode.QName); 
                    Label lblLocalComputed = this.helper.DefineLabel();
                    this.helper.LoadQueryRuntime(); 
                    this.helper.LoadParameter(paramId); 
                    this.helper.LoadInteger((int)XmlTypeCode.QName);
                    this.helper.Call(XmlILMethods.SeqMatchesCode); 

                    this.helper.Emit(OpCodes.Brfalse, lblLocalComputed);

                    // Compute default value of this parameter 
                    StartNestedIterator(iter);
                    NestedVisitEnsureStack(iter.Binding, GetItemStorageType(iter), /*isCached:*/!iter.XmlType.IsSingleton); 
                    EndNestedIterator(iter); 

                    this.helper.SetParameter(paramId); 
                    this.helper.MarkLabel(lblLocalComputed);
                }
            }
 
            StartNestedIterator(ndFunc);
 
            // If function did not push results to writer, then function will return value(s) (rather than void) 
            if (useWriter)
                NestedVisit(ndFunc.Definition); 
            else
                NestedVisitEnsureStack(ndFunc.Definition, GetItemStorageType(ndFunc), !ndFunc.XmlType.IsSingleton);

            EndNestedIterator(ndFunc); 

            this.helper.MethodEnd(); 
        } 

        //----------------------------------------------- 
        // QilVisitor
        //-----------------------------------------------

        ///  
        /// Generate a query plan for the QilExpression subgraph.
        ///  
        protected override QilNode Visit(QilNode nd) { 
            if (nd == null)
                return null; 

            // DebugInfo: Sequence point just before generating code for this expression
            if (this.qil.IsDebug && nd.SourceLine != null && !(nd is QilIterator))
                this.helper.DebugSequencePoint(nd.SourceLine); 

            // Expressions are constructed using one of several possible methods 
            switch (XmlILConstructInfo.Read(nd).ConstructMethod) { 
                case XmlILConstructMethod.WriterThenIterator:
                    // Push results of expression to cached writer; then iterate over cached results 
                    NestedConstruction(nd);
                    break;

                case XmlILConstructMethod.IteratorThenWriter: 
                    // Iterate over items in the sequence; send items to writer
                    CopySequence(nd); 
                    break; 

                case XmlILConstructMethod.Iterator: 
                    Debug.Assert(nd.XmlType.IsSingleton || CachesResult(nd) || this.iterCurr.HasLabelNext,
                                 "When generating code for a non-singleton expression, LabelNext must be defined.");
                    goto default;
 
                default:
                    // Allow base internal class to dispatch to correct Visit method 
                    base.Visit(nd); 
                    break;
            } 

            return nd;
        }
 
        /// 
        /// VisitChildren should never be called. 
        ///  
        protected override QilNode VisitChildren(QilNode parent) {
            Debug.Fail("Visit" + parent.NodeType + " should never be called"); 
            return parent;
        }

        ///  
        /// Generate code to cache a sequence of items that are pushed to output.
        ///  
        private void NestedConstruction(QilNode nd) { 
            // Start nested construction of a sequence of items
            this.helper.CallStartSequenceConstruction(); 

            // Allow base internal class to dispatch to correct Visit method
            base.Visit(nd);
 
            // Get the result sequence
            this.helper.CallEndSequenceConstruction(); 
            this.iterCurr.Storage = StorageDescriptor.Stack(typeof(XPathItem), true); 
        }
 
        /// 
        /// Iterate over items produced by the "nd" expression and copy each item to output.
        /// 
        private void CopySequence(QilNode nd) { 
            XmlQueryType typ = nd.XmlType;
            bool hasOnEnd; 
            Label lblOnEnd; 

            StartWriterLoop(nd, out hasOnEnd, out lblOnEnd); 

            if (typ.IsSingleton) {
                // Always write atomic values via XmlQueryOutput
                this.helper.LoadQueryOutput(); 

                // Allow base internal class to dispatch to correct Visit method 
                base.Visit(nd); 
                this.iterCurr.EnsureItemStorageType(nd.XmlType, typeof(XPathItem));
            } 
            else {
                // Allow base internal class to dispatch to correct Visit method
                base.Visit(nd);
                this.iterCurr.EnsureItemStorageType(nd.XmlType, typeof(XPathItem)); 

                // Save any stack values in a temporary local 
                this.iterCurr.EnsureNoStackNoCache("$$$copyTemp"); 

                this.helper.LoadQueryOutput(); 
            }

            // Write value to output
            this.iterCurr.EnsureStackNoCache(); 
            this.helper.Call(XmlILMethods.WriteItem);
 
            EndWriterLoop(nd, hasOnEnd, lblOnEnd); 
        }
 
        /// 
        /// Generate code for QilNodeType.DataSource.
        /// 
        ///  
        /// Generates code to retrieve a document using the XmlResolver.
        ///  
        protected override QilNode VisitDataSource(QilDataSource ndSrc) { 
            LocalBuilder locNav;
 
            // XPathNavigator navDoc = runtime.ExternalContext.GetEntity(uri)
            this.helper.LoadQueryContext();
            NestedVisitEnsureStack(ndSrc.Name);
            NestedVisitEnsureStack(ndSrc.BaseUri); 
            this.helper.Call(XmlILMethods.GetDataSource);
 
            locNav = this.helper.DeclareLocal("$$$navDoc", typeof(XPathNavigator)); 
            this.helper.Emit(OpCodes.Stloc, locNav);
 
            // if (navDoc == null) goto LabelNextCtxt;
            this.helper.Emit(OpCodes.Ldloc, locNav);
            this.helper.Emit(OpCodes.Brfalse, this.iterCurr.GetLabelNext());
 
            this.iterCurr.Storage = StorageDescriptor.Local(locNav, typeof(XPathNavigator), false);
 
            return ndSrc; 
        }
 
        /// 
        /// Generate code for QilNodeType.Nop.
        /// 
        protected override QilNode VisitNop(QilUnary ndNop) { 
            return Visit(ndNop.Child);
        } 
 
        /// 
        /// Generate code for QilNodeType.OptimizeBarrier. 
        /// 
        protected override QilNode VisitOptimizeBarrier(QilUnary ndBarrier) {
            return Visit(ndBarrier.Child);
        } 

        ///  
        /// Generate code for QilNodeType.Error. 
        /// 
        protected override QilNode VisitError(QilUnary ndErr) { 
            // XmlQueryRuntime.ThrowException(strErr);
            this.helper.LoadQueryRuntime();
            NestedVisitEnsureStack(ndErr.Child);
            this.helper.Call(XmlILMethods.ThrowException); 

            if (XmlILConstructInfo.Read(ndErr).ConstructMethod == XmlILConstructMethod.Writer) { 
                this.iterCurr.Storage = StorageDescriptor.None(); 
            }
            else { 
                // Push dummy value so that Location is not None and IL rules are met
                this.helper.Emit(OpCodes.Ldnull);
                this.iterCurr.Storage = StorageDescriptor.Stack(typeof(XPathItem), false);
            } 

            return ndErr; 
        } 

        ///  
        /// Generate code for QilNodeType.Warning.
        /// 
        protected override QilNode VisitWarning(QilUnary ndWarning) {
            // runtime.SendMessage(strWarning); 
            this.helper.LoadQueryRuntime();
            NestedVisitEnsureStack(ndWarning.Child); 
            this.helper.Call(XmlILMethods.SendMessage); 

            if (XmlILConstructInfo.Read(ndWarning).ConstructMethod == XmlILConstructMethod.Writer) 
                this.iterCurr.Storage = StorageDescriptor.None();
            else
                VisitEmpty(ndWarning);
 
            return ndWarning;
        } 
 
        /// 
        /// Generate code for QilNodeType.True. 
        /// 
        /// 
        /// BranchingContext.OnFalse context: [nothing]
        /// BranchingContext.OnTrue context:  goto LabelParent; 
        /// BranchingContext.None context:  push true();
        ///  
        protected override QilNode VisitTrue(QilNode ndTrue) { 
            if (this.iterCurr.CurrentBranchingContext != BranchingContext.None) {
                // Make sure there's an IL code path to both the true and false branches in order to avoid dead 
                // code which can cause IL verification errors.
                this.helper.EmitUnconditionalBranch(this.iterCurr.CurrentBranchingContext == BranchingContext.OnTrue ?
                        OpCodes.Brtrue : OpCodes.Brfalse, this.iterCurr.LabelBranch);
 
                this.iterCurr.Storage = StorageDescriptor.None();
            } 
            else { 
                // Push boolean result onto the stack
                this.helper.LoadBoolean(true); 
                this.iterCurr.Storage = StorageDescriptor.Stack(typeof(bool), false);
            }

            return ndTrue; 
        }
 
        ///  
        /// Generate code for QilNodeType.False.
        ///  
        /// 
        /// BranchingContext.OnFalse context: goto LabelParent;
        /// BranchingContext.OnTrue context:  [nothing]
        /// BranchingContext.None context:  push false(); 
        /// 
        protected override QilNode VisitFalse(QilNode ndFalse) { 
            if (this.iterCurr.CurrentBranchingContext != BranchingContext.None) { 
                // Make sure there's an IL code path to both the true and false branches in order to avoid dead
                // code which can cause IL verification errors. 
                this.helper.EmitUnconditionalBranch(this.iterCurr.CurrentBranchingContext == BranchingContext.OnFalse ?
                        OpCodes.Brtrue : OpCodes.Brfalse, this.iterCurr.LabelBranch);

                this.iterCurr.Storage = StorageDescriptor.None(); 
            }
            else { 
                // Push boolean result onto the stack 
                this.helper.LoadBoolean(false);
                this.iterCurr.Storage = StorageDescriptor.Stack(typeof(bool), false); 
            }

            return ndFalse;
        } 

        ///  
        /// Generate code for QilNodeType.LiteralString. 
        /// 
        protected override QilNode VisitLiteralString(QilLiteral ndStr) { 
            this.helper.Emit(OpCodes.Ldstr, (string) ndStr);
            this.iterCurr.Storage = StorageDescriptor.Stack(typeof(string), false);
            return ndStr;
        } 

        ///  
        /// Generate code for QilNodeType.LiteralInt32. 
        /// 
        protected override QilNode VisitLiteralInt32(QilLiteral ndInt) { 
            this.helper.LoadInteger((int) ndInt);
            this.iterCurr.Storage = StorageDescriptor.Stack(typeof(int), false);
            return ndInt;
        } 

        ///  
        /// Generate code for QilNodeType.LiteralInt64. 
        /// 
        protected override QilNode VisitLiteralInt64(QilLiteral ndLong) { 
            this.helper.Emit(OpCodes.Ldc_I8, (long) ndLong);
            this.iterCurr.Storage = StorageDescriptor.Stack(typeof(long), false);
            return ndLong;
        } 

        ///  
        /// Generate code for QilNodeType.LiteralDouble. 
        /// 
        protected override QilNode VisitLiteralDouble(QilLiteral ndDbl) { 
            this.helper.Emit(OpCodes.Ldc_R8, (double) ndDbl);
            this.iterCurr.Storage = StorageDescriptor.Stack(typeof(double), false);
            return ndDbl;
        } 

        ///  
        /// Generate code for QilNodeType.LiteralDecimal. 
        /// 
        protected override QilNode VisitLiteralDecimal(QilLiteral ndDec) { 
            this.helper.ConstructLiteralDecimal((decimal) ndDec);
            this.iterCurr.Storage = StorageDescriptor.Stack(typeof(decimal), false);
            return ndDec;
        } 

        ///  
        /// Generate code for QilNodeType.LiteralQName. 
        /// 
        protected override QilNode VisitLiteralQName(QilName ndQName) { 
            this.helper.ConstructLiteralQName(ndQName.LocalName, ndQName.NamespaceUri);
            this.iterCurr.Storage = StorageDescriptor.Stack(typeof(XmlQualifiedName), false);
            return ndQName;
        } 

        ///  
        /// Generate code for QilNodeType.And. 
        /// 
        ///  
        /// BranchingContext.OnFalse context: (expr1) and (expr2)
        /// ==> if (!expr1) goto LabelParent;
        ///     if (!expr2) goto LabelParent;
        /// 
        /// BranchingContext.OnTrue context: (expr1) and (expr2)
        /// ==> if (!expr1) goto LabelTemp; 
        ///     if (expr1) goto LabelParent; 
        ///     LabelTemp:
        /// 
        /// BranchingContext.None context: (expr1) and (expr2)
        /// ==> if (!expr1) goto LabelTemp;
        ///     if (!expr1) goto LabelTemp;
        ///     push true(); 
        ///     goto LabelSkip;
        ///     LabelTemp: 
        ///     push false(); 
        ///     LabelSkip:
        /// 
        /// 
        protected override QilNode VisitAnd(QilBinary ndAnd) {
            IteratorDescriptor iterParent = this.iterCurr;
            Label lblOnFalse; 

            // Visit left branch 
            StartNestedIterator(ndAnd.Left); 
            lblOnFalse = StartConjunctiveTests(iterParent.CurrentBranchingContext, iterParent.LabelBranch);
            Visit(ndAnd.Left); 
            EndNestedIterator(ndAnd.Left);

            // Visit right branch
            StartNestedIterator(ndAnd.Right); 
            StartLastConjunctiveTest(iterParent.CurrentBranchingContext, iterParent.LabelBranch, lblOnFalse);
            Visit(ndAnd.Right); 
            EndNestedIterator(ndAnd.Right); 

            // End And expression 
            EndConjunctiveTests(iterParent.CurrentBranchingContext, iterParent.LabelBranch, lblOnFalse);

            return ndAnd;
        } 

        ///  
        /// Fixup branching context for all but the last test in a conjunctive (Logical And) expression. 
        /// Return a temporary label which will be passed to StartLastAndBranch() and EndAndBranch().
        ///  
        private Label StartConjunctiveTests(BranchingContext brctxt, Label lblBranch) {
            Label lblOnFalse;

            switch (brctxt) { 
                case BranchingContext.OnFalse:
                    // If condition evaluates to false, branch to false label 
                    this.iterCurr.SetBranching(BranchingContext.OnFalse, lblBranch); 
                    return lblBranch;
 
                default:
                    // If condition evaluates to false:
                    //   1. Jump to new false label that will be fixed just beyond the second condition
                    //   2. Or, jump to code that pushes "false" 
                    lblOnFalse = this.helper.DefineLabel();
                    this.iterCurr.SetBranching(BranchingContext.OnFalse, lblOnFalse); 
                    return lblOnFalse; 
            }
        } 

        /// 
        /// Fixup branching context for the last test in a conjunctive (Logical And) expression.
        ///  
        private void StartLastConjunctiveTest(BranchingContext brctxt, Label lblBranch, Label lblOnFalse) {
            switch (brctxt) { 
                case BranchingContext.OnTrue: 
                    // If last condition evaluates to true, branch to true label
                    this.iterCurr.SetBranching(BranchingContext.OnTrue, lblBranch); 
                    break;

                default:
                    // If last condition evalutes to false, branch to false label 
                    // Else fall through to true code path
                    this.iterCurr.SetBranching(BranchingContext.OnFalse, lblOnFalse); 
                    break; 
            }
        } 

        /// 
        /// Anchor any remaining labels.
        ///  
        private void EndConjunctiveTests(BranchingContext brctxt, Label lblBranch, Label lblOnFalse) {
            switch (brctxt) { 
                case BranchingContext.OnTrue: 
                    // Anchor false label
                    this.helper.MarkLabel(lblOnFalse); 
                    goto case BranchingContext.OnFalse;

                case BranchingContext.OnFalse:
                    this.iterCurr.Storage = StorageDescriptor.None(); 
                    break;
 
                case BranchingContext.None: 
                    // Convert branch targets into push of true/false
                    this.helper.ConvBranchToBool(lblOnFalse, false); 
                    this.iterCurr.Storage = StorageDescriptor.Stack(typeof(bool), false);
                    break;
            }
        } 

        ///  
        /// Generate code for QilNodeType.Or. 
        /// 
        ///  
        /// BranchingContext.OnFalse context: (expr1) or (expr2)
        /// ==> if (expr1) goto LabelTemp;
        ///     if (!expr2) goto LabelParent;
        ///     LabelTemp: 
        ///
        /// BranchingContext.OnTrue context: (expr1) or (expr2) 
        /// ==> if (expr1) goto LabelParent; 
        ///     if (expr1) goto LabelParent;
        /// 
        /// BranchingContext.None context: (expr1) or (expr2)
        /// ==> if (expr1) goto LabelTemp;
        ///     if (expr1) goto LabelTemp;
        ///     push false(); 
        ///     goto LabelSkip;
        ///     LabelTemp: 
        ///     push true(); 
        ///     LabelSkip:
        /// 
        /// 
        protected override QilNode VisitOr(QilBinary ndOr) {
            Label lblTemp = new Label();
 
            // Visit left branch
            switch (this.iterCurr.CurrentBranchingContext) { 
                case BranchingContext.OnFalse: 
                    // If left condition evaluates to true, jump to new label that will be fixed
                    // just beyond the second condition 
                    lblTemp = this.helper.DefineLabel();
                    NestedVisitWithBranch(ndOr.Left, BranchingContext.OnTrue, lblTemp);
                    break;
 
                case BranchingContext.OnTrue:
                    // If left condition evaluates to true, branch to true label 
                    NestedVisitWithBranch(ndOr.Left, BranchingContext.OnTrue, this.iterCurr.LabelBranch); 
                    break;
 
                default:
                    // If left condition evalutes to true, jump to code that pushes "true"
                    Debug.Assert(this.iterCurr.CurrentBranchingContext == BranchingContext.None);
                    lblTemp = this.helper.DefineLabel(); 
                    NestedVisitWithBranch(ndOr.Left, BranchingContext.OnTrue, lblTemp);
                    break; 
            } 

            // Visit right branch 
            switch (this.iterCurr.CurrentBranchingContext) {
                case BranchingContext.OnFalse:
                    // If right condition evaluates to false, branch to false label
                    NestedVisitWithBranch(ndOr.Right, BranchingContext.OnFalse, this.iterCurr.LabelBranch); 
                    break;
 
                case BranchingContext.OnTrue: 
                    // If right condition evaluates to true, branch to true label
                    NestedVisitWithBranch(ndOr.Right, BranchingContext.OnTrue, this.iterCurr.LabelBranch); 
                    break;

                default:
                    // If right condition evalutes to true, jump to code that pushes "true". 
                    // Otherwise, if both conditions evaluate to false, fall through code path
                    // will push "false". 
                    NestedVisitWithBranch(ndOr.Right, BranchingContext.OnTrue, lblTemp); 
                    break;
            } 

            switch (this.iterCurr.CurrentBranchingContext) {
                case BranchingContext.OnFalse:
                    // Anchor true label 
                    this.helper.MarkLabel(lblTemp);
                    goto case BranchingContext.OnTrue; 
 
                case BranchingContext.OnTrue:
                    this.iterCurr.Storage = StorageDescriptor.None(); 
                    break;

                case BranchingContext.None:
                    // Convert branch targets into push of true/false 
                    this.helper.ConvBranchToBool(lblTemp, true);
                    this.iterCurr.Storage = StorageDescriptor.Stack(typeof(bool), false); 
                    break; 
            }
 
            return ndOr;
        }

        ///  
        /// Generate code for QilNodeType.Not.
        ///  
        ///  
        /// BranchingContext.OnFalse context: not(expr1)
        /// ==> if (expr1) goto LabelParent; 
        ///
        /// BranchingContext.OnTrue context: not(expr1)
        /// ==> if (!expr1) goto LabelParent;
        /// 
        /// BranchingContext.None context: not(expr1)
        /// ==> if (expr1) goto LabelTemp; 
        ///     push false(); 
        ///     goto LabelSkip;
        ///     LabelTemp: 
        ///     push true();
        ///     LabelSkip:
        ///
        ///  
        protected override QilNode VisitNot(QilUnary ndNot) {
            Label lblTemp = new Label(); 
 
            // Visit operand
            // Reverse branch types 
            switch (this.iterCurr.CurrentBranchingContext) {
                case BranchingContext.OnFalse:
                    NestedVisitWithBranch(ndNot.Child, BranchingContext.OnTrue, this.iterCurr.LabelBranch);
                    break; 

                case BranchingContext.OnTrue: 
                    NestedVisitWithBranch(ndNot.Child, BranchingContext.OnFalse, this.iterCurr.LabelBranch); 
                    break;
 
                default:
                    // Replace boolean argument on top of stack with its inverse
                    Debug.Assert(this.iterCurr.CurrentBranchingContext == BranchingContext.None);
                    lblTemp = this.helper.DefineLabel(); 
                    NestedVisitWithBranch(ndNot.Child, BranchingContext.OnTrue, lblTemp);
                    break; 
            } 

            if (this.iterCurr.CurrentBranchingContext == BranchingContext.None) { 
                // If condition evaluates to true, then jump to code that pushes false
                this.helper.ConvBranchToBool(lblTemp, false);
                this.iterCurr.Storage = StorageDescriptor.Stack(typeof(bool), false);
            } 
            else {
                this.iterCurr.Storage = StorageDescriptor.None(); 
            } 

            return ndNot; 
        }

        /// 
        /// Generate code for QilNodeType.Conditional. 
        /// 
        protected override QilNode VisitConditional(QilTernary ndCond) { 
            XmlILConstructInfo info = XmlILConstructInfo.Read(ndCond); 

            if (info.ConstructMethod == XmlILConstructMethod.Writer) { 
                Label lblFalse, lblDone;

                // Evaluate if test
                lblFalse = this.helper.DefineLabel(); 
                NestedVisitWithBranch(ndCond.Left, BranchingContext.OnFalse, lblFalse);
 
                // Generate true branch code 
                NestedVisit(ndCond.Center);
 
                // Generate false branch code.  If false branch is the empty list,
                if (ndCond.Right.NodeType == QilNodeType.Sequence && ndCond.Right.Count == 0) {
                    // Then generate simplified code that doesn't contain a false branch
                    this.helper.MarkLabel(lblFalse); 
                    NestedVisit(ndCond.Right);
                } 
                else { 
                    // Jump past false branch
                    lblDone = this.helper.DefineLabel(); 
                    this.helper.EmitUnconditionalBranch(OpCodes.Br, lblDone);

                    // Generate false branch code
                    this.helper.MarkLabel(lblFalse); 
                    NestedVisit(ndCond.Right);
 
                    this.helper.MarkLabel(lblDone); 
                }
 
                this.iterCurr.Storage = StorageDescriptor.None();
            }
            else {
                IteratorDescriptor iterInfoTrue; 
                LocalBuilder locBool = null, locCond = null;
                Label lblFalse, lblDone, lblNext; 
                Type itemStorageType = GetItemStorageType(ndCond); 
                Debug.Assert(info.ConstructMethod == XmlILConstructMethod.Iterator);
 
                // Evaluate conditional test -- save boolean result in boolResult
                Debug.Assert(ndCond.Left.XmlType.TypeCode == XmlTypeCode.Boolean);
                lblFalse = this.helper.DefineLabel();
 
                if (ndCond.XmlType.IsSingleton) {
                    // if (!bool-expr) goto LabelFalse; 
                    NestedVisitWithBranch(ndCond.Left, BranchingContext.OnFalse, lblFalse); 
                }
                else { 
                    // CondType itemCond;
                    // int boolResult = bool-expr;
                    locCond = this.helper.DeclareLocal("$$$cond", itemStorageType);
                    locBool = this.helper.DeclareLocal("$$$boolResult", typeof(bool)); 
                    NestedVisitEnsureLocal(ndCond.Left, locBool);
 
                    // if (!boolResult) goto LabelFalse; 
                    this.helper.Emit(OpCodes.Ldloc, locBool);
                    this.helper.Emit(OpCodes.Brfalse, lblFalse); 
                }

                // Generate code for true branch
                ConditionalBranch(ndCond.Center, itemStorageType, locCond); 
                iterInfoTrue = this.iterNested;
 
                // goto LabelDone; 
                lblDone = this.helper.DefineLabel();
                this.helper.EmitUnconditionalBranch(OpCodes.Br, lblDone); 

                // Generate code for false branch
                // LabelFalse:
                this.helper.MarkLabel(lblFalse); 
                ConditionalBranch(ndCond.Right, itemStorageType, locCond);
 
                // If conditional is not cardinality one, then need to iterate through all values 
                if (!ndCond.XmlType.IsSingleton) {
                    Debug.Assert(!ndCond.Center.XmlType.IsSingleton || !ndCond.Right.XmlType.IsSingleton); 

                    // IL's rules do not allow OpCodes.Br here
                    // goto LabelDone;
                    this.helper.EmitUnconditionalBranch(OpCodes.Brtrue, lblDone); 

                    // LabelNext: 
                    lblNext = this.helper.DefineLabel(); 
                    this.helper.MarkLabel(lblNext);
 
                    // if (boolResult) goto LabelNextTrue else goto LabelNextFalse;
                    this.helper.Emit(OpCodes.Ldloc, locBool);
                    this.helper.Emit(OpCodes.Brtrue, iterInfoTrue.GetLabelNext());
                    this.helper.EmitUnconditionalBranch(OpCodes.Br, this.iterNested.GetLabelNext()); 

                    this.iterCurr.SetIterator(lblNext, StorageDescriptor.Local(locCond, itemStorageType, false)); 
                } 

                // LabelDone: 
                this.helper.MarkLabel(lblDone);
            }

            return ndCond; 
        }
 
        ///  
        /// Generate code for one of the branches of QilNodeType.Conditional.
        ///  
        private void ConditionalBranch(QilNode ndBranch, Type itemStorageType, LocalBuilder locResult) {
            if (locResult == null) {
                Debug.Assert(ndBranch.XmlType.IsSingleton, "Conditional must produce a singleton");
 
                // If in a branching context, then inherit branch target from parent context
                if (this.iterCurr.IsBranching) { 
                    Debug.Assert(itemStorageType == typeof(bool)); 
                    NestedVisitWithBranch(ndBranch, this.iterCurr.CurrentBranchingContext, this.iterCurr.LabelBranch);
                } 
                else {
                    NestedVisitEnsureStack(ndBranch, itemStorageType, false);
                }
            } 
            else {
                // Link nested iterator to parent conditional's iterator 
                NestedVisit(ndBranch, this.iterCurr.GetLabelNext()); 
                this.iterCurr.EnsureItemStorageType(ndBranch.XmlType, itemStorageType);
                this.iterCurr.EnsureLocalNoCache(locResult); 
            }
        }

        ///  
        /// Generate code for QilNodeType.Choice.
        ///  
        protected override QilNode VisitChoice(QilChoice ndChoice) { 
            QilNode ndBranches;
            Label[] switchLabels; 
            Label lblOtherwise, lblDone;
            int regBranches, idx;
            Debug.Assert(XmlILConstructInfo.Read(ndChoice).PushToWriterFirst);
 
            // Evaluate the expression
            NestedVisit(ndChoice.Expression); 
 
            // Generate switching code
            ndBranches = ndChoice.Branches; 
            regBranches = ndBranches.Count - 1;
            switchLabels = new Label[regBranches];
            for (idx = 0; idx < regBranches; idx++)
                switchLabels[idx] = this.helper.DefineLabel(); 

            lblOtherwise = this.helper.DefineLabel(); 
            lblDone = this.helper.DefineLabel(); 

            // switch (value) 
            //   case 0: goto Label[0];
            //   ...
            //   case N-1: goto Label[N-1];
            //   default: goto LabelOtherwise; 
            this.helper.Emit(OpCodes.Switch, switchLabels);
            this.helper.EmitUnconditionalBranch(OpCodes.Br, lblOtherwise); 
 
            for (idx = 0; idx < regBranches; idx++) {
                // Label[i]: 
                this.helper.MarkLabel(switchLabels[idx]);

                // Generate regular branch code
                NestedVisit(ndBranches[idx]); 

                // goto LabelDone 
                this.helper.EmitUnconditionalBranch(OpCodes.Br, lblDone); 
            }
 
            // LabelOtherwise:
            this.helper.MarkLabel(lblOtherwise);

            // Generate otherwise branch code 
            NestedVisit(ndBranches[idx]);
 
            // LabelDone: 
            this.helper.MarkLabel(lblDone);
 
            this.iterCurr.Storage = StorageDescriptor.None();

            return ndChoice;
        } 

        ///  
        /// Generate code for QilNodeType.Length. 
        /// 
        ///  
        /// int length = 0;
        /// foreach (item in expr)
        ///   length++;
        ///  
        protected override QilNode VisitLength(QilUnary ndSetLen) {
            Label lblOnEnd = this.helper.DefineLabel(); 
            OptimizerPatterns patt = OptimizerPatterns.Read(ndSetLen); 

            if (CachesResult(ndSetLen.Child)) { 
                NestedVisitEnsureStack(ndSetLen.Child);
                this.helper.CallCacheCount(this.iterNested.Storage.ItemStorageType);
            }
            else { 
                // length = 0;
                this.helper.Emit(OpCodes.Ldc_I4_0); 
 
                StartNestedIterator(ndSetLen.Child, lblOnEnd);
 
                // foreach (item in expr) {
                Visit(ndSetLen.Child);

                // Pop values of SetLength expression from the stack if necessary 
                this.iterCurr.EnsureNoCache();
                this.iterCurr.DiscardStack(); 
 
                // length++;
                this.helper.Emit(OpCodes.Ldc_I4_1); 
                this.helper.Emit(OpCodes.Add);

                if (patt.MatchesPattern(OptimizerPatternName.MaxPosition)) {
                    // Short-circuit rest of loop if max position has been exceeded 
                    this.helper.Emit(OpCodes.Dup);
                    this.helper.LoadInteger((int) patt.GetArgument(OptimizerPatternArgument.MaxPosition)); 
                    this.helper.Emit(OpCodes.Bgt, lblOnEnd); 
                }
 
                // }
                this.iterCurr.LoopToEnd(lblOnEnd);

                EndNestedIterator(ndSetLen.Child); 
            }
 
            this.iterCurr.Storage = StorageDescriptor.Stack(typeof(int), false); 

            return ndSetLen; 
        }

        /// 
        /// Find physical query plan for QilNodeType.Sequence. 
        /// 
        protected override QilNode VisitSequence(QilList ndSeq) { 
            if (XmlILConstructInfo.Read(ndSeq).ConstructMethod == XmlILConstructMethod.Writer) { 
                // Push each item in the list to output
                foreach (QilNode nd in ndSeq) 
                    NestedVisit(nd);
            }
            else {
                // Empty sequence is special case 
                if (ndSeq.Count == 0)
                    VisitEmpty(ndSeq); 
                else 
                    Sequence(ndSeq);
            } 

            return ndSeq;
        }
 
        /// 
        /// Generate code for the empty sequence. 
        ///  
        private void VisitEmpty(QilNode nd) {
            Debug.Assert(XmlILConstructInfo.Read(nd).PullFromIteratorFirst, "VisitEmpty should only be called if items are iterated"); 

            // IL's rules prevent OpCodes.Br here
            // Empty sequence
            this.helper.EmitUnconditionalBranch(OpCodes.Brtrue, this.iterCurr.GetLabelNext()); 

            // Push dummy value so that Location is not None and IL rules are met 
            this.helper.Emit(OpCodes.Ldnull); 
            this.iterCurr.Storage = StorageDescriptor.Stack(typeof(XPathItem), false);
        } 

        /// 
        /// Generate code for QilNodeType.Sequence, when sort-merging to retain document order is not necessary.
        ///  
        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));
            }
        } 

        ///  
        /// Generate code for QilNodeType.Union. 
        /// 
        protected override QilNode VisitUnion(QilBinary ndUnion) { 
            return CreateSetIterator(ndUnion, "$$$iterUnion", typeof(UnionIterator), XmlILMethods.UnionCreate, XmlILMethods.UnionNext);
        }

        ///  
        /// Generate code for QilNodeType.Intersection.
        ///  
        protected override QilNode VisitIntersection(QilBinary ndInter) { 
            return CreateSetIterator(ndInter, "$$$iterInter", typeof(IntersectIterator), XmlILMethods.InterCreate, XmlILMethods.InterNext);
        } 

        /// 
        /// Generate code for QilNodeType.Difference.
        ///  
        protected override QilNode VisitDifference(QilBinary ndDiff) {
            return CreateSetIterator(ndDiff, "$$$iterDiff", typeof(DifferenceIterator), XmlILMethods.DiffCreate, XmlILMethods.DiffNext); 
        } 

        ///  
        /// Generate code to combine nodes from two nested iterators using Union, Intersection, or Difference semantics.
        /// 
        private QilNode CreateSetIterator(QilBinary ndSet, string iterName, Type iterType, MethodInfo methCreate, MethodInfo methNext) {
            LocalBuilder locIter, locNav; 
            Label lblNext, lblCall, lblNextLeft, lblNextRight, lblInitRight;
 
            // SetIterator iterSet; 
            // XPathNavigator navSet;
            locIter = this.helper.DeclareLocal(iterName, iterType); 
            locNav = this.helper.DeclareLocal("$$$navSet", typeof(XPathNavigator));

            // iterSet.Create(runtime);
            this.helper.Emit(OpCodes.Ldloca, locIter); 
            this.helper.LoadQueryRuntime();
            this.helper.Call(methCreate); 
 
            // Define labels that will be used
            lblNext = this.helper.DefineLabel(); 
            lblCall = this.helper.DefineLabel();
            lblInitRight = this.helper.DefineLabel();

            // Generate left nested iterator.  When it is empty, it will branch to lblNext. 
            // goto LabelCall;
            NestedVisit(ndSet.Left, lblNext); 
            lblNextLeft = this.iterNested.GetLabelNext(); 
            this.iterCurr.EnsureLocal(locNav);
            this.helper.EmitUnconditionalBranch(OpCodes.Brtrue, lblCall); 

            // Generate right nested iterator.  When it is empty, it will branch to lblNext.
            // LabelInitRight:
            // goto LabelCall; 
            this.helper.MarkLabel(lblInitRight);
            NestedVisit(ndSet.Right, lblNext); 
            lblNextRight = this.iterNested.GetLabelNext(); 
            this.iterCurr.EnsureLocal(locNav);
            this.helper.EmitUnconditionalBranch(OpCodes.Brtrue, lblCall); 

            // LabelNext:
            this.helper.MarkLabel(lblNext);
            this.helper.Emit(OpCodes.Ldnull); 
            this.helper.Emit(OpCodes.Stloc, locNav);
 
            // LabelCall: 
            // switch (iterSet.MoveNext(nestedNested)) {
            //      case SetIteratorResult.NoMoreNodes: goto LabelNextCtxt; 
            //      case SetIteratorResult.InitRightIterator: goto LabelInitRight;
            //      case SetIteratorResult.NeedLeftNode: goto LabelNextLeft;
            //      case SetIteratorResult.NeedRightNode: goto LabelNextRight;
            // } 
            this.helper.MarkLabel(lblCall);
            this.helper.Emit(OpCodes.Ldloca, locIter); 
            this.helper.Emit(OpCodes.Ldloc, locNav); 
            this.helper.Call(methNext);
 
            // If this iterator always returns a single node, then NoMoreNodes will never be returned
            // Don't expose Next label if this iterator always returns a single node
            if (ndSet.XmlType.IsSingleton) {
                this.helper.Emit(OpCodes.Switch, new Label[] {lblInitRight, lblNextLeft, lblNextRight}); 
                this.iterCurr.Storage = StorageDescriptor.Current(locIter, typeof(XPathNavigator));
            } 
            else { 
                this.helper.Emit(OpCodes.Switch, new Label[] {this.iterCurr.GetLabelNext(), lblInitRight, lblNextLeft, lblNextRight});
                this.iterCurr.SetIterator(lblNext, StorageDescriptor.Current(locIter, typeof(XPathNavigator))); 
            }

            return ndSet;
        } 

        ///  
        /// Generate code for QilNodeType.Average. 
        /// 
        protected override QilNode VisitAverage(QilUnary ndAvg) { 
            XmlILStorageMethods meths = XmlILMethods.StorageMethods[GetItemStorageType(ndAvg)];
            return CreateAggregator(ndAvg, "$$$aggAvg", meths, meths.AggAvg, meths.AggAvgResult);
        }
 
        /// 
        /// Generate code for QilNodeType.Sum. 
        ///  
        protected override QilNode VisitSum(QilUnary ndSum) {
            XmlILStorageMethods meths = XmlILMethods.StorageMethods[GetItemStorageType(ndSum)]; 
            return CreateAggregator(ndSum, "$$$aggSum", meths, meths.AggSum, meths.AggSumResult);
        }

        ///  
        /// Generate code for QilNodeType.Minimum.
        ///  
        protected override QilNode VisitMinimum(QilUnary ndMin) { 
            XmlILStorageMethods meths = XmlILMethods.StorageMethods[GetItemStorageType(ndMin)];
            return CreateAggregator(ndMin, "$$$aggMin", meths, meths.AggMin, meths.AggMinResult); 
        }

        /// 
        /// Generate code for QilNodeType.Maximum. 
        /// 
        protected override QilNode VisitMaximum(QilUnary ndMax) { 
            XmlILStorageMethods meths = XmlILMethods.StorageMethods[GetItemStorageType(ndMax)]; 
            return CreateAggregator(ndMax, "$$$aggMax", meths, meths.AggMax, meths.AggMaxResult);
        } 

        /// 
        /// Generate code for QilNodeType.Sum, QilNodeType.Average, QilNodeType.Minimum, and QilNodeType.Maximum.
        ///  
        private QilNode CreateAggregator(QilUnary ndAgg, string aggName, XmlILStorageMethods methods, MethodInfo methAgg, MethodInfo methResult) {
            Label lblOnEnd = this.helper.DefineLabel(); 
            Type typAgg = methAgg.DeclaringType; 
            LocalBuilder locAgg;
 
            // Aggregate agg;
            // agg.Create();
            locAgg = this.helper.DeclareLocal(aggName, typAgg);
            this.helper.Emit(OpCodes.Ldloca, locAgg); 
            this.helper.Call(methods.AggCreate);
 
            // foreach (num in expr) { 
            StartNestedIterator(ndAgg.Child, lblOnEnd);
            this.helper.Emit(OpCodes.Ldloca, locAgg); 
            Visit(ndAgg.Child);

            //   agg.Aggregate(num);
            this.iterCurr.EnsureStackNoCache(); 
            this.iterCurr.EnsureItemStorageType(ndAgg.XmlType, GetItemStorageType(ndAgg));
            this.helper.Call(methAgg); 
            this.helper.Emit(OpCodes.Ldloca, locAgg); 

            // } 
            this.iterCurr.LoopToEnd(lblOnEnd);

            // End nested iterator
            EndNestedIterator(ndAgg.Child); 

            // If aggregate might be empty sequence, then generate code to handle this possibility 
            if (ndAgg.XmlType.MaybeEmpty) { 
                // if (agg.IsEmpty) goto LabelNextCtxt;
                this.helper.Call(methods.AggIsEmpty); 
                this.helper.Emit(OpCodes.Brtrue, this.iterCurr.GetLabelNext());
                this.helper.Emit(OpCodes.Ldloca, locAgg);
            }
 
            // result = agg.Result;
            this.helper.Call(methResult); 
            this.iterCurr.Storage = StorageDescriptor.Stack(GetItemStorageType(ndAgg), false); 

            return ndAgg; 
        }

        /// 
        /// Generate code for QilNodeType.Negate. 
        /// 
        protected override QilNode VisitNegate(QilUnary ndNeg) { 
            NestedVisitEnsureStack(ndNeg.Child); 
            this.helper.CallArithmeticOp(QilNodeType.Negate, ndNeg.XmlType.TypeCode);
            this.iterCurr.Storage = StorageDescriptor.Stack(GetItemStorageType(ndNeg), false); 
            return ndNeg;
        }

        ///  
        /// Generate code for QilNodeType.Add.
        ///  
        protected override QilNode VisitAdd(QilBinary ndPlus) { 
            return ArithmeticOp(ndPlus);
        } 

        /// 
        /// Generate code for QilNodeType.Subtract.
        ///  
        protected override QilNode VisitSubtract(QilBinary ndMinus) {
            return ArithmeticOp(ndMinus); 
        } 

        ///  
        /// Generate code for QilNodeType.Multiply.
        /// 
        protected override QilNode VisitMultiply(QilBinary ndMul) {
            return ArithmeticOp(ndMul); 
        }
 
        ///  
        /// Generate code for QilNodeType.Divide.
        ///  
        protected override QilNode VisitDivide(QilBinary ndDiv) {
            return ArithmeticOp(ndDiv);
        }
 
        /// 
        /// Generate code for QilNodeType.Modulo. 
        ///  
        protected override QilNode VisitModulo(QilBinary ndMod) {
            return ArithmeticOp(ndMod); 
        }

        /// 
        /// Generate code for two-argument arithmetic operations. 
        /// 
        private QilNode ArithmeticOp(QilBinary ndOp) { 
            NestedVisitEnsureStack(ndOp.Left, ndOp.Right); 
            this.helper.CallArithmeticOp(ndOp.NodeType, ndOp.XmlType.TypeCode);
            this.iterCurr.Storage = StorageDescriptor.Stack(GetItemStorageType(ndOp), false); 
            return ndOp;
        }

        ///  
        /// Generate code for QilNodeType.StrLength.
        ///  
        protected override QilNode VisitStrLength(QilUnary ndLen) { 
            NestedVisitEnsureStack(ndLen.Child);
            this.helper.Call(XmlILMethods.StrLen); 
            this.iterCurr.Storage = StorageDescriptor.Stack(typeof(int), false);
            return ndLen;
        }
 
        /// 
        /// Generate code for QilNodeType.StrConcat. 
        ///  
        protected override QilNode VisitStrConcat(QilStrConcat ndStrConcat) {
            LocalBuilder locStringConcat; 
            bool fasterConcat;
            QilNode delimiter;
            QilNode listStrings;
            Debug.Assert(!ndStrConcat.Values.XmlType.IsSingleton, "Optimizer should have folded StrConcat of a singleton value"); 

            // Get delimiter (assuming it's not the empty string) 
            delimiter = ndStrConcat.Delimiter; 
            if (delimiter.NodeType == QilNodeType.LiteralString && ((string) (QilLiteral) delimiter).Length == 0) {
                delimiter = null; 
            }

            listStrings = ndStrConcat.Values;
            if (listStrings.NodeType == QilNodeType.Sequence && listStrings.Count < 5) { 
                // Faster concat possible only if cardinality can be guaranteed at compile-time and there's no delimiter
                fasterConcat = true; 
                foreach (QilNode ndStr in listStrings) { 
                    if (!ndStr.XmlType.IsSingleton)
                        fasterConcat = false; 
                }
            }
            else {
                // If more than 4 strings, array will need to be built 
                fasterConcat = false;
            } 
 
            if (fasterConcat) {
                foreach (QilNode ndStr in listStrings) 
                    NestedVisitEnsureStack(ndStr);

                this.helper.CallConcatStrings(listStrings.Count);
            } 
            else {
                // Create StringConcat helper internal class 
                locStringConcat = this.helper.DeclareLocal("$$$strcat", typeof(StringConcat)); 
                this.helper.Emit(OpCodes.Ldloca, locStringConcat);
                this.helper.Call(XmlILMethods.StrCatClear); 

                // Set delimiter, if it's not empty string
                if (delimiter != null) {
                    this.helper.Emit(OpCodes.Ldloca, locStringConcat); 
                    NestedVisitEnsureStack(delimiter);
                    this.helper.Call(XmlILMethods.StrCatDelim); 
                } 

                this.helper.Emit(OpCodes.Ldloca, locStringConcat); 

                if (listStrings.NodeType == QilNodeType.Sequence) {
                    foreach (QilNode ndStr in listStrings)
                        GenerateConcat(ndStr, locStringConcat); 
                }
                else { 
                    GenerateConcat(listStrings, locStringConcat); 
                }
 
                // Push resulting string onto stack
                this.helper.Call(XmlILMethods.StrCatResult);
            }
 
            this.iterCurr.Storage = StorageDescriptor.Stack(typeof(string), false);
 
            return ndStrConcat; 
        }
 
        /// 
        /// Generate code to concatenate string values returned by expression "ndStr" using the StringConcat helper class.
        /// 
        private void GenerateConcat(QilNode ndStr, LocalBuilder locStringConcat) { 
            Label lblOnEnd;
 
            // str = each string; 
            lblOnEnd = this.helper.DefineLabel();
            StartNestedIterator(ndStr, lblOnEnd); 
            Visit(ndStr);

            // strcat.Concat(str);
            this.iterCurr.EnsureStackNoCache(); 
            this.iterCurr.EnsureItemStorageType(ndStr.XmlType, typeof(string));
            this.helper.Call(XmlILMethods.StrCatCat); 
            this.helper.Emit(OpCodes.Ldloca, locStringConcat); 

            // Get next string 
            // goto LabelNext;
            // LabelOnEnd:
            this.iterCurr.LoopToEnd(lblOnEnd);
 
            // End nested iterator
            EndNestedIterator(ndStr); 
        } 

        ///  
        /// Generate code for QilNodeType.StrParseQName.
        /// 
        protected override QilNode VisitStrParseQName(QilBinary ndParsedTagName) {
            VisitStrParseQName(ndParsedTagName, false); 
            return ndParsedTagName;
        } 
 
        /// 
        /// Generate code for QilNodeType.StrParseQName. 
        /// 
        private void VisitStrParseQName(QilBinary ndParsedTagName, bool preservePrefix) {
            // If QName prefix should be preserved, then don't create an XmlQualifiedName, which discards the prefix
            if (!preservePrefix) 
                this.helper.LoadQueryRuntime();
 
            // Push (possibly computed) tag name onto the stack 
            NestedVisitEnsureStack(ndParsedTagName.Left);
 
            // If type of second parameter is string,
            if (ndParsedTagName.Right.XmlType.TypeCode == XmlTypeCode.String) {
                // Then push (possibly computed) namespace onto the stack
                Debug.Assert(ndParsedTagName.Right.XmlType.IsSingleton); 
                NestedVisitEnsureStack(ndParsedTagName.Right);
 
                if (!preservePrefix) 
                    this.helper.CallParseTagName(GenerateNameType.TagNameAndNamespace);
            } 
            else {
                // Else push index of set of prefix mappings to use in resolving the prefix
                if (ndParsedTagName.Right.NodeType == QilNodeType.Sequence)
                    this.helper.LoadInteger(this.helper.StaticData.DeclarePrefixMappings(ndParsedTagName.Right)); 
                else
                    this.helper.LoadInteger(this.helper.StaticData.DeclarePrefixMappings(new QilNode[] {ndParsedTagName.Right})); 
 
                // If QName prefix should be preserved, then don't create an XmlQualifiedName, which discards the prefix
                if (!preservePrefix) 
                    this.helper.CallParseTagName(GenerateNameType.TagNameAndMappings);
            }

            this.iterCurr.Storage = StorageDescriptor.Stack(typeof(XmlQualifiedName), false); 
        }
 
        ///  
        /// Generate code for QilNodeType.Ne.
        ///  
        protected override QilNode VisitNe(QilBinary ndNe) {
            Compare(ndNe);
            return ndNe;
        } 

        ///  
        /// Generate code for QilNodeType.Eq. 
        /// 
        protected override QilNode VisitEq(QilBinary ndEq) { 
            Compare(ndEq);
            return ndEq;
        }
 
        /// 
        /// Generate code for QilNodeType.Gt. 
        ///  
        protected override QilNode VisitGt(QilBinary ndGt) {
            Compare(ndGt); 
            return ndGt;
        }

        ///  
        /// Generate code for QilNodeType.Ne.
        ///  
        protected override QilNode VisitGe(QilBinary ndGe) { 
            Compare(ndGe);
            return ndGe; 
        }

        /// 
        /// Generate code for QilNodeType.Lt. 
        /// 
        protected override QilNode VisitLt(QilBinary ndLt) { 
            Compare(ndLt); 
            return ndLt;
        } 

        /// 
        /// Generate code for QilNodeType.Le.
        ///  
        protected override QilNode VisitLe(QilBinary ndLe) {
            Compare(ndLe); 
            return ndLe; 
        }
 
        /// 
        /// Generate code for comparison operations.
        /// 
        private void Compare(QilBinary ndComp) { 
            QilNodeType relOp = ndComp.NodeType;
            XmlTypeCode code; 
            Debug.Assert(ndComp.Left.XmlType.IsAtomicValue && ndComp.Right.XmlType.IsAtomicValue, "Operands to compare must be atomic values."); 
            Debug.Assert(ndComp.Left.XmlType.IsSingleton && ndComp.Right.XmlType.IsSingleton, "Operands to compare must be cardinality one.");
            Debug.Assert(ndComp.Left.XmlType == ndComp.Right.XmlType, "Operands to compare may not be heterogenous."); 

            if (relOp == QilNodeType.Eq || relOp == QilNodeType.Ne) {
                // Generate better code for certain special cases
                if (TryZeroCompare(relOp, ndComp.Left, ndComp.Right)) 
                    return;
 
                if (TryZeroCompare(relOp, ndComp.Right, ndComp.Left)) 
                    return;
 
                if (TryNameCompare(relOp, ndComp.Left, ndComp.Right))
                    return;

                if (TryNameCompare(relOp, ndComp.Right, ndComp.Left)) 
                    return;
            } 
 
            // Push two operands onto the stack
            NestedVisitEnsureStack(ndComp.Left, ndComp.Right); 

            // Perform comparison
            code = ndComp.Left.XmlType.TypeCode;
            switch (code) { 
                case XmlTypeCode.String:
                case XmlTypeCode.Decimal: 
                case XmlTypeCode.QName: 
                    if (relOp == QilNodeType.Eq || relOp == QilNodeType.Ne) {
                        this.helper.CallCompareEquals(code); 

                        // If relOp is Eq, then branch to true label or push "true" if Equals function returns true (non-zero)
                        // If relOp is Ne, then branch to true label or push "true" if Equals function returns false (zero)
                        ZeroCompare((relOp == QilNodeType.Eq) ? QilNodeType.Ne : QilNodeType.Eq, true); 
                    }
                    else { 
                        Debug.Assert(code != XmlTypeCode.QName, "QName values do not support the " + relOp + " operation"); 

                        // Push -1, 0, or 1 onto the stack depending upon the result of the comparison 
                        this.helper.CallCompare(code);

                        // Compare result to 0 (e.g. Ge is >= 0)
                        this.helper.Emit(OpCodes.Ldc_I4_0); 
                        ClrCompare(relOp, code);
                    } 
                    break; 

                case XmlTypeCode.Integer: 
                case XmlTypeCode.Int:
                case XmlTypeCode.Boolean:
                case XmlTypeCode.Double:
                    ClrCompare(relOp, code); 
                    break;
 
                default: 
                    Debug.Fail("Comparisons for datatype " + code + " are invalid.");
                    break; 
            }
        }

        ///  
        /// Generate code for QilNodeType.VisitIs.
        ///  
        protected override QilNode VisitIs(QilBinary ndIs) { 
            // Generate code to push arguments onto stack
            NestedVisitEnsureStack(ndIs.Left, ndIs.Right); 
            this.helper.Call(XmlILMethods.NavSamePos);

            // navThis.IsSamePosition(navThat);
            ZeroCompare(QilNodeType.Ne, true); 
            return ndIs;
        } 
 
        /// 
        /// Generate code for QilNodeType.VisitBefore. 
        /// 
        protected override QilNode VisitBefore(QilBinary ndBefore) {
            ComparePosition(ndBefore);
            return ndBefore; 
        }
 
        ///  
        /// Generate code for QilNodeType.VisitAfter.
        ///  
        protected override QilNode VisitAfter(QilBinary ndAfter) {
            ComparePosition(ndAfter);
            return ndAfter;
        } 

        ///  
        /// Generate code for QilNodeType.VisitBefore and QilNodeType.VisitAfter. 
        /// 
        private void ComparePosition(QilBinary ndComp) { 
            // Generate code to push arguments onto stack
            this.helper.LoadQueryRuntime();
            NestedVisitEnsureStack(ndComp.Left, ndComp.Right);
            this.helper.Call(XmlILMethods.CompPos); 

            // XmlQueryRuntime.ComparePosition(navThis, navThat) < 0; 
            this.helper.LoadInteger(0); 
            ClrCompare(ndComp.NodeType == QilNodeType.Before ? QilNodeType.Lt : QilNodeType.Gt, XmlTypeCode.String);
        } 

        /// 
        /// Generate code for a QilNodeType.For.
        ///  
        protected override QilNode VisitFor(QilIterator ndFor) {
            IteratorDescriptor iterInfo; 
 
            // Reference saved location
            iterInfo = XmlILAnnotation.Write(ndFor).CachedIteratorDescriptor; 
            this.iterCurr.Storage = iterInfo.Storage;

            // If the iterator is a reference to a global variable or parameter,
            if (this.iterCurr.Storage.Location == ItemLocation.Global) { 
                // Then compute global value and push it onto the stack
                this.iterCurr.EnsureStack(); 
            } 

            return ndFor; 
        }

        /// 
        /// Generate code for a QilNodeType.Let. 
        /// 
        protected override QilNode VisitLet(QilIterator ndLet) { 
            // Same as For 
            return VisitFor(ndLet);
        } 

        /// 
        /// Generate code for a QilNodeType.Parameter.
        ///  
        protected override QilNode VisitParameter(QilParameter ndParameter) {
            // Same as For 
            return VisitFor(ndParameter); 
        }
 
        /// 
        /// Generate code for a QilNodeType.Loop.
        /// 
        protected override QilNode VisitLoop(QilLoop ndLoop) { 
            bool hasOnEnd;
            Label lblOnEnd; 
 
            StartWriterLoop(ndLoop, out hasOnEnd, out lblOnEnd);
 
            StartBinding(ndLoop.Variable);

            // Unnest loop body as part of the current iterator
            Visit(ndLoop.Body); 

            EndBinding(ndLoop.Variable); 
 
            EndWriterLoop(ndLoop, hasOnEnd, lblOnEnd);
 
            return ndLoop;
        }

        ///  
        /// Generate code for a QilNodeType.Filter.
        ///  
        protected override QilNode VisitFilter(QilLoop ndFilter) { 
            // Handle any special-case patterns that are rooted at Filter
            if (HandleFilterPatterns(ndFilter)) 
                return ndFilter;

            StartBinding(ndFilter.Variable);
 
            // Result of filter is the sequence bound to the iterator
            this.iterCurr.SetIterator(this.iterNested); 
 
            // If filter is false, skip the current item
            StartNestedIterator(ndFilter.Body); 
            this.iterCurr.SetBranching(BranchingContext.OnFalse, this.iterCurr.ParentIterator.GetLabelNext());
            Visit(ndFilter.Body);
            EndNestedIterator(ndFilter.Body);
 
            EndBinding(ndFilter.Variable);
 
            return ndFilter; 
        }
 
        /// 
        /// There are a number of path patterns that can be rooted at Filter nodes.  Determine whether one of these patterns
        /// has been previously matched on "ndFilter".  If so, generate code for the pattern and return true.  Otherwise, just
        /// return false. 
        /// 
        private bool HandleFilterPatterns(QilLoop ndFilter) { 
            OptimizerPatterns patt = OptimizerPatterns.Read(ndFilter); 
            LocalBuilder locIter;
            XmlNodeKindFlags kinds; 
            QilName name;
            QilNode input, step;
            bool isFilterElements;
 
            // Handle FilterElements and FilterContentKind patterns
            isFilterElements = patt.MatchesPattern(OptimizerPatternName.FilterElements); 
            if (isFilterElements || patt.MatchesPattern(OptimizerPatternName.FilterContentKind)) { 
                if (isFilterElements) {
                    // FilterElements pattern, so Kind = Element and Name = Argument 
                    kinds = XmlNodeKindFlags.Element;
                    name = (QilName) patt.GetArgument(OptimizerPatternArgument.ElementQName);
                }
                else { 
                    // FilterKindTest pattern, so Kind = Argument and Name = null
                    kinds = ((XmlQueryType) patt.GetArgument(OptimizerPatternArgument.KindTestType)).NodeKinds; 
                    name = null; 
                }
 
                step = (QilNode) patt.GetArgument(OptimizerPatternArgument.StepNode);
                input = (QilNode) patt.GetArgument(OptimizerPatternArgument.StepInput);
                switch (step.NodeType) {
                    case QilNodeType.Content: 
                        if (isFilterElements) {
                            // Iterator iter; 
                            locIter = this.helper.DeclareLocal("$$$iterElemContent", typeof(ElementContentIterator)); 

                            // iter.Create(navCtxt, locName, ns); 
                            this.helper.Emit(OpCodes.Ldloca, locIter);
                            NestedVisitEnsureStack(input);
                            this.helper.CallGetAtomizedName(this.helper.StaticData.DeclareName(name.LocalName));
                            this.helper.CallGetAtomizedName(this.helper.StaticData.DeclareName(name.NamespaceUri)); 
                            this.helper.Call(XmlILMethods.ElemContentCreate);
 
                            GenerateSimpleIterator(typeof(XPathNavigator), locIter, XmlILMethods.ElemContentNext); 
                        }
                        else { 
                            if (kinds == XmlNodeKindFlags.Content) {
                                CreateSimpleIterator(input, "$$$iterContent", typeof(ContentIterator), XmlILMethods.ContentCreate, XmlILMethods.ContentNext);
                            }
                            else { 
                                // Iterator iter;
                                locIter = this.helper.DeclareLocal("$$$iterContent", typeof(NodeKindContentIterator)); 
 
                                // iter.Create(navCtxt, nodeType);
                                this.helper.Emit(OpCodes.Ldloca, locIter); 
                                NestedVisitEnsureStack(input);
                                this.helper.LoadInteger((int) QilXmlToXPathNodeType(kinds));
                                this.helper.Call(XmlILMethods.KindContentCreate);
 
                                GenerateSimpleIterator(typeof(XPathNavigator), locIter, XmlILMethods.KindContentNext);
                            } 
                        } 
                        return true;
 
                    case QilNodeType.Parent:
                        CreateFilteredIterator(input, "$$$iterPar", typeof(ParentIterator), XmlILMethods.ParentCreate, XmlILMethods.ParentNext,
                                               kinds, name, TriState.Unknown, null);
                        return true; 

                    case QilNodeType.Ancestor: 
                    case QilNodeType.AncestorOrSelf: 
                        CreateFilteredIterator(input, "$$$iterAnc", typeof(AncestorIterator), XmlILMethods.AncCreate, XmlILMethods.AncNext,
                                               kinds, name, (step.NodeType == QilNodeType.Ancestor) ? TriState.False : TriState.True, null); 
                        return true;

                    case QilNodeType.Descendant:
                    case QilNodeType.DescendantOrSelf: 
                        CreateFilteredIterator(input, "$$$iterDesc", typeof(DescendantIterator), XmlILMethods.DescCreate, XmlILMethods.DescNext,
                                               kinds, name, (step.NodeType == QilNodeType.Descendant) ? TriState.False : TriState.True, null); 
                        return true; 

                    case QilNodeType.Preceding: 
                        CreateFilteredIterator(input, "$$$iterPrec", typeof(PrecedingIterator), XmlILMethods.PrecCreate, XmlILMethods.PrecNext,
                                               kinds, name, TriState.Unknown, null);
                        return true;
 
                    case QilNodeType.FollowingSibling:
                        CreateFilteredIterator(input, "$$$iterFollSib", typeof(FollowingSiblingIterator), XmlILMethods.FollSibCreate, XmlILMethods.FollSibNext, 
                                               kinds, name, TriState.Unknown, null); 
                        return true;
 
                    case QilNodeType.PrecedingSibling:
                        CreateFilteredIterator(input, "$$$iterPreSib", typeof(PrecedingSiblingIterator), XmlILMethods.PreSibCreate, XmlILMethods.PreSibNext,
                                               kinds, name, TriState.Unknown, null);
                        return true; 

                    case QilNodeType.NodeRange: 
                        CreateFilteredIterator(input, "$$$iterRange", typeof(NodeRangeIterator), XmlILMethods.NodeRangeCreate, XmlILMethods.NodeRangeNext, 
                                               kinds, name, TriState.Unknown, ((QilBinary) step).Right);
                        return true; 

                    case QilNodeType.XPathFollowing:
                        CreateFilteredIterator(input, "$$$iterFoll", typeof(XPathFollowingIterator), XmlILMethods.XPFollCreate, XmlILMethods.XPFollNext,
                                               kinds, name, TriState.Unknown, null); 
                        return true;
 
                    case QilNodeType.XPathPreceding: 
                        CreateFilteredIterator(input, "$$$iterPrec", typeof(XPathPrecedingIterator), XmlILMethods.XPPrecCreate, XmlILMethods.XPPrecNext,
                                               kinds, name, TriState.Unknown, null); 
                        return true;

                    default:
                        Debug.Assert(false, "Pattern " + step.NodeType + " should have been handled."); 
                        break;
                } 
            } 
            else if (patt.MatchesPattern(OptimizerPatternName.FilterAttributeKind)) {
                // Handle FilterAttributeKind pattern 
                input = (QilNode) patt.GetArgument(OptimizerPatternArgument.StepInput);
                CreateSimpleIterator(input, "$$$iterAttr", typeof(AttributeIterator), XmlILMethods.AttrCreate, XmlILMethods.AttrNext);
                return true;
            } 
            else if (patt.MatchesPattern(OptimizerPatternName.EqualityIndex)) {
                // Handle EqualityIndex pattern 
                Label lblOnEnd = this.helper.DefineLabel(); 
                Label lblLookup = this.helper.DefineLabel();
                QilIterator nodes = (QilIterator) patt.GetArgument(OptimizerPatternArgument.IndexedNodes); 
                QilNode keys = (QilNode) patt.GetArgument(OptimizerPatternArgument.KeyExpression);

                // XmlILIndex index;
                // if (runtime.FindIndex(navCtxt, indexId, out index)) goto LabelLookup; 
                LocalBuilder locIndex = this.helper.DeclareLocal("$$$index", typeof(XmlILIndex));
                this.helper.LoadQueryRuntime(); 
                this.helper.Emit(OpCodes.Ldarg_1); 
                this.helper.LoadInteger(this.indexId);
                this.helper.Emit(OpCodes.Ldloca, locIndex); 
                this.helper.Call(XmlILMethods.FindIndex);
                this.helper.Emit(OpCodes.Brtrue, lblLookup);

                // runtime.AddNewIndex(navCtxt, indexId, [build index]); 
                this.helper.LoadQueryRuntime();
                this.helper.Emit(OpCodes.Ldarg_1); 
                this.helper.LoadInteger(this.indexId); 
                this.helper.Emit(OpCodes.Ldloc, locIndex);
 
                // Generate code to iterate over the the nodes which are being indexed ($iterNodes in the pattern)
                StartNestedIterator(nodes, lblOnEnd);
                StartBinding(nodes);
 
                // Generate code to iterate over the keys for each node ($bindingKeys in the pattern)
                Visit(keys); 
 
                // index.Add(key, value);
                this.iterCurr.EnsureStackNoCache(); 
                VisitFor(nodes);
                this.iterCurr.EnsureStackNoCache();
                this.iterCurr.EnsureItemStorageType(nodes.XmlType, typeof(XPathNavigator));
                this.helper.Call(XmlILMethods.IndexAdd); 
                this.helper.Emit(OpCodes.Ldloc, locIndex);
 
                // LabelOnEnd: 
                this.iterCurr.LoopToEnd(lblOnEnd);
                EndBinding(nodes); 
                EndNestedIterator(nodes);

                // runtime.AddNewIndex(navCtxt, indexId, [build index]);
                this.helper.Call(XmlILMethods.AddNewIndex); 

                // LabelLookup: 
                // results = index.Lookup(keyValue); 
                this.helper.MarkLabel(lblLookup);
                this.helper.Emit(OpCodes.Ldloc, locIndex); 
                this.helper.Emit(OpCodes.Ldarg_2);
                this.helper.Call(XmlILMethods.IndexLookup);
                this.iterCurr.Storage = StorageDescriptor.Stack(typeof(XPathNavigator), true);
 
                this.indexId++;
 
                return true; 
            }
 
            return false;
        }

        ///  
        /// Generate code for a Let, For, or Parameter iterator.  Bind iterated value to a variable.
        ///  
        private void StartBinding(QilIterator ndIter) { 
            OptimizerPatterns patt = OptimizerPatterns.Read(ndIter);
            Debug.Assert(ndIter != null); 

            // DebugInfo: Sequence point just before generating code for the bound expression
            if (this.qil.IsDebug && ndIter.SourceLine != null)
                this.helper.DebugSequencePoint(ndIter.SourceLine); 

            // Treat cardinality one Let iterators as if they were For iterators (no nesting necessary) 
            if (ndIter.NodeType == QilNodeType.For || ndIter.XmlType.IsSingleton) { 
                StartForBinding(ndIter, patt);
            } 
            else {
                Debug.Assert(ndIter.NodeType == QilNodeType.Let || ndIter.NodeType == QilNodeType.Parameter);
                Debug.Assert(!patt.MatchesPattern(OptimizerPatternName.IsPositional));
 
                // Bind Let values (nested iterator) to variable
                StartLetBinding(ndIter); 
            } 

            // Attach IteratorDescriptor to the iterator 
            XmlILAnnotation.Write(ndIter).CachedIteratorDescriptor = this.iterNested;
        }

        ///  
        /// Bind values produced by the "ndFor" expression to a non-stack location that can later
        /// be referenced. 
        ///  
        private void StartForBinding(QilIterator ndFor, OptimizerPatterns patt) {
            LocalBuilder locPos = null; 
            Debug.Assert(ndFor.XmlType.IsSingleton);

            // For expression iterator will be unnested as part of parent iterator
            if (this.iterCurr.HasLabelNext) 
                StartNestedIterator(ndFor.Binding, this.iterCurr.GetLabelNext());
            else 
                StartNestedIterator(ndFor.Binding); 

            if (patt.MatchesPattern(OptimizerPatternName.IsPositional)) { 
                // Need to track loop index so initialize it to 0 before starting loop
                locPos = this.helper.DeclareLocal("$$$pos", typeof(int));
                this.helper.Emit(OpCodes.Ldc_I4_0);
                this.helper.Emit(OpCodes.Stloc, locPos); 
            }
 
            // Allow base internal class to dispatch based on QilExpression node type 
            Visit(ndFor.Binding);
 
            // DebugInfo: Open variable scope
            // DebugInfo: Ensure that for variable is stored in a local and tag it with the user-defined name
            if (this.qil.IsDebug && ndFor.DebugName != null) {
                this.helper.DebugStartScope(); 

                // Ensure that values are stored in a local variable with a user-defined name 
                this.iterCurr.EnsureLocalNoCache("$$$for"); 
                this.iterCurr.Storage.LocalLocation.SetLocalSymInfo(ndFor.DebugName);
            } 
            else {
                // Ensure that values are not stored on the stack
                this.iterCurr.EnsureNoStackNoCache("$$$for");
            } 

            if (patt.MatchesPattern(OptimizerPatternName.IsPositional)) { 
                // Increment position 
                this.helper.Emit(OpCodes.Ldloc, locPos);
                this.helper.Emit(OpCodes.Ldc_I4_1); 
                this.helper.Emit(OpCodes.Add);
                this.helper.Emit(OpCodes.Stloc, locPos);

                if (patt.MatchesPattern(OptimizerPatternName.MaxPosition)) { 
                    // Short-circuit rest of loop if max position has already been reached
                    this.helper.Emit(OpCodes.Ldloc, locPos); 
                    this.helper.LoadInteger((int) patt.GetArgument(OptimizerPatternArgument.MaxPosition)); 
                    this.helper.Emit(OpCodes.Bgt, this.iterCurr.ParentIterator.GetLabelNext());
                } 

                this.iterCurr.LocalPosition = locPos;
            }
 
            EndNestedIterator(ndFor.Binding);
            this.iterCurr.SetIterator(this.iterNested); 
        } 

        ///  
        /// Bind values in the "ndLet" expression to a non-stack location that can later be referenced.
        /// 
        public void StartLetBinding(QilIterator ndLet) {
            Debug.Assert(!ndLet.XmlType.IsSingleton); 

            // Construct nested iterator 
            StartNestedIterator(ndLet); 

            // Allow base internal class to dispatch based on QilExpression node type 
            NestedVisit(ndLet.Binding, GetItemStorageType(ndLet), !ndLet.XmlType.IsSingleton);

            // DebugInfo: Open variable scope
            // DebugInfo: Ensure that for variable is stored in a local and tag it with the user-defined name 
            if (this.qil.IsDebug && ndLet.DebugName != null) {
                this.helper.DebugStartScope(); 
 
                // Ensure that cache is stored in a local variable with a user-defined name
                this.iterCurr.EnsureLocal("$$$cache"); 
                this.iterCurr.Storage.LocalLocation.SetLocalSymInfo(ndLet.DebugName);
            }
            else {
                // Ensure that cache is not stored on the stack 
                this.iterCurr.EnsureNoStack("$$$cache");
            } 
 
            EndNestedIterator(ndLet);
        } 

        /// 
        /// Mark iterator variables as out-of-scope.
        ///  
        private void EndBinding(QilIterator ndIter) {
            Debug.Assert(ndIter != null); 
 
            // Variables go out of scope here
            if (this.qil.IsDebug && ndIter.DebugName != null) 
                this.helper.DebugEndScope();
        }

        ///  
        /// Generate code for QilNodeType.PositionOf.
        ///  
        protected override QilNode VisitPositionOf(QilUnary ndPos) { 
            QilIterator ndIter = ndPos.Child as QilIterator;
            LocalBuilder locPos; 
            Debug.Assert(ndIter.NodeType == QilNodeType.For);

            locPos = XmlILAnnotation.Write(ndIter).CachedIteratorDescriptor.LocalPosition;
            Debug.Assert(locPos != null); 
            this.iterCurr.Storage = StorageDescriptor.Local(locPos, typeof(int), false);
 
            return ndPos; 
        }
 
        /// 
        /// Generate code for QilNodeType.Sort.
        /// 
        protected override QilNode VisitSort(QilLoop ndSort) { 
            Type itemStorageType = GetItemStorageType(ndSort);
            LocalBuilder locCache, locKeys; 
            Label lblOnEndSort = this.helper.DefineLabel(); 
            Debug.Assert(ndSort.Variable.NodeType == QilNodeType.For);
 
            // XmlQuerySequence cache;
            // cache = XmlQuerySequence.CreateOrReuse(cache);
            XmlILStorageMethods methods = XmlILMethods.StorageMethods[itemStorageType];
            locCache = this.helper.DeclareLocal("$$$cache", methods.SeqType); 
            this.helper.Emit(OpCodes.Ldloc, locCache);
            this.helper.CallToken(methods.SeqReuse); 
            this.helper.Emit(OpCodes.Stloc, locCache); 
            this.helper.Emit(OpCodes.Ldloc, locCache);
 
            // XmlSortKeyAccumulator keys;
            // keys.Create(runtime);
            locKeys = this.helper.DeclareLocal("$$$keys", typeof(XmlSortKeyAccumulator));
            this.helper.Emit(OpCodes.Ldloca, locKeys); 
            this.helper.Call(XmlILMethods.SortKeyCreate);
 
            // Construct nested iterator 
            // foreach (item in sort-expr) {
            StartNestedIterator(ndSort.Variable, lblOnEndSort); 
            StartBinding(ndSort.Variable);
            Debug.Assert(!this.iterNested.Storage.IsCached);

            // cache.Add(item); 
            this.iterCurr.EnsureStackNoCache();
            this.iterCurr.EnsureItemStorageType(ndSort.Variable.XmlType, GetItemStorageType(ndSort.Variable)); 
            this.helper.Call(methods.SeqAdd); 

            this.helper.Emit(OpCodes.Ldloca, locKeys); 

            // Add keys to accumulator (there may be several keys)
            foreach (QilSortKey ndKey in ndSort.Body)
                VisitSortKey(ndKey, locKeys); 

            // keys.FinishSortKeys(); 
            this.helper.Call(XmlILMethods.SortKeyFinish); 

            // } 
            this.helper.Emit(OpCodes.Ldloc, locCache);
            this.iterCurr.LoopToEnd(lblOnEndSort);

            // Remove cache reference from stack 
            this.helper.Emit(OpCodes.Pop);
 
            // cache.SortByKeys(keys.Keys); 
            this.helper.Emit(OpCodes.Ldloc, locCache);
            this.helper.Emit(OpCodes.Ldloca, locKeys); 
            this.helper.Call(XmlILMethods.SortKeyKeys);
            this.helper.Call(methods.SeqSortByKeys);

            // End nested iterator 
            this.iterCurr.Storage = StorageDescriptor.Local(locCache, itemStorageType, true);
            EndBinding(ndSort.Variable); 
            EndNestedIterator(ndSort.Variable); 
            this.iterCurr.SetIterator(this.iterNested);
 
            return ndSort;
        }

        ///  
        /// Generate code to add a (value, collation) sort key to the XmlSortKeyAccumulator.
        ///  
        private void VisitSortKey(QilSortKey ndKey, LocalBuilder locKeys) { 
            Label lblOnEndKey;
            Debug.Assert(ndKey.Key.XmlType.IsAtomicValue, "Sort key must be an atomic value."); 

            // Push collation onto the stack
            this.helper.Emit(OpCodes.Ldloca, locKeys);
            if (ndKey.Collation.NodeType == QilNodeType.LiteralString) { 
                // collation = runtime.GetCollation(idx);
                this.helper.CallGetCollation(this.helper.StaticData.DeclareCollation((string) (QilLiteral) ndKey.Collation)); 
            } 
            else {
                // collation = runtime.CreateCollation(str); 
                this.helper.LoadQueryRuntime();
                NestedVisitEnsureStack(ndKey.Collation);
                this.helper.Call(XmlILMethods.CreateCollation);
            } 

            if (ndKey.XmlType.IsSingleton) { 
                NestedVisitEnsureStack(ndKey.Key); 

                // keys.AddSortKey(collation, value); 
                this.helper.AddSortKey(ndKey.Key.XmlType);
            }
            else {
                lblOnEndKey = this.helper.DefineLabel(); 
                StartNestedIterator(ndKey.Key, lblOnEndKey);
                Visit(ndKey.Key); 
                this.iterCurr.EnsureStackNoCache(); 
                this.iterCurr.EnsureItemStorageType(ndKey.Key.XmlType, GetItemStorageType(ndKey.Key));
 
                // Non-empty sort key
                // keys.AddSortKey(collation, value);
                this.helper.AddSortKey(ndKey.Key.XmlType);
 
                // goto LabelDone;
                // LabelOnEnd: 
                Label lblDone = this.helper.DefineLabel(); 
                this.helper.EmitUnconditionalBranch(OpCodes.Br_S, lblDone);
                this.helper.MarkLabel(lblOnEndKey); 

                // Empty sequence key
                // keys.AddSortKey(collation);
                this.helper.AddSortKey(null); 

                this.helper.MarkLabel(lblDone); 
 
                EndNestedIterator(ndKey.Key);
            } 
        }

        /// 
        /// Generate code for for QilNodeType.DocOrderDistinct. 
        /// 
        protected override QilNode VisitDocOrderDistinct(QilUnary ndDod) { 
            // DocOrderDistinct applied to a singleton is a no-op 
            if (ndDod.XmlType.IsSingleton)
                return Visit(ndDod.Child); 

            // Handle any special-case patterns that are rooted at DocOrderDistinct
            if (HandleDodPatterns(ndDod))
                return ndDod; 

            // Sort results of child expression by document order and remove duplicate nodes 
            // cache = runtime.DocOrderDistinct(cache); 
            this.helper.LoadQueryRuntime();
            NestedVisitEnsureCache(ndDod.Child, typeof(XPathNavigator)); 
            this.iterCurr.EnsureStack();
            this.helper.Call(XmlILMethods.DocOrder);
            return ndDod;
        } 

        ///  
        /// There are a number of path patterns that can be rooted at DocOrderDistinct nodes.  Determine whether one of these 
        /// patterns has been previously matched on "ndDod".  If so, generate code for the pattern and return true.  Otherwise,
        /// just return false. 
        /// 
        private bool HandleDodPatterns(QilUnary ndDod) {
            OptimizerPatterns pattDod = OptimizerPatterns.Read(ndDod);
            XmlNodeKindFlags kinds; 
            QilName name;
            QilNode input, step; 
            bool isJoinAndDod; 

            // Handle JoinAndDod and DodReverse patterns 
            isJoinAndDod = pattDod.MatchesPattern(OptimizerPatternName.JoinAndDod);
            if (isJoinAndDod || pattDod.MatchesPattern(OptimizerPatternName.DodReverse)) {
                OptimizerPatterns pattStep = OptimizerPatterns.Read((QilNode) pattDod.GetArgument(OptimizerPatternArgument.DodStep));
 
                if (pattStep.MatchesPattern(OptimizerPatternName.FilterElements)) {
                    // FilterElements pattern, so Kind = Element and Name = Argument 
                    kinds = XmlNodeKindFlags.Element; 
                    name = (QilName) pattStep.GetArgument(OptimizerPatternArgument.ElementQName);
                } 
                else if (pattStep.MatchesPattern(OptimizerPatternName.FilterContentKind)) {
                    // FilterKindTest pattern, so Kind = Argument and Name = null
                    kinds = ((XmlQueryType) pattStep.GetArgument(OptimizerPatternArgument.KindTestType)).NodeKinds;
                    name = null; 
                }
                else { 
                    Debug.Assert(pattStep.MatchesPattern(OptimizerPatternName.Axis), "Dod patterns should only match if step is FilterElements or FilterKindTest or Axis"); 
                    kinds = ((ndDod.XmlType.NodeKinds & XmlNodeKindFlags.Attribute) != 0) ? XmlNodeKindFlags.Any : XmlNodeKindFlags.Content;
                    name = null; 
                }

                step = (QilNode) pattStep.GetArgument(OptimizerPatternArgument.StepNode);
                if (isJoinAndDod) { 
                    switch (step.NodeType) {
                        case QilNodeType.Content: 
                            CreateContainerIterator(ndDod, "$$$iterContent", typeof(ContentMergeIterator), XmlILMethods.ContentMergeCreate, XmlILMethods.ContentMergeNext, 
                                                    kinds, name, TriState.Unknown);
                            return true; 

                        case QilNodeType.Descendant:
                        case QilNodeType.DescendantOrSelf:
                            CreateContainerIterator(ndDod, "$$$iterDesc", typeof(DescendantMergeIterator), XmlILMethods.DescMergeCreate, XmlILMethods.DescMergeNext, 
                                                    kinds, name, (step.NodeType == QilNodeType.Descendant) ? TriState.False : TriState.True);
                            return true; 
 
                        case QilNodeType.XPathFollowing:
                            CreateContainerIterator(ndDod, "$$$iterFoll", typeof(XPathFollowingMergeIterator), XmlILMethods.XPFollMergeCreate, XmlILMethods.XPFollMergeNext, 
                                                    kinds, name, TriState.Unknown);
                            return true;

                        case QilNodeType.FollowingSibling: 
                            CreateContainerIterator(ndDod, "$$$iterFollSib", typeof(FollowingSiblingMergeIterator), XmlILMethods.FollSibMergeCreate, XmlILMethods.FollSibMergeNext,
                                                    kinds, name, TriState.Unknown); 
                            return true; 

                        case QilNodeType.XPathPreceding: 
                            CreateContainerIterator(ndDod, "$$$iterPrec", typeof(XPathPrecedingMergeIterator), XmlILMethods.XPPrecMergeCreate, XmlILMethods.XPPrecMergeNext,
                                                    kinds, name, TriState.Unknown);
                            return true;
 
                        default:
                            Debug.Assert(false, "Pattern " + step.NodeType + " should have been handled."); 
                            break; 
                    }
                } 
                else {
                    input = (QilNode) pattStep.GetArgument(OptimizerPatternArgument.StepInput);
                    switch (step.NodeType) {
                        case QilNodeType.Ancestor: 
                        case QilNodeType.AncestorOrSelf:
                            CreateFilteredIterator(input, "$$$iterAnc", typeof(AncestorDocOrderIterator), XmlILMethods.AncDOCreate, XmlILMethods.AncDONext, 
                                                   kinds, name, (step.NodeType == QilNodeType.Ancestor) ? TriState.False : TriState.True, null); 
                            return true;
 
                        case QilNodeType.PrecedingSibling:
                            CreateFilteredIterator(input, "$$$iterPreSib", typeof(PrecedingSiblingDocOrderIterator), XmlILMethods.PreSibDOCreate, XmlILMethods.PreSibDONext,
                                                   kinds, name, TriState.Unknown, null);
                            return true; 

                        case QilNodeType.XPathPreceding: 
                            CreateFilteredIterator(input, "$$$iterPrec", typeof(XPathPrecedingDocOrderIterator), XmlILMethods.XPPrecDOCreate, XmlILMethods.XPPrecDONext, 
                                                   kinds, name, TriState.Unknown, null);
                            return true; 

                        default:
                            Debug.Assert(false, "Pattern " + step.NodeType + " should have been handled.");
                            break; 
                    }
                } 
            } 
            else if (pattDod.MatchesPattern(OptimizerPatternName.DodMerge)) {
                // DodSequenceMerge dodMerge; 
                LocalBuilder locMerge = this.helper.DeclareLocal("$$$dodMerge", typeof(DodSequenceMerge));
                Label lblOnEnd = this.helper.DefineLabel();

                // dodMerge.Create(runtime); 
                this.helper.Emit(OpCodes.Ldloca, locMerge);
                this.helper.LoadQueryRuntime(); 
                this.helper.Call(XmlILMethods.DodMergeCreate); 
                this.helper.Emit(OpCodes.Ldloca, locMerge);
 
                StartNestedIterator(ndDod.Child, lblOnEnd);

                // foreach (seq in expr) {
                Visit(ndDod.Child); 

                // dodMerge.AddSequence(seq); 
                Debug.Assert(this.iterCurr.Storage.IsCached, "DodMerge pattern should only be matched when cached sequences are returned from loop"); 
                this.iterCurr.EnsureStack();
                this.helper.Call(XmlILMethods.DodMergeAdd); 
                this.helper.Emit(OpCodes.Ldloca, locMerge);

                // }
                this.iterCurr.LoopToEnd(lblOnEnd); 

                EndNestedIterator(ndDod.Child); 
 
                // mergedSequence = dodMerge.MergeSequences();
                this.helper.Call(XmlILMethods.DodMergeSeq); 

                this.iterCurr.Storage = StorageDescriptor.Stack(typeof(XPathNavigator), true);

                return true; 
            }
 
            return false; 
        }
 
        /// 
        /// Generate code for for QilNodeType.Invoke.
        /// 
        protected override QilNode VisitInvoke(QilInvoke ndInvoke) { 
            QilFunction ndFunc = ndInvoke.Function;
            MethodInfo methInfo = XmlILAnnotation.Write(ndFunc).FunctionBinding; 
            bool useWriter = (XmlILConstructInfo.Read(ndFunc).ConstructMethod == XmlILConstructMethod.Writer); 
            Debug.Assert(!XmlILConstructInfo.Read(ndInvoke).PushToWriterFirst || useWriter);
 
            // Push XmlQueryRuntime onto the stack as the first parameter
            this.helper.LoadQueryRuntime();

            // Generate code to push each Invoke argument onto the stack 
            for (int iArg = 0; iArg < ndInvoke.Arguments.Count; iArg++) {
                QilNode ndActualArg = ndInvoke.Arguments[iArg]; 
                QilNode ndFormalArg = ndInvoke.Function.Arguments[iArg]; 
                NestedVisitEnsureStack(ndActualArg, GetItemStorageType(ndFormalArg), !ndFormalArg.XmlType.IsSingleton);
            } 

            // Check whether this call should compiled using the .tailcall instruction
            if (OptimizerPatterns.Read(ndInvoke).MatchesPattern(OptimizerPatternName.TailCall))
                this.helper.TailCall(methInfo); 
            else
                this.helper.Call(methInfo); 
 
            // If function's results are not pushed to Writer,
            if (!useWriter) { 
                // Return value is on the stack; ensure it has the correct storage type
                this.iterCurr.Storage = StorageDescriptor.Stack(GetItemStorageType(ndInvoke), !ndInvoke.XmlType.IsSingleton);
            }
            else { 
                this.iterCurr.Storage = StorageDescriptor.None();
            } 
 
            return ndInvoke;
        } 

        /// 
        /// Generate code for for QilNodeType.Content.
        ///  
        protected override QilNode VisitContent(QilUnary ndContent) {
            CreateSimpleIterator(ndContent.Child, "$$$iterAttrContent", typeof(AttributeContentIterator), XmlILMethods.AttrContentCreate, XmlILMethods.AttrContentNext); 
            return ndContent; 
        }
 
        /// 
        /// Generate code for for QilNodeType.Attribute.
        /// 
        protected override QilNode VisitAttribute(QilBinary ndAttr) { 
            QilName ndName = ndAttr.Right as QilName;
            Debug.Assert(ndName != null, "Attribute node must have a literal QName as its second argument"); 
 
            // XPathNavigator navAttr;
            LocalBuilder locNav = this.helper.DeclareLocal("$$$navAttr", typeof(XPathNavigator)); 

            // navAttr = SyncToNavigator(navAttr, navCtxt);
            SyncToNavigator(locNav, ndAttr.Left);
 
            // if (!navAttr.MoveToAttribute(localName, namespaceUri)) goto LabelNextCtxt;
            this.helper.Emit(OpCodes.Ldloc, locNav); 
            this.helper.CallGetAtomizedName(this.helper.StaticData.DeclareName(ndName.LocalName)); 
            this.helper.CallGetAtomizedName(this.helper.StaticData.DeclareName(ndName.NamespaceUri));
            this.helper.Call(XmlILMethods.NavMoveAttr); 
            this.helper.Emit(OpCodes.Brfalse, this.iterCurr.GetLabelNext());

            this.iterCurr.Storage = StorageDescriptor.Local(locNav, typeof(XPathNavigator), false);
            return ndAttr; 
        }
 
        ///  
        /// Generate code for for QilNodeType.Parent.
        ///  
        protected override QilNode VisitParent(QilUnary ndParent) {
            // XPathNavigator navParent;
            LocalBuilder locNav = this.helper.DeclareLocal("$$$navParent", typeof(XPathNavigator));
 
            // navParent = SyncToNavigator(navParent, navCtxt);
            SyncToNavigator(locNav, ndParent.Child); 
 
            // if (!navParent.MoveToParent()) goto LabelNextCtxt;
            this.helper.Emit(OpCodes.Ldloc, locNav); 
            this.helper.Call(XmlILMethods.NavMoveParent);
            this.helper.Emit(OpCodes.Brfalse, this.iterCurr.GetLabelNext());

            this.iterCurr.Storage = StorageDescriptor.Local(locNav, typeof(XPathNavigator), false); 
            return ndParent;
        } 
 
        /// 
        /// Generate code for for QilNodeType.Root. 
        /// 
        protected override QilNode VisitRoot(QilUnary ndRoot) {
            // XPathNavigator navRoot;
            LocalBuilder locNav = this.helper.DeclareLocal("$$$navRoot", typeof(XPathNavigator)); 

            // navRoot = SyncToNavigator(navRoot, navCtxt); 
            SyncToNavigator(locNav, ndRoot.Child); 

            // navRoot.MoveToRoot(); 
            this.helper.Emit(OpCodes.Ldloc, locNav);
            this.helper.Call(XmlILMethods.NavMoveRoot);

            this.iterCurr.Storage = StorageDescriptor.Local(locNav, typeof(XPathNavigator), false); 
            return ndRoot;
        } 
 
        /// 
        /// Generate code for QilNodeType.XmlContext. 
        /// 
        /// 
        /// Generates code to retrieve the default document using the XmlResolver.
        ///  
        protected override QilNode VisitXmlContext(QilNode ndCtxt) {
            // runtime.ExternalContext.DefaultDataSource 
            this.helper.LoadQueryContext(); 
            this.helper.Call(XmlILMethods.GetDefaultDataSource);
            this.iterCurr.Storage = StorageDescriptor.Stack(typeof(XPathNavigator), false); 
            return ndCtxt;
        }

        ///  
        /// Find physical query plan for QilNodeType.Descendant.
        ///  
        protected override QilNode VisitDescendant(QilUnary ndDesc) { 
            CreateFilteredIterator(ndDesc.Child, "$$$iterDesc", typeof(DescendantIterator), XmlILMethods.DescCreate, XmlILMethods.DescNext,
                                   XmlNodeKindFlags.Any, null, TriState.False, null); 
            return ndDesc;
        }

        ///  
        /// Generate code for for QilNodeType.DescendantOrSelf.
        ///  
        protected override QilNode VisitDescendantOrSelf(QilUnary ndDesc) { 
            CreateFilteredIterator(ndDesc.Child, "$$$iterDesc", typeof(DescendantIterator), XmlILMethods.DescCreate, XmlILMethods.DescNext,
                                   XmlNodeKindFlags.Any, null, TriState.True, null); 
            return ndDesc;
        }

        ///  
        /// Find physical query plan for QilNodeType.Ancestor.
        ///  
        protected override QilNode VisitAncestor(QilUnary ndAnc) { 
            CreateFilteredIterator(ndAnc.Child, "$$$iterAnc", typeof(AncestorIterator), XmlILMethods.AncCreate, XmlILMethods.AncNext,
                                   XmlNodeKindFlags.Any, null, TriState.False, null); 
            return ndAnc;
        }

        ///  
        /// Find physical query plan for QilNodeType.AncestorOrSelf.
        ///  
        protected override QilNode VisitAncestorOrSelf(QilUnary ndAnc) { 
            CreateFilteredIterator(ndAnc.Child, "$$$iterAnc", typeof(AncestorIterator), XmlILMethods.AncCreate, XmlILMethods.AncNext,
                                   XmlNodeKindFlags.Any, null, TriState.True, null); 
            return ndAnc;
        }

        ///  
        /// Find physical query plan for QilNodeType.Preceding.
        ///  
        protected override QilNode VisitPreceding(QilUnary ndPrec) { 
            CreateFilteredIterator(ndPrec.Child, "$$$iterPrec", typeof(PrecedingIterator), XmlILMethods.PrecCreate, XmlILMethods.PrecNext,
                                   XmlNodeKindFlags.Any, null, TriState.Unknown, null); 
            return ndPrec;
        }

        ///  
        /// Find physical query plan for QilNodeType.FollowingSibling.
        ///  
        protected override QilNode VisitFollowingSibling(QilUnary ndFollSib) { 
            CreateFilteredIterator(ndFollSib.Child, "$$$iterFollSib", typeof(FollowingSiblingIterator), XmlILMethods.FollSibCreate, XmlILMethods.FollSibNext,
                                   XmlNodeKindFlags.Any, null, TriState.Unknown, null); 
            return ndFollSib;
        }

        ///  
        /// Find physical query plan for QilNodeType.PrecedingSibling.
        ///  
        protected override QilNode VisitPrecedingSibling(QilUnary ndPreSib) { 
            CreateFilteredIterator(ndPreSib.Child, "$$$iterPreSib", typeof(PrecedingSiblingIterator), XmlILMethods.PreSibCreate, XmlILMethods.PreSibNext,
                                   XmlNodeKindFlags.Any, null, TriState.Unknown, null); 
            return ndPreSib;
        }

        ///  
        /// Find physical query plan for QilNodeType.NodeRange.
        ///  
        protected override QilNode VisitNodeRange(QilBinary ndRange) { 
            CreateFilteredIterator(ndRange.Left, "$$$iterRange", typeof(NodeRangeIterator), XmlILMethods.NodeRangeCreate, XmlILMethods.NodeRangeNext,
                                   XmlNodeKindFlags.Any, null, TriState.Unknown, ndRange.Right); 
            return ndRange;
        }

        ///  
        /// Generate code for for QilNodeType.Deref.
        ///  
        protected override QilNode VisitDeref(QilBinary ndDeref) { 
            // IdIterator iterId;
            LocalBuilder locIter = this.helper.DeclareLocal("$$$iterId", typeof(IdIterator)); 

            // iterId.Create(navCtxt, value);
            this.helper.Emit(OpCodes.Ldloca, locIter);
            NestedVisitEnsureStack(ndDeref.Left); 
            NestedVisitEnsureStack(ndDeref.Right);
            this.helper.Call(XmlILMethods.IdCreate); 
 
            GenerateSimpleIterator(typeof(XPathNavigator), locIter, XmlILMethods.IdNext);
 
            return ndDeref;
        }

        ///  
        /// Generate code for QilNodeType.ElementCtor.
        ///  
        protected override QilNode VisitElementCtor(QilBinary ndElem) { 
            XmlILConstructInfo info = XmlILConstructInfo.Read(ndElem);
            bool callChk; 
            GenerateNameType nameType;
            Debug.Assert(XmlILConstructInfo.Read(ndElem).PushToWriterFirst, "Element contruction should always be pushed to writer.");

            // Runtime checks must be made in the following cases: 
            //   1. Xml state is not known at compile-time, or is illegal
            //   2. Element's namespace must be declared 
            //   3. Element's attributes might be duplicates of one another, or namespaces might follow attributes 
            callChk = CheckWithinContent(info) || !info.IsNamespaceInScope || ElementCachesAttributes(info);
 
            // If it is not known whether element content was output, then make this check at run-time
            if (XmlILConstructInfo.Read(ndElem.Right).FinalStates == PossibleXmlStates.Any)
                callChk = true;
 
            // If runtime state after EndElement is called is not known, then call XmlQueryOutput.WriteEndElementChk
            if (info.FinalStates == PossibleXmlStates.Any) 
                callChk = true; 

            // If WriteStartElementChk will *not* be called, then code must be generated to ensure valid state transitions 
            if (!callChk)
                BeforeStartChecks(ndElem);

            // Generate call to WriteStartElement 
            nameType = LoadNameAndType(XPathNodeType.Element, ndElem.Left, true, callChk);
            this.helper.CallWriteStartElement(nameType, callChk); 
 
            // Recursively construct content
            NestedVisit(ndElem.Right); 

            // If runtime state is guaranteed to be EnumAttrs, and an element is being constructed, call XmlQueryOutput.StartElementContent
            if (XmlILConstructInfo.Read(ndElem.Right).FinalStates == PossibleXmlStates.EnumAttrs && !callChk)
                this.helper.CallStartElementContent(); 

            // Generate call to WriteEndElement 
            nameType = LoadNameAndType(XPathNodeType.Element, ndElem.Left, false, callChk); 
            this.helper.CallWriteEndElement(nameType, callChk);
 
            if (!callChk)
                AfterEndChecks(ndElem);

            this.iterCurr.Storage = StorageDescriptor.None(); 
            return ndElem;
        } 
 
        /// 
        /// Generate code for QilNodeType.AttributeCtor. 
        /// 
        protected override QilNode VisitAttributeCtor(QilBinary ndAttr) {
            XmlILConstructInfo info = XmlILConstructInfo.Read(ndAttr);
            bool callChk; 
            GenerateNameType nameType;
            Debug.Assert(XmlILConstructInfo.Read(ndAttr).PushToWriterFirst, "Attribute construction should always be pushed to writer."); 
 
            // Runtime checks must be made in the following cases:
            //   1. Xml state is not known at compile-time, or is illegal 
            //   2. Attribute's namespace must be declared
            callChk = CheckEnumAttrs(info) || !info.IsNamespaceInScope;

            // If WriteStartAttributeChk will *not* be called, then code must be generated to ensure well-formedness 
            // and track namespace scope.
            if (!callChk) 
                BeforeStartChecks(ndAttr); 

            // Generate call to WriteStartAttribute 
            nameType = LoadNameAndType(XPathNodeType.Attribute, ndAttr.Left, true, callChk);
            this.helper.CallWriteStartAttribute(nameType, callChk);

            // Recursively construct content 
            NestedVisit(ndAttr.Right);
 
            // Generate call to WriteEndAttribute 
            this.helper.CallWriteEndAttribute(callChk);
 
            if (!callChk)
                AfterEndChecks(ndAttr);

            this.iterCurr.Storage = StorageDescriptor.None(); 
            return ndAttr;
        } 
 
        /// 
        /// Generate code for QilNodeType.CommentCtor. 
        /// 
        protected override QilNode VisitCommentCtor(QilUnary ndComment) {
            Debug.Assert(XmlILConstructInfo.Read(ndComment).PushToWriterFirst, "Comment construction should always be pushed to writer.");
 
            // Always call XmlQueryOutput.WriteStartComment
            this.helper.CallWriteStartComment(); 
 
            // Recursively construct content
            NestedVisit(ndComment.Child); 

            // Always call XmlQueryOutput.WriteEndComment
            this.helper.CallWriteEndComment();
 
            this.iterCurr.Storage = StorageDescriptor.None();
            return ndComment; 
        } 

        ///  
        /// Generate code for QilNodeType.PICtor.
        /// 
        protected override QilNode VisitPICtor(QilBinary ndPI) {
            Debug.Assert(XmlILConstructInfo.Read(ndPI).PushToWriterFirst, "PI construction should always be pushed to writer."); 

            // Always call XmlQueryOutput.WriteStartPI 
            this.helper.LoadQueryOutput(); 
            NestedVisitEnsureStack(ndPI.Left);
            this.helper.CallWriteStartPI(); 

            // Recursively construct content
            NestedVisit(ndPI.Right);
 
            // Always call XmlQueryOutput.WriteEndPI
            this.helper.CallWriteEndPI(); 
 
            this.iterCurr.Storage = StorageDescriptor.None();
            return ndPI; 
        }

        /// 
        /// Generate code for QilNodeType.TextCtor. 
        /// 
        protected override QilNode VisitTextCtor(QilUnary ndText) { 
            return VisitTextCtor(ndText, false); 
        }
 
        /// 
        /// Generate code for QilNodeType.RawTextCtor.
        /// 
        protected override QilNode VisitRawTextCtor(QilUnary ndText) { 
            return VisitTextCtor(ndText, true);
        } 
 
        /// 
        /// Generate code for QilNodeType.TextCtor and QilNodeType.RawTextCtor. 
        /// 
        private QilNode VisitTextCtor(QilUnary ndText, bool disableOutputEscaping) {
            XmlILConstructInfo info = XmlILConstructInfo.Read(ndText);
            bool callChk; 
            Debug.Assert(info.PushToWriterFirst, "Text construction should always be pushed to writer.");
 
            // Write out text in different contexts (within attribute, within element, within comment, etc.) 
            switch (info.InitialStates) {
                case PossibleXmlStates.WithinAttr: 
                case PossibleXmlStates.WithinComment:
                case PossibleXmlStates.WithinPI:
                    callChk = false;
                    break; 

                default: 
                    callChk = CheckWithinContent(info); 
                    break;
            } 

            if (!callChk)
                BeforeStartChecks(ndText);
 
            this.helper.LoadQueryOutput();
 
            // Push string value of text onto IL stack 
            NestedVisitEnsureStack(ndText.Child);
 
            // Write out text in different contexts (within attribute, within element, within comment, etc.)
            switch (info.InitialStates) {
                case PossibleXmlStates.WithinAttr:
                    // Ignore hints when writing out attribute text 
                    this.helper.CallWriteString(false, callChk);
                    break; 
 
                case PossibleXmlStates.WithinComment:
                    // Call XmlQueryOutput.WriteCommentString 
                    this.helper.Call(XmlILMethods.CommentText);
                    break;

                case PossibleXmlStates.WithinPI: 
                    // Call XmlQueryOutput.WriteProcessingInstructionString
                    this.helper.Call(XmlILMethods.PIText); 
                    break; 

                default: 
                    // Call XmlQueryOutput.WriteTextBlockChk, XmlQueryOutput.WriteTextBlockNoEntities, or XmlQueryOutput.WriteTextBlock
                    this.helper.CallWriteString(disableOutputEscaping, callChk);
                    break;
            } 

            if (!callChk) 
                AfterEndChecks(ndText); 

            this.iterCurr.Storage = StorageDescriptor.None(); 
            return ndText;
        }

        ///  
        /// Generate code for QilNodeType.DocumentCtor.
        ///  
        protected override QilNode VisitDocumentCtor(QilUnary ndDoc) { 
            Debug.Assert(XmlILConstructInfo.Read(ndDoc).PushToWriterFirst, "Document root construction should always be pushed to writer.");
 
            // Generate call to XmlQueryOutput.WriteStartRootChk
            this.helper.CallWriteStartRoot();

            // Recursively construct content 
            NestedVisit(ndDoc.Child);
 
            // Generate call to XmlQueryOutput.WriteEndRootChk 
            this.helper.CallWriteEndRoot();
 
            this.iterCurr.Storage = StorageDescriptor.None();

            return ndDoc;
        } 

        ///  
        /// Generate code for QilNodeType.NamespaceDecl. 
        /// 
        protected override QilNode VisitNamespaceDecl(QilBinary ndNmsp) { 
            XmlILConstructInfo info = XmlILConstructInfo.Read(ndNmsp);
            bool callChk;
            Debug.Assert(info.PushToWriterFirst, "Namespace construction should always be pushed to writer.");
 
            // Runtime checks must be made in the following cases:
            //   1. Xml state is not known at compile-time, or is illegal 
            //   2. Namespaces might be added to element after attributes have already been added 
            callChk = CheckEnumAttrs(info) || MightHaveNamespacesAfterAttributes(info);
 
            // If WriteNamespaceDeclarationChk will *not* be called, then code must be generated to ensure well-formedness
            // and track namespace scope.
            if (!callChk)
                BeforeStartChecks(ndNmsp); 

            this.helper.LoadQueryOutput(); 
 
            // Recursively construct prefix and ns
            NestedVisitEnsureStack(ndNmsp.Left); 
            NestedVisitEnsureStack(ndNmsp.Right);

            // Generate call to WriteNamespaceDecl
            this.helper.CallWriteNamespaceDecl(callChk); 

            if (!callChk) 
                AfterEndChecks(ndNmsp); 

            this.iterCurr.Storage = StorageDescriptor.None(); 
            return ndNmsp;
        }

        ///  
        /// Generate code for for QilNodeType.RtfCtor.
        ///  
        protected override QilNode VisitRtfCtor(QilBinary ndRtf) { 
            OptimizerPatterns patt = OptimizerPatterns.Read(ndRtf);
            string baseUri = (string) (QilLiteral) ndRtf.Right; 

            if (patt.MatchesPattern(OptimizerPatternName.SingleTextRtf)) {
                // Special-case Rtf containing a root node and a single text node child
                this.helper.LoadQueryRuntime(); 
                NestedVisitEnsureStack((QilNode) patt.GetArgument(OptimizerPatternArgument.RtfText));
                this.helper.Emit(OpCodes.Ldstr, baseUri); 
                this.helper.Call(XmlILMethods.RtfConstr); 
            }
            else { 
                // Start nested construction of an Rtf
                this.helper.CallStartRtfConstruction(baseUri);

                // Write content of Rtf to writer 
                NestedVisit(ndRtf.Left);
 
                // Get the result Rtf 
                this.helper.CallEndRtfConstruction();
            } 

            this.iterCurr.Storage = StorageDescriptor.Stack(typeof(XPathNavigator), false);
            return ndRtf;
        } 

        ///  
        /// Generate code for QilNodeType.NameOf. 
        /// 
        protected override QilNode VisitNameOf(QilUnary ndName) { 
            return VisitNodeProperty(ndName);
        }

        ///  
        /// Generate code for QilNodeType.LocalNameOf.
        ///  
        protected override QilNode VisitLocalNameOf(QilUnary ndName) { 
            return VisitNodeProperty(ndName);
        } 

        /// 
        /// Generate code for QilNodeType.NamespaceUriOf.
        ///  
        protected override QilNode VisitNamespaceUriOf(QilUnary ndName) {
            return VisitNodeProperty(ndName); 
        } 

        ///  
        /// Generate code for QilNodeType.PrefixOf.
        /// 
        protected override QilNode VisitPrefixOf(QilUnary ndName) {
            return VisitNodeProperty(ndName); 
        }
 
        ///  
        /// Generate code to push the local name, namespace uri, or qname of the context navigator.
        ///  
        private QilNode VisitNodeProperty(QilUnary ndProp) {
            // Generate code to push argument onto stack
            NestedVisitEnsureStack(ndProp.Child);
 
            switch (ndProp.NodeType) {
                case QilNodeType.NameOf: 
                    // push new XmlQualifiedName(navigator.LocalName, navigator.NamespaceURI); 
                    this.helper.Emit(OpCodes.Dup);
                    this.helper.Call(XmlILMethods.NavLocalName); 
                    this.helper.Call(XmlILMethods.NavNmsp);
                    this.helper.Construct(XmlILConstructors.QName);
                    this.iterCurr.Storage = StorageDescriptor.Stack(typeof(XmlQualifiedName), false);
                    break; 

                case QilNodeType.LocalNameOf: 
                    // push navigator.Name; 
                    this.helper.Call(XmlILMethods.NavLocalName);
                    this.iterCurr.Storage = StorageDescriptor.Stack(typeof(string), false); 
                    break;

                case QilNodeType.NamespaceUriOf:
                    // push navigator.NamespaceURI; 
                    this.helper.Call(XmlILMethods.NavNmsp);
                    this.iterCurr.Storage = StorageDescriptor.Stack(typeof(string), false); 
                    break; 

                case QilNodeType.PrefixOf: 
                    // push navigator.Prefix;
                    this.helper.Call(XmlILMethods.NavPrefix);
                    this.iterCurr.Storage = StorageDescriptor.Stack(typeof(string), false);
                    break; 

                default: 
                    Debug.Assert(false); 
                    break;
            } 

            return ndProp;
        }
 
        /// 
        /// Find physical query plan for QilNodeType.TypeAssert. 
        ///  
        protected override QilNode VisitTypeAssert(QilTargetType ndTypeAssert) {
            if (!ndTypeAssert.Source.XmlType.IsSingleton && ndTypeAssert.XmlType.IsSingleton && !this.iterCurr.HasLabelNext) { 
                // This case occurs when a non-singleton expression is treated as cardinality One.
                // The trouble is that the expression will branch to an end label when it's done iterating, so
                // an end label must be provided.  But there is no next label in the current iteration context,
                // so we've got to create a dummy label instead (IL requires it).  This creates an infinite loop, 
                // but since it's known statically that the expression is cardinality One, this branch will never
                // be taken. 
                Label lblDummy = this.helper.DefineLabel(); 
                this.helper.MarkLabel(lblDummy);
                NestedVisit(ndTypeAssert.Source, lblDummy); 
            }
            else {
                // Generate code for child expression
                Visit(ndTypeAssert.Source); 
            }
 
            this.iterCurr.EnsureItemStorageType(ndTypeAssert.Source.XmlType, GetItemStorageType(ndTypeAssert)); 
            return ndTypeAssert;
        } 

        /// 
        /// Generate code for QilNodeType.IsType.
        ///  
        protected override QilNode VisitIsType(QilTargetType ndIsType) {
            XmlQueryType typDerived, typBase; 
            XmlTypeCode codeBase; 

            typDerived = ndIsType.Source.XmlType; 
            typBase = ndIsType.TargetType;
            Debug.Assert(!typDerived.NeverSubtypeOf(typBase), "Normalizer should have eliminated IsType where source can never be a subtype of destination type.");

            // Special Case: Test whether singleton item is a Node 
            if (typDerived.IsSingleton && (object) typBase == (object) TypeFactory.Node) {
                NestedVisitEnsureStack(ndIsType.Source); 
                Debug.Assert(this.iterCurr.Storage.ItemStorageType == typeof(XPathItem), "If !IsNode, then storage type should be Item"); 

                // if (item.IsNode op true) goto LabelBranch; 
                this.helper.Call(XmlILMethods.ItemIsNode);
                ZeroCompare(QilNodeType.Ne, true);

                return ndIsType; 
            }
 
            // Special Case: Source value is a singleton Node, and we're testing whether it is an Element, Attribute, PI, etc. 
            if (MatchesNodeKinds(ndIsType, typDerived, typBase))
                return ndIsType; 

            // Special Case: XmlTypeCode is sufficient to describe destination type
            if ((object) typBase == (object) TypeFactory.Double) codeBase = XmlTypeCode.Double;
            else if ((object) typBase == (object) TypeFactory.String) codeBase = XmlTypeCode.String; 
            else if ((object) typBase == (object) TypeFactory.Boolean) codeBase = XmlTypeCode.Boolean;
            else if ((object) typBase == (object) TypeFactory.Node) codeBase = XmlTypeCode.Node; 
            else codeBase = XmlTypeCode.None; 

            if (codeBase != XmlTypeCode.None) { 
                // if (runtime.MatchesXmlType(value, code) op true) goto LabelBranch;
                this.helper.LoadQueryRuntime();
                NestedVisitEnsureStack(ndIsType.Source, typeof(XPathItem), !typDerived.IsSingleton);
                this.helper.LoadInteger((int) codeBase); 
                this.helper.Call(typDerived.IsSingleton ? XmlILMethods.ItemMatchesCode : XmlILMethods.SeqMatchesCode);
                ZeroCompare(QilNodeType.Ne, true); 
 
                return ndIsType;
            } 

            // if (runtime.MatchesXmlType(value, idxType) op true) goto LabelBranch;
            this.helper.LoadQueryRuntime();
            NestedVisitEnsureStack(ndIsType.Source, typeof(XPathItem), !typDerived.IsSingleton); 
            this.helper.LoadInteger(this.helper.StaticData.DeclareXmlType(typBase));
            this.helper.Call(typDerived.IsSingleton ? XmlILMethods.ItemMatchesType : XmlILMethods.SeqMatchesType); 
            ZeroCompare(QilNodeType.Ne, true); 

            return ndIsType; 
        }

        /// 
        /// Faster code can be generated if type test is just a node kind test.  If this special case is detected, then generate code and return true. 
        /// Otherwise, return false, and a call to MatchesXmlType will be generated instead.
        ///  
        private bool MatchesNodeKinds(QilTargetType ndIsType, XmlQueryType typDerived, XmlQueryType typBase) { 
            XmlNodeKindFlags kinds;
            bool allowKinds = true; 
            XPathNodeType kindsRuntime;
            int kindsUnion;

            // If not checking whether typDerived is some kind of singleton node, then fallback to MatchesXmlType 
            if (!typBase.IsNode || !typBase.IsSingleton)
                return false; 
 
            // If typDerived is not statically guaranteed to be a singleton node (and not an rtf), then fallback to MatchesXmlType
            if (!typDerived.IsNode || !typDerived.IsSingleton || !typDerived.IsNotRtf) 
                return false;

            // Now we are guaranteed that typDerived is a node, and typBase is a node, so check node kinds
            // Ensure that typBase is only composed of kind-test prime types (no name-test, no schema-test, etc.) 
            kinds = XmlNodeKindFlags.None;
            foreach (XmlQueryType typItem in typBase) { 
                if ((object) typItem == (object) TypeFactory.Element) kinds |= XmlNodeKindFlags.Element; 
                else if ((object) typItem == (object) TypeFactory.Attribute) kinds |= XmlNodeKindFlags.Attribute;
                else if ((object) typItem == (object) TypeFactory.Text) kinds |= XmlNodeKindFlags.Text; 
                else if ((object) typItem == (object) TypeFactory.Document) kinds |= XmlNodeKindFlags.Document;
                else if ((object) typItem == (object) TypeFactory.Comment) kinds |= XmlNodeKindFlags.Comment;
                else if ((object) typItem == (object) TypeFactory.PI) kinds |= XmlNodeKindFlags.PI;
                else if ((object) typItem == (object) TypeFactory.Namespace) kinds |= XmlNodeKindFlags.Namespace; 
                else return false;
            } 
 
            Debug.Assert((typDerived.NodeKinds & kinds) != XmlNodeKindFlags.None, "Normalizer should have taken care of case where node kinds are disjoint.");
 
            kinds = typDerived.NodeKinds & kinds;

            // Attempt to allow or disallow exactly one kind
            if (!Bits.ExactlyOne((uint) kinds)) { 
                // Not possible to allow one kind, so try to disallow one kind
                kinds = ~kinds & XmlNodeKindFlags.Any; 
                allowKinds = !allowKinds; 
            }
 
            switch (kinds) {
                case XmlNodeKindFlags.Element: kindsRuntime = XPathNodeType.Element; break;
                case XmlNodeKindFlags.Attribute: kindsRuntime = XPathNodeType.Attribute; break;
                case XmlNodeKindFlags.Namespace: kindsRuntime = XPathNodeType.Namespace; break; 
                case XmlNodeKindFlags.PI: kindsRuntime = XPathNodeType.ProcessingInstruction; break;
                case XmlNodeKindFlags.Comment: kindsRuntime = XPathNodeType.Comment; break; 
                case XmlNodeKindFlags.Document: kindsRuntime = XPathNodeType.Root; break; 

                default: 
                    // Union of several types (when testing for Text, we need to test for Whitespace as well)

                    // if (((1 << navigator.NodeType) & nodesDisallow) op 0) goto LabelBranch;
                    this.helper.Emit(OpCodes.Ldc_I4_1); 
                    kindsRuntime = XPathNodeType.All;
                    break; 
            } 

            // Push navigator.NodeType onto the stack 
            NestedVisitEnsureStack(ndIsType.Source);
            this.helper.Call(XmlILMethods.NavType);

            if (kindsRuntime == XPathNodeType.All) { 
                // if (((1 << navigator.NodeType) & kindsUnion) op 0) goto LabelBranch;
                this.helper.Emit(OpCodes.Shl); 
 
                kindsUnion = 0;
                if ((kinds & XmlNodeKindFlags.Document) != 0) kindsUnion |= (1 << (int) XPathNodeType.Root); 
                if ((kinds & XmlNodeKindFlags.Element) != 0) kindsUnion |= (1 << (int) XPathNodeType.Element);
                if ((kinds & XmlNodeKindFlags.Attribute) != 0) kindsUnion |= (1 << (int) XPathNodeType.Attribute);
                if ((kinds & XmlNodeKindFlags.Text) != 0) kindsUnion |= (1 << (int) (int) XPathNodeType.Text) |
                                                                      (1 << (int) (int) XPathNodeType.SignificantWhitespace) | 
                                                                      (1 << (int) (int) XPathNodeType.Whitespace);
                if ((kinds & XmlNodeKindFlags.Comment) != 0) kindsUnion |= (1 << (int) XPathNodeType.Comment); 
                if ((kinds & XmlNodeKindFlags.PI) != 0) kindsUnion |= (1 << (int) XPathNodeType.ProcessingInstruction); 
                if ((kinds & XmlNodeKindFlags.Namespace) != 0) kindsUnion |= (1 << (int) XPathNodeType.Namespace);
 
                this.helper.LoadInteger(kindsUnion);
                this.helper.Emit(OpCodes.And);
                ZeroCompare(allowKinds ? QilNodeType.Ne : QilNodeType.Eq, false);
            } 
            else {
                // if (navigator.NodeType op runtimeItem) goto LabelBranch; 
                this.helper.LoadInteger((int) kindsRuntime); 
                ClrCompare(allowKinds ? QilNodeType.Eq : QilNodeType.Ne, XmlTypeCode.Int);
            } 

            return true;
        }
 
        /// 
        /// Generate code for QilNodeType.IsEmpty. 
        ///  
        /// 
        /// BranchingContext.OnFalse context: is-empty(expr) 
        /// ==> foreach (item in expr)
        ///         goto LabelBranch;
        ///
        /// BranchingContext.OnTrue context: is-empty(expr) 
        /// ==> foreach (item in expr)
        ///         break; 
        ///     ... 
        ///     LabelOnEnd: (called if foreach is empty)
        ///     goto LabelBranch; 
        ///
        /// BranchingContext.None context: is-empty(expr)
        /// ==> foreach (item in expr)
        ///         break; 
        ///     push true();
        ///     ... 
        ///     LabelOnEnd: (called if foreach is empty) 
        ///     push false();
        ///  
        protected override QilNode VisitIsEmpty(QilUnary ndIsEmpty) {
            Label lblTrue;

            // If the child expression returns a cached result, 
            if (CachesResult(ndIsEmpty.Child)) {
                // Then get the count directly from the cache 
                NestedVisitEnsureStack(ndIsEmpty.Child); 
                this.helper.CallCacheCount(this.iterNested.Storage.ItemStorageType);
 
                switch (this.iterCurr.CurrentBranchingContext) {
                    case BranchingContext.OnFalse:
                        // Take false path if count != 0
                        this.helper.TestAndBranch(0, this.iterCurr.LabelBranch, OpCodes.Bne_Un); 
                        break;
 
                    case BranchingContext.OnTrue: 
                        // Take true path if count == 0
                        this.helper.TestAndBranch(0, this.iterCurr.LabelBranch, OpCodes.Beq); 
                        break;

                    default:
                        Debug.Assert(this.iterCurr.CurrentBranchingContext == BranchingContext.None); 

                        // if (count == 0) goto LabelTrue; 
                        lblTrue = this.helper.DefineLabel(); 
                        this.helper.Emit(OpCodes.Brfalse_S, lblTrue);
 
                        // Convert branch targets into push of true/false
                        this.helper.ConvBranchToBool(lblTrue, true);
                        break;
                } 
            }
            else { 
                Label lblOnEnd = this.helper.DefineLabel(); 
                IteratorDescriptor iterParent = this.iterCurr;
 
                // Forward any LabelOnEnd jumps to LabelBranch if BranchingContext.OnTrue
                if (iterParent.CurrentBranchingContext == BranchingContext.OnTrue)
                    StartNestedIterator(ndIsEmpty.Child, this.iterCurr.LabelBranch);
                else 
                    StartNestedIterator(ndIsEmpty.Child, lblOnEnd);
 
                Visit(ndIsEmpty.Child); 

                // Pop value of IsEmpty expression from the stack if necessary 
                this.iterCurr.EnsureNoCache();
                this.iterCurr.DiscardStack();

                switch (iterParent.CurrentBranchingContext) { 
                    case BranchingContext.OnFalse:
                        // Reverse polarity of iterator 
                        this.helper.EmitUnconditionalBranch(OpCodes.Br, iterParent.LabelBranch); 
                        this.helper.MarkLabel(lblOnEnd);
                        break; 

                    case BranchingContext.OnTrue:
                        // Nothing to do
                        break; 

                    case BranchingContext.None: 
                        // Convert branch targets into push of true/false 
                        this.helper.ConvBranchToBool(lblOnEnd, true);
                        break; 
                }

                // End nested iterator
                EndNestedIterator(ndIsEmpty.Child); 
            }
 
            if (this.iterCurr.IsBranching) 
                this.iterCurr.Storage = StorageDescriptor.None();
            else 
                this.iterCurr.Storage = StorageDescriptor.Stack(typeof(bool), false);

            return ndIsEmpty;
        } 

        ///  
        /// Generate code for QilNodeType.XPathNodeValue. 
        /// 
        protected override QilNode VisitXPathNodeValue(QilUnary ndVal) { 
            Label lblOnEnd, lblDone;
            Debug.Assert(ndVal.Child.XmlType.IsNode, "XPathNodeValue node may only be applied to a sequence of Nodes.");

            // If the expression is a singleton, 
            if (ndVal.Child.XmlType.IsSingleton) {
                // Then generate code to push expresion result onto the stack 
                NestedVisitEnsureStack(ndVal.Child, typeof(XPathNavigator), false); 

                // navigator.Value; 
                this.helper.Call(XmlILMethods.Value);
            }
            else {
                lblOnEnd = this.helper.DefineLabel(); 

                // Construct nested iterator and iterate over results 
                StartNestedIterator(ndVal.Child, lblOnEnd); 
                Visit(ndVal.Child);
                this.iterCurr.EnsureStackNoCache(); 

                // navigator.Value;
                this.helper.Call(XmlILMethods.Value);
 
                // Handle empty sequence by pushing empty string onto the stack
                lblDone = this.helper.DefineLabel(); 
                this.helper.EmitUnconditionalBranch(OpCodes.Br, lblDone); 
                this.helper.MarkLabel(lblOnEnd);
                this.helper.Emit(OpCodes.Ldstr, ""); 
                this.helper.MarkLabel(lblDone);

                // End nested iterator
                EndNestedIterator(ndVal.Child); 
            }
 
            this.iterCurr.Storage = StorageDescriptor.Stack(typeof(string), false); 

            return ndVal; 
        }

        /// 
        /// Find physical query plan for QilNodeType.XPathFollowing. 
        /// 
        protected override QilNode VisitXPathFollowing(QilUnary ndFoll) { 
            CreateFilteredIterator(ndFoll.Child, "$$$iterFoll", typeof(XPathFollowingIterator), XmlILMethods.XPFollCreate, XmlILMethods.XPFollNext, 
                                   XmlNodeKindFlags.Any, null, TriState.Unknown, null);
            return ndFoll; 
        }

        /// 
        /// Find physical query plan for QilNodeType.XPathPreceding. 
        /// 
        protected override QilNode VisitXPathPreceding(QilUnary ndPrec) { 
            CreateFilteredIterator(ndPrec.Child, "$$$iterPrec", typeof(XPathPrecedingIterator), XmlILMethods.XPPrecCreate, XmlILMethods.XPPrecNext, 
                                   XmlNodeKindFlags.Any, null, TriState.Unknown, null);
            return ndPrec; 
        }

        /// 
        /// Find physical query plan for QilNodeType.XPathNamespace. 
        /// 
        protected override QilNode VisitXPathNamespace(QilUnary ndNmsp) { 
            CreateSimpleIterator(ndNmsp.Child, "$$$iterNmsp", typeof(NamespaceIterator), XmlILMethods.NmspCreate, XmlILMethods.NmspNext); 
            return ndNmsp;
        } 

        /// 
        /// Generate code for QilNodeType.XsltGenerateId.
        ///  
        protected override QilNode VisitXsltGenerateId(QilUnary ndGenId) {
            Label lblOnEnd, lblDone; 
 
            this.helper.LoadQueryRuntime();
 
            // If the expression is a singleton,
            if (ndGenId.Child.XmlType.IsSingleton) {
                // Then generate code to push expresion result onto the stack
                NestedVisitEnsureStack(ndGenId.Child, typeof(XPathNavigator), false); 

                // runtime.GenerateId(value); 
                this.helper.Call(XmlILMethods.GenId); 
            }
            else { 
                lblOnEnd = this.helper.DefineLabel();

                // Construct nested iterator and iterate over results
                StartNestedIterator(ndGenId.Child, lblOnEnd); 
                Visit(ndGenId.Child);
                this.iterCurr.EnsureStackNoCache(); 
                this.iterCurr.EnsureItemStorageType(ndGenId.Child.XmlType, typeof(XPathNavigator)); 

                // runtime.GenerateId(value); 
                this.helper.Call(XmlILMethods.GenId);

                // Handle empty sequence by pushing empty string onto the stack
                lblDone = this.helper.DefineLabel(); 
                this.helper.EmitUnconditionalBranch(OpCodes.Br, lblDone);
                this.helper.MarkLabel(lblOnEnd); 
                this.helper.Emit(OpCodes.Pop); 
                this.helper.Emit(OpCodes.Ldstr, "");
                this.helper.MarkLabel(lblDone); 

                // End nested iterator
                EndNestedIterator(ndGenId.Child);
            } 

            this.iterCurr.Storage = StorageDescriptor.Stack(typeof(string), false); 
 
            return ndGenId;
        } 

        /// 
        /// Generate code for for QilNodeType.XsltInvokeLateBound.
        ///  
        protected override QilNode VisitXsltInvokeLateBound(QilInvokeLateBound ndInvoke) {
            LocalBuilder locArgs = this.helper.DeclareLocal("$$$args", typeof(IList[])); 
            QilName ndName = (QilName) ndInvoke.Name; 
            Debug.Assert(XmlILConstructInfo.Read(ndInvoke).ConstructMethod != XmlILConstructMethod.Writer);
 
            // runtime.ExternalContext.InvokeXsltLateBoundFunction(name, ns, args);
            this.helper.LoadQueryContext();
            this.helper.Emit(OpCodes.Ldstr, ndName.LocalName);
            this.helper.Emit(OpCodes.Ldstr, ndName.NamespaceUri); 

            // args = new IList[argCount]; 
            this.helper.LoadInteger(ndInvoke.Arguments.Count); 
            this.helper.Emit(OpCodes.Newarr, typeof(IList));
            this.helper.Emit(OpCodes.Stloc, locArgs); 

            for (int iArg = 0; iArg < ndInvoke.Arguments.Count; iArg++) {
                QilNode ndArg = ndInvoke.Arguments[iArg];
 
                // args[0] = arg0;
                // ... 
                // args[N] = argN; 
                this.helper.Emit(OpCodes.Ldloc, locArgs);
                this.helper.LoadInteger(iArg); 
                this.helper.Emit(OpCodes.Ldelema, typeof(IList));

                NestedVisitEnsureCache(ndArg, typeof(XPathItem));
                this.iterCurr.EnsureStack(); 

                this.helper.Emit(OpCodes.Stobj, typeof(IList)); 
            } 

            this.helper.Emit(OpCodes.Ldloc, locArgs); 

            this.helper.Call(XmlILMethods.InvokeXsltLate);

            // Returned item sequence is on the stack 
            this.iterCurr.Storage = StorageDescriptor.Stack(typeof(XPathItem), true);
 
            return ndInvoke; 
        }
 
        /// 
        /// Generate code for for QilNodeType.XsltInvokeEarlyBound.
        /// 
        protected override QilNode VisitXsltInvokeEarlyBound(QilInvokeEarlyBound ndInvoke) { 
            QilName ndName = ndInvoke.Name;
            XmlExtensionFunction extFunc; 
            Type clrTypeRetSrc, clrTypeRetDst; 

            // Retrieve metadata from the extension function 
            extFunc = new XmlExtensionFunction(ndName.LocalName, ndName.NamespaceUri, ndInvoke.ClrMethod);
            clrTypeRetSrc = extFunc.ClrReturnType;
            clrTypeRetDst = GetStorageType(ndInvoke);
 
            // Prepare to call runtime.ChangeTypeXsltResult
            if (clrTypeRetSrc != clrTypeRetDst && !ndInvoke.XmlType.IsEmpty) { 
                this.helper.LoadQueryRuntime(); 
                this.helper.LoadInteger(this.helper.StaticData.DeclareXmlType(ndInvoke.XmlType));
            } 

            // If this is not a static method, then get the instance object
            if (!extFunc.Method.IsStatic) {
                // Special-case the XsltLibrary object 
                if (ndName.NamespaceUri.Length == 0)
                    this.helper.LoadXsltLibrary(); 
                else 
                    this.helper.CallGetEarlyBoundObject(this.helper.StaticData.DeclareEarlyBound(ndName.NamespaceUri, extFunc.Method.DeclaringType), extFunc.Method.DeclaringType);
            } 

            // Generate code to push each Invoke argument onto the stack
            for (int iArg = 0; iArg < ndInvoke.Arguments.Count; iArg++) {
                QilNode ndActualArg; 
                XmlQueryType xmlTypeFormalArg;
                Type clrTypeActualArg, clrTypeFormalArg; 
 
                ndActualArg = ndInvoke.Arguments[iArg];
 
                // Infer Xml type and Clr type of formal argument
                xmlTypeFormalArg = extFunc.GetXmlArgumentType(iArg);
                clrTypeFormalArg = extFunc.GetClrArgumentType(iArg);
 
                Debug.Assert(ndActualArg.XmlType.IsSubtypeOf(xmlTypeFormalArg), "Xml type of actual arg must be a subtype of the Xml type of the formal arg");
 
                // Use different conversion rules for internal Xslt libraries.  If the actual argument is 
                // stored using Clr type T, then library must use type T, XPathItem, IList, or IList.
                // If the actual argument is stored using Clr type IList, then library must use type 
                // IList or IList.  This is to ensure that there will not be unnecessary
                // conversions that take place when calling into an internal library.
                if (ndName.NamespaceUri.Length == 0) {
                    Type itemType = GetItemStorageType(ndActualArg); 

                    if (clrTypeFormalArg == XmlILMethods.StorageMethods[itemType].IListType) { 
                        // Formal type is IList 
                        NestedVisitEnsureStack(ndActualArg, itemType, true);
                    } 
                    else if (clrTypeFormalArg == XmlILMethods.StorageMethods[typeof(XPathItem)].IListType) {
                        // Formal type is IList
                        NestedVisitEnsureStack(ndActualArg, typeof(XPathItem), true);
                    } 
                    else if ((ndActualArg.XmlType.IsSingleton && clrTypeFormalArg == itemType) || ndActualArg.XmlType.TypeCode == XmlTypeCode.None) {
                        // Formal type is T 
                        NestedVisitEnsureStack(ndActualArg, clrTypeFormalArg, false); 
                    }
                    else if (ndActualArg.XmlType.IsSingleton && clrTypeFormalArg == typeof(XPathItem)) { 
                        // Formal type is XPathItem
                        NestedVisitEnsureStack(ndActualArg, typeof(XPathItem), false);
                    }
                    else 
                        Debug.Fail("Internal Xslt library may not use parameters of type " + clrTypeFormalArg);
                } 
                else { 
                    // There is an implicit upcast to the Xml type of the formal argument.  This can change the Clr storage type.
                    clrTypeActualArg = GetStorageType(xmlTypeFormalArg); 

                    // If the formal Clr type is typeof(object) or if it is not a supertype of the actual Clr type, then call ChangeTypeXsltArgument
                    if (xmlTypeFormalArg.TypeCode == XmlTypeCode.Item || !clrTypeFormalArg.IsAssignableFrom(clrTypeActualArg)) {
                        // (clrTypeFormalArg) runtime.ChangeTypeXsltArgument(xmlTypeFormalArg, (object) value, clrTypeFormalArg); 
                        this.helper.LoadQueryRuntime();
                        this.helper.LoadInteger(this.helper.StaticData.DeclareXmlType(xmlTypeFormalArg)); 
                        NestedVisitEnsureStack(ndActualArg, GetItemStorageType(xmlTypeFormalArg), !xmlTypeFormalArg.IsSingleton); 
                        this.helper.TreatAs(clrTypeActualArg, typeof(object));
                        this.helper.LoadType(clrTypeFormalArg); 
                        this.helper.Call(XmlILMethods.ChangeTypeXsltArg);
                        this.helper.TreatAs(typeof(object), clrTypeFormalArg);
                    }
                    else { 
                        NestedVisitEnsureStack(ndActualArg, GetItemStorageType(xmlTypeFormalArg), !xmlTypeFormalArg.IsSingleton);
                    } 
                } 
            }
 
            // Invoke the target method
            this.helper.Call(extFunc.Method);

            // Return value is on the stack; convert it to canonical ILGen storage type 
            if (ndInvoke.XmlType.IsEmpty) {
                this.helper.Emit(OpCodes.Ldsfld, XmlILMethods.StorageMethods[typeof(XPathItem)].SeqEmpty); 
            } 
            else if (clrTypeRetSrc != clrTypeRetDst) {
                // (T) runtime.ChangeTypeXsltResult(idxType, (object) value); 
                this.helper.TreatAs(clrTypeRetSrc, typeof(object));
                this.helper.Call(XmlILMethods.ChangeTypeXsltResult);
                this.helper.TreatAs(typeof(object), clrTypeRetDst);
            } 
            else if (ndName.NamespaceUri.Length != 0 && !clrTypeRetSrc.IsValueType){
                // Check for null if a user-defined extension function returns a reference type 
                Label lblSkip = this.helper.DefineLabel(); 
                this.helper.Emit(OpCodes.Dup);
                this.helper.Emit(OpCodes.Brtrue, lblSkip); 
                this.helper.LoadQueryRuntime();
                this.helper.Emit(OpCodes.Ldstr, Res.GetString(Res.Xslt_ItemNull));
                this.helper.Call(XmlILMethods.ThrowException);
                this.helper.MarkLabel(lblSkip); 
            }
 
            this.iterCurr.Storage = StorageDescriptor.Stack(GetItemStorageType(ndInvoke), !ndInvoke.XmlType.IsSingleton); 

            return ndInvoke; 
        }

        /// 
        /// Generate code for QilNodeType.XsltCopy. 
        /// 
        protected override QilNode VisitXsltCopy(QilBinary ndCopy) { 
            Label lblSkipContent = this.helper.DefineLabel(); 
            Debug.Assert(XmlILConstructInfo.Read(ndCopy).PushToWriterFirst);
 
            // if (!xwrtChk.StartCopyChk(navCopy)) goto LabelSkipContent;
            this.helper.LoadQueryOutput();

            NestedVisitEnsureStack(ndCopy.Left); 
            Debug.Assert(ndCopy.Left.XmlType.IsNode);
 
            this.helper.Call(XmlILMethods.StartCopy); 
            this.helper.Emit(OpCodes.Brfalse, lblSkipContent);
 
            // Recursively construct content
            NestedVisit(ndCopy.Right);

            // xwrtChk.EndCopyChk(navCopy); 
            this.helper.LoadQueryOutput();
 
            NestedVisitEnsureStack(ndCopy.Left); 
            Debug.Assert(ndCopy.Left.XmlType.IsNode);
 
            this.helper.Call(XmlILMethods.EndCopy);

            // LabelSkipContent:
            this.helper.MarkLabel(lblSkipContent); 

            this.iterCurr.Storage = StorageDescriptor.None(); 
            return ndCopy; 
        }
 
        /// 
        /// Generate code for QilNodeType.XsltCopyOf.
        /// 
        protected override QilNode VisitXsltCopyOf(QilUnary ndCopyOf) { 
            Debug.Assert(XmlILConstructInfo.Read(ndCopyOf).PushToWriterFirst, "XsltCopyOf should always be pushed to writer.");
 
            this.helper.LoadQueryOutput(); 

            // XmlQueryOutput.XsltCopyOf(navigator); 
            NestedVisitEnsureStack(ndCopyOf.Child);
            this.helper.Call(XmlILMethods.CopyOf);

            this.iterCurr.Storage = StorageDescriptor.None(); 
            return ndCopyOf;
        } 
 
        /// 
        /// Generate code for QilNodeType.XsltConvert. 
        /// 
        protected override QilNode VisitXsltConvert(QilTargetType ndConv) {
            XmlQueryType typSrc, typDst;
            MethodInfo meth; 

            typSrc = ndConv.Source.XmlType; 
            typDst = ndConv.TargetType; 

            if (GetXsltConvertMethod(typSrc, typDst, out meth)) { 
                NestedVisitEnsureStack(ndConv.Source);
            }
            else {
                // If a conversion could not be found, then convert the source expression to item or item* and try again 
                NestedVisitEnsureStack(ndConv.Source, typeof(XPathItem), !typSrc.IsSingleton);
                if (!GetXsltConvertMethod(typSrc.IsSingleton ? TypeFactory.Item : TypeFactory.ItemS, typDst, out meth)) 
                    Debug.Fail("Conversion from " + ndConv.Source.XmlType + " to " + ndConv.TargetType + " is not supported."); 
            }
 
            // XsltConvert.XXXToYYY(value);
            if (meth != null)
                this.helper.Call(meth);
 
            this.iterCurr.Storage = StorageDescriptor.Stack(GetItemStorageType(typDst), !typDst.IsSingleton);
            return ndConv; 
        } 

        ///  
        /// Get the XsltConvert method that converts from "typSrc" to "typDst".  Return false if no
        /// such method exists.  This conversion matrix should match the one in XsltConvert.ExternalValueToExternalValue.
        /// 
        private bool GetXsltConvertMethod(XmlQueryType typSrc, XmlQueryType typDst, out MethodInfo meth) { 
            meth = null;
 
            // Note, Ref.Equals is OK to use here, since we will always fall back to Item or Item* in the 
            // case where the source or destination types do not match the static types exposed on the
            // XmlQueryTypeFactory.  This is bad for perf if it accidentally occurs, but the results 
            // should still be correct.

            // => xs:boolean
            if ((object) typDst == (object) TypeFactory.BooleanX) { 
                if ((object) typSrc == (object) TypeFactory.Item)               meth = XmlILMethods.ItemToBool;
                else if ((object) typSrc == (object) TypeFactory.ItemS)         meth = XmlILMethods.ItemsToBool; 
            } 
            // => xs:dateTime
            else if ((object) typDst == (object) TypeFactory.DateTimeX) { 
                if ((object) typSrc == (object) TypeFactory.StringX)            meth = XmlILMethods.StrToDT;
            }
            // => xs:decimal
            else if ((object) typDst == (object) TypeFactory.DecimalX) { 
                if ((object) typSrc == (object) TypeFactory.DoubleX)            meth = XmlILMethods.DblToDec;
            } 
            // => xs:double 
            else if ((object) typDst == (object) TypeFactory.DoubleX) {
                if ((object) typSrc == (object) TypeFactory.DecimalX)           meth = XmlILMethods.DecToDbl; 
                else if ((object) typSrc == (object) TypeFactory.IntX)          meth = XmlILMethods.IntToDbl;
                else if ((object) typSrc == (object) TypeFactory.Item)          meth = XmlILMethods.ItemToDbl;
                else if ((object) typSrc == (object) TypeFactory.ItemS)         meth = XmlILMethods.ItemsToDbl;
                else if ((object) typSrc == (object) TypeFactory.LongX)         meth = XmlILMethods.LngToDbl; 
                else if ((object) typSrc == (object) TypeFactory.StringX)       meth = XmlILMethods.StrToDbl;
            } 
            // => xs:int 
            else if ((object) typDst == (object) TypeFactory.IntX) {
                if ((object) typSrc == (object) TypeFactory.DoubleX)            meth = XmlILMethods.DblToInt; 
            }
            // => xs:long
            else if ((object) typDst == (object) TypeFactory.LongX) {
                if ((object) typSrc == (object) TypeFactory.DoubleX)            meth = XmlILMethods.DblToLng; 
            }
            // => node 
            else if ((object) typDst == (object) TypeFactory.NodeNotRtf) { 
                if ((object) typSrc == (object) TypeFactory.Item)               meth = XmlILMethods.ItemToNode;
                else if ((object) typSrc == (object) TypeFactory.ItemS)         meth = XmlILMethods.ItemsToNode; 
            }
            // => node*
            else if ((object) typDst == (object) TypeFactory.NodeSDod ||
                     (object) typDst == (object) TypeFactory.NodeNotRtfS) { 
                if ((object) typSrc == (object) TypeFactory.Item)               meth = XmlILMethods.ItemToNodes;
                else if ((object) typSrc == (object) TypeFactory.ItemS)         meth = XmlILMethods.ItemsToNodes; 
            } 
            // => xs:string
            else if ((object) typDst == (object) TypeFactory.StringX) { 
                if ((object) typSrc == (object) TypeFactory.DateTimeX)          meth = XmlILMethods.DTToStr;
                else if ((object) typSrc == (object) TypeFactory.DoubleX)       meth = XmlILMethods.DblToStr;
                else if ((object) typSrc == (object) TypeFactory.Item)          meth = XmlILMethods.ItemToStr;
                else if ((object) typSrc == (object) TypeFactory.ItemS)         meth = XmlILMethods.ItemsToStr; 
            }
 
            return meth != null; 
        }
 

        //-----------------------------------------------
        // Helper methods
        //----------------------------------------------- 

        ///  
        /// Ensure that the "locNav" navigator is positioned to the context node "ndCtxt". 
        /// 
        private void SyncToNavigator(LocalBuilder locNav, QilNode ndCtxt) { 
            this.helper.Emit(OpCodes.Ldloc, locNav);
            NestedVisitEnsureStack(ndCtxt);
            this.helper.CallSyncToNavigator();
            this.helper.Emit(OpCodes.Stloc, locNav); 
        }
 
        ///  
        /// Generate boiler-plate code to create a simple Xml iterator.
        ///  
        /// 
        ///     Iterator iter;
        ///     iter.Create(navCtxt);
        /// LabelNext: 
        ///     if (!iter.MoveNext())
        ///         goto LabelNextCtxt; 
        ///  
        private void CreateSimpleIterator(QilNode ndCtxt, string iterName, Type iterType, MethodInfo methCreate, MethodInfo methNext) {
            // Iterator iter; 
            LocalBuilder locIter = this.helper.DeclareLocal(iterName, iterType);

            // iter.Create(navCtxt);
            this.helper.Emit(OpCodes.Ldloca, locIter); 
            NestedVisitEnsureStack(ndCtxt);
            this.helper.Call(methCreate); 
 
            GenerateSimpleIterator(typeof(XPathNavigator), locIter, methNext);
        } 

        /// 
        /// Generate boiler-plate code to create an Xml iterator that uses an XmlNavigatorFilter to filter items.
        ///  
        /// 
        ///     Iterator iter; 
        ///     iter.Create(navCtxt, filter [, orSelf] [, navEnd]); 
        /// LabelNext:
        ///     if (!iter.MoveNext()) 
        ///         goto LabelNextCtxt;
        /// 
        private void CreateFilteredIterator(QilNode ndCtxt, string iterName, Type iterType, MethodInfo methCreate, MethodInfo methNext,
                                                XmlNodeKindFlags kinds, QilName ndName, TriState orSelf, QilNode ndEnd) { 
            // Iterator iter;
            LocalBuilder locIter = this.helper.DeclareLocal(iterName, iterType); 
 
            // iter.Create(navCtxt, filter [, orSelf], [, navEnd]);
            this.helper.Emit(OpCodes.Ldloca, locIter); 
            NestedVisitEnsureStack(ndCtxt);
            LoadSelectFilter(kinds, ndName);
            if (orSelf != TriState.Unknown)
                this.helper.LoadBoolean(orSelf == TriState.True); 
            if (ndEnd != null)
                NestedVisitEnsureStack(ndEnd); 
            this.helper.Call(methCreate); 

            GenerateSimpleIterator(typeof(XPathNavigator), locIter, methNext); 
        }

        /// 
        /// Generate boiler-plate code to create an Xml iterator that controls a nested iterator. 
        /// 
        ///  
        ///     Iterator iter; 
        ///     iter.Create(filter [, orSelf]);
        ///         ...nested iterator... 
        ///     navInput = nestedNested;
        ///     goto LabelCall;
        /// LabelNext:
        ///     navInput = null; 
        /// LabelCall:
        ///     switch (iter.MoveNext(navInput)) { 
        ///         case IteratorState.NoMoreNodes: goto LabelNextCtxt; 
        ///         case IteratorState.NextInputNode: goto LabelNextNested;
        ///     } 
        /// 
        private void CreateContainerIterator(QilUnary ndDod, string iterName, Type iterType, MethodInfo methCreate, MethodInfo methNext,
                                                   XmlNodeKindFlags kinds, QilName ndName, TriState orSelf) {
            // Iterator iter; 
            LocalBuilder locIter = this.helper.DeclareLocal(iterName, iterType);
            Label lblOnEndNested; 
            QilLoop ndLoop = (QilLoop) ndDod.Child; 
            Debug.Assert(ndDod.NodeType == QilNodeType.DocOrderDistinct && ndLoop != null);
 
            // iter.Create(filter [, orSelf]);
            this.helper.Emit(OpCodes.Ldloca, locIter);
            LoadSelectFilter(kinds, ndName);
            if (orSelf != TriState.Unknown) 
                this.helper.LoadBoolean(orSelf == TriState.True);
            this.helper.Call(methCreate); 
 
            // Generate nested iterator (branch to lblOnEndNested when iteration is complete)
            lblOnEndNested = this.helper.DefineLabel(); 
            StartNestedIterator(ndLoop, lblOnEndNested);
            StartBinding(ndLoop.Variable);
            EndBinding(ndLoop.Variable);
            EndNestedIterator(ndLoop.Variable); 
            this.iterCurr.Storage = this.iterNested.Storage;
 
            GenerateContainerIterator(ndDod, locIter, lblOnEndNested, methNext, typeof(XPathNavigator)); 
        }
 
        /// 
        /// Generate boiler-plate code that calls MoveNext on a simple Xml iterator.  Iterator should have already been
        /// created by calling code.
        ///  
        /// 
        ///     ... 
        /// LabelNext: 
        ///     if (!iter.MoveNext())
        ///         goto LabelNextCtxt; 
        /// 
        private void GenerateSimpleIterator(Type itemStorageType, LocalBuilder locIter, MethodInfo methNext) {
            Label lblNext;
 
            // LabelNext:
            lblNext = this.helper.DefineLabel(); 
            this.helper.MarkLabel(lblNext); 

            // if (!iter.MoveNext()) goto LabelNextCtxt; 
            this.helper.Emit(OpCodes.Ldloca, locIter);
            this.helper.Call(methNext);
            this.helper.Emit(OpCodes.Brfalse, this.iterCurr.GetLabelNext());
 
            this.iterCurr.SetIterator(lblNext, StorageDescriptor.Current(locIter, itemStorageType));
        } 
 
        /// 
        /// Generate boiler-plate code that calls MoveNext on an Xml iterator that controls a nested iterator.  Iterator should 
        /// have already been created by calling code.
        /// 
        /// 
        ///     ... 
        ///     goto LabelCall;
        /// LabelNext: 
        ///     navCtxt = null; 
        /// LabelCall:
        ///     switch (iter.MoveNext(navCtxt)) { 
        ///         case IteratorState.NoMoreNodes: goto LabelNextCtxt;
        ///         case IteratorState.NextInputNode: goto LabelNextNested;
        ///     }
        ///  
        private void GenerateContainerIterator(QilNode nd, LocalBuilder locIter, Label lblOnEndNested,
                                                       MethodInfo methNext, Type itemStorageType) { 
            Label lblCall; 

            // Define labels that will be used 
            lblCall = this.helper.DefineLabel();

            // iter.MoveNext(input);
            // goto LabelCall; 
            this.iterCurr.EnsureNoStackNoCache(nd.XmlType.IsNode ? "$$$navInput" : "$$$itemInput");
            this.helper.Emit(OpCodes.Ldloca, locIter); 
            this.iterCurr.PushValue(); 
            this.helper.EmitUnconditionalBranch(OpCodes.Br, lblCall);
 
            // LabelNext:
            // iterSet.MoveNext(null);
            this.helper.MarkLabel(lblOnEndNested);
            this.helper.Emit(OpCodes.Ldloca, locIter); 
            this.helper.Emit(OpCodes.Ldnull);
 
            // LabelCall: 
            // result = iter.MoveNext(input);
            this.helper.MarkLabel(lblCall); 
            this.helper.Call(methNext);

            // If this iterator always returns a single node, then NoMoreNodes will never be returned
            if (nd.XmlType.IsSingleton) { 
                // if (result == IteratorResult.NeedInputNode) goto LabelNextInput;
                this.helper.LoadInteger((int) IteratorResult.NeedInputNode); 
                this.helper.Emit(OpCodes.Beq, this.iterNested.GetLabelNext()); 

                this.iterCurr.Storage = StorageDescriptor.Current(locIter, itemStorageType); 
            }
            else {
                // switch (iter.MoveNext(input)) {
                //      case IteratorResult.NoMoreNodes: goto LabelNextCtxt; 
                //      case IteratorResult.NeedInputNode: goto LabelNextInput;
                // } 
                this.helper.Emit(OpCodes.Switch, new Label[] {this.iterCurr.GetLabelNext(), this.iterNested.GetLabelNext()}); 

                this.iterCurr.SetIterator(lblOnEndNested, StorageDescriptor.Current(locIter, itemStorageType)); 
            }
        }

        ///  
        /// Load XmlQueryOutput, load a name (computed or literal) and load an index to an Xml schema type.
        /// Return an enumeration that specifies what kind of name was loaded. 
        ///  
        private GenerateNameType LoadNameAndType(XPathNodeType nodeType, QilNode ndName, bool isStart, bool callChk) {
            QilName ndLiteralName; 
            string prefix, localName, ns;
            GenerateNameType nameType;
            Debug.Assert(ndName.XmlType.TypeCode == XmlTypeCode.QName, "Element or attribute name must have QName type.");
 
            this.helper.LoadQueryOutput();
 
            // 0. Default is to pop names off stack 
            nameType = GenerateNameType.StackName;
 
            // 1. Literal names
            if (ndName.NodeType == QilNodeType.LiteralQName) {
                // If checks need to be made on End construction, then always pop names from stack
                if (isStart || !callChk) { 
                    ndLiteralName = ndName as QilName;
                    prefix = ndLiteralName.Prefix; 
                    localName = ndLiteralName.LocalName; 
                    ns = ndLiteralName.NamespaceUri;
 
                    // Check local name, namespace parts in debug code
                    Debug.Assert(ValidateNames.ValidateName(prefix, localName, ns, nodeType, ValidateNames.Flags.AllExceptPrefixMapping));

                    // If the namespace is empty, 
                    if (ndLiteralName.NamespaceUri.Length == 0) {
                        // Then always call method on XmlQueryOutput 
                        this.helper.Emit(OpCodes.Ldstr, ndLiteralName.LocalName); 
                        return GenerateNameType.LiteralLocalName;
                    } 

                    // If prefix is not valid for the node type,
                    if (!ValidateNames.ValidateName(prefix, localName, ns, nodeType, ValidateNames.Flags.CheckPrefixMapping)) {
                        // Then construct a new prefix at run-time 
                        if (isStart) {
                            this.helper.Emit(OpCodes.Ldstr, localName); 
                            this.helper.Emit(OpCodes.Ldstr, ns); 
                            this.helper.Construct(XmlILConstructors.QName);
 
                            nameType = GenerateNameType.QName;
                        }
                    }
                    else { 
                        // Push string parts
                        this.helper.Emit(OpCodes.Ldstr, prefix); 
                        this.helper.Emit(OpCodes.Ldstr, localName); 
                        this.helper.Emit(OpCodes.Ldstr, ns);
 
                        nameType = GenerateNameType.LiteralName;
                    }
                }
            } 
            else {
                if (isStart) { 
                    // 2. Copied names 
                    if (ndName.NodeType == QilNodeType.NameOf) {
                        // Preserve prefix of source node, so just push navigator onto stack 
                        NestedVisitEnsureStack((ndName as QilUnary).Child);
                        nameType = GenerateNameType.CopiedName;
                    }
                    // 3. Parsed tag names (foo:bar) 
                    else if (ndName.NodeType == QilNodeType.StrParseQName) {
                        // Preserve prefix from parsed tag name 
                        VisitStrParseQName(ndName as QilBinary, true); 

                        // Type of name depends upon data-type of name argument 
                        if ((ndName as QilBinary).Right.XmlType.TypeCode == XmlTypeCode.String)
                            nameType = GenerateNameType.TagNameAndNamespace;
                        else
                            nameType = GenerateNameType.TagNameAndMappings; 
                    }
                    // 4. Other computed qnames 
                    else { 
                        // Push XmlQualifiedName onto the stack
                        NestedVisitEnsureStack(ndName); 
                        nameType = GenerateNameType.QName;
                    }
                }
            } 

            return nameType; 
        } 

        ///  
        /// If the first argument is a constant value that evaluates to zero, then a more optimal instruction sequence
        /// can be generated that does not have to push the zero onto the stack.  Instead, a Brfalse or Brtrue instruction
        /// can be used.
        ///  
        private bool TryZeroCompare(QilNodeType relOp, QilNode ndFirst, QilNode ndSecond) {
            Debug.Assert(relOp == QilNodeType.Eq || relOp == QilNodeType.Ne); 
 
            switch (ndFirst.NodeType) {
                case QilNodeType.LiteralInt64: 
                    if ((int) (QilLiteral) ndFirst != 0) return false;
                    break;

                case QilNodeType.LiteralInt32: 
                    if ((int) (QilLiteral) ndFirst != 0) return false;
                    break; 
 
                case QilNodeType.False:
                    break; 

                case QilNodeType.True:
                    // Inverse of QilNodeType.False
                    relOp = (relOp == QilNodeType.Eq) ? QilNodeType.Ne : QilNodeType.Eq; 
                    break;
 
                default: 
                    return false;
            } 

            // Generate code to push second argument on stack
            NestedVisitEnsureStack(ndSecond);
 
            // Generate comparison code -- op == 0 or op != 0
            ZeroCompare(relOp, ndSecond.XmlType.TypeCode == XmlTypeCode.Boolean); 
 
            return true;
        } 

        /// 
        /// If the comparison involves a qname, then perform comparison using atoms and return true.
        /// Otherwise, return false (caller will perform comparison). 
        /// 
        private bool TryNameCompare(QilNodeType relOp, QilNode ndFirst, QilNode ndSecond) { 
            Debug.Assert(relOp == QilNodeType.Eq || relOp == QilNodeType.Ne); 

            if (ndFirst.NodeType == QilNodeType.NameOf) { 
                switch (ndSecond.NodeType) {
                    case QilNodeType.NameOf:
                    case QilNodeType.LiteralQName: {
                        this.helper.LoadQueryRuntime(); 

                        // Push left navigator onto the stack 
                        NestedVisitEnsureStack((ndFirst as QilUnary).Child); 

                        // Push the local name and namespace uri of the right argument onto the stack 
                        if (ndSecond.NodeType == QilNodeType.LiteralQName) {
                            QilName ndName = ndSecond as QilName;
                            this.helper.LoadInteger(this.helper.StaticData.DeclareName(ndName.LocalName));
                            this.helper.LoadInteger(this.helper.StaticData.DeclareName(ndName.NamespaceUri)); 

                            // push runtime.IsQNameEqual(navigator, localName, namespaceUri) 
                            this.helper.Call(XmlILMethods.QNameEqualLit); 
                        }
                        else { 
                            // Generate code to locate the navigator argument of NameOf operator
                            Debug.Assert(ndSecond.NodeType == QilNodeType.NameOf);
                            NestedVisitEnsureStack(ndSecond);
 
                            // push runtime.IsQNameEqual(nav1, nav2)
                            this.helper.Call(XmlILMethods.QNameEqualNav); 
                        } 

                        // Branch based on boolean result or push boolean value 
                        ZeroCompare((relOp == QilNodeType.Eq) ? QilNodeType.Ne : QilNodeType.Eq, true);
                        return true;
                    }
                } 
            }
 
            // Caller must perform comparison 
            return false;
        } 

        /// 
        /// For QilExpression types that map directly to CLR primitive types, the built-in CLR comparison operators can
        /// be used to perform the specified relational operation. 
        /// 
        private void ClrCompare(QilNodeType relOp, XmlTypeCode code) { 
            OpCode opcode; 
            Label lblTrue;
 
            switch (this.iterCurr.CurrentBranchingContext) {
                case BranchingContext.OnFalse:
                    // Reverse the comparison operator
                    // Use Bxx_Un OpCodes to handle NaN case for double and single types 
                    if (code == XmlTypeCode.Double || code == XmlTypeCode.Float) {
                        switch (relOp) { 
                            case QilNodeType.Gt: opcode = OpCodes.Ble_Un; break; 
                            case QilNodeType.Ge: opcode = OpCodes.Blt_Un; break;
                            case QilNodeType.Lt: opcode = OpCodes.Bge_Un; break; 
                            case QilNodeType.Le: opcode = OpCodes.Bgt_Un; break;
                            case QilNodeType.Eq: opcode = OpCodes.Bne_Un; break;
                            case QilNodeType.Ne: opcode = OpCodes.Beq; break;
                            default: Debug.Assert(false); opcode = OpCodes.Nop; break; 
                        }
                    } 
                    else { 
                        switch (relOp)
                        { 
                            case QilNodeType.Gt: opcode = OpCodes.Ble; break;
                            case QilNodeType.Ge: opcode = OpCodes.Blt; break;
                            case QilNodeType.Lt: opcode = OpCodes.Bge; break;
                            case QilNodeType.Le: opcode = OpCodes.Bgt; break; 
                            case QilNodeType.Eq: opcode = OpCodes.Bne_Un; break;
                            case QilNodeType.Ne: opcode = OpCodes.Beq; break; 
                            default: Debug.Assert(false); opcode = OpCodes.Nop; break; 
                        }
                    } 
                    this.helper.Emit(opcode, this.iterCurr.LabelBranch);
                    this.iterCurr.Storage = StorageDescriptor.None();
                    break;
 
                case BranchingContext.OnTrue:
                    switch (relOp) { 
                        case QilNodeType.Gt: opcode = OpCodes.Bgt; break; 
                        case QilNodeType.Ge: opcode = OpCodes.Bge; break;
                        case QilNodeType.Lt: opcode = OpCodes.Blt; break; 
                        case QilNodeType.Le: opcode = OpCodes.Ble; break;
                        case QilNodeType.Eq: opcode = OpCodes.Beq; break;
                        case QilNodeType.Ne: opcode = OpCodes.Bne_Un; break;
                        default: Debug.Assert(false); opcode = OpCodes.Nop; break; 
                    }
                    this.helper.Emit(opcode, this.iterCurr.LabelBranch); 
                    this.iterCurr.Storage = StorageDescriptor.None(); 
                    break;
 
                default:
                    Debug.Assert(this.iterCurr.CurrentBranchingContext == BranchingContext.None);
                    switch (relOp) {
                        case QilNodeType.Gt: this.helper.Emit(OpCodes.Cgt); break; 
                        case QilNodeType.Lt: this.helper.Emit(OpCodes.Clt); break;
                        case QilNodeType.Eq: this.helper.Emit(OpCodes.Ceq); break; 
                        default: 
                            switch (relOp) {
                                case QilNodeType.Ge: opcode = OpCodes.Bge_S; break; 
                                case QilNodeType.Le: opcode = OpCodes.Ble_S; break;
                                case QilNodeType.Ne: opcode = OpCodes.Bne_Un_S; break;
                                default: Debug.Assert(false); opcode = OpCodes.Nop; break;
                            } 

                            // Push "true" if comparison succeeds, "false" otherwise 
                            lblTrue = this.helper.DefineLabel(); 
                            this.helper.Emit(opcode, lblTrue);
                            this.helper.ConvBranchToBool(lblTrue, true); 
                            break;
                    }
                    this.iterCurr.Storage = StorageDescriptor.Stack(typeof(bool), false);
                    break; 
            }
        } 
 
        /// 
        /// Generate code to compare the top stack value to 0 by using the Brfalse or Brtrue instructions, 
        /// which avoid pushing zero onto the stack.  Both of these instructions test for null/zero/false.
        /// 
        private void ZeroCompare(QilNodeType relOp, bool isBoolVal) {
            Label lblTrue; 
            Debug.Assert(relOp == QilNodeType.Eq || relOp == QilNodeType.Ne);
 
            // Test to determine if top stack value is zero (if relOp is Eq) or is not zero (if relOp is Ne) 
            switch (this.iterCurr.CurrentBranchingContext) {
                case BranchingContext.OnTrue: 
                    // If relOp is Eq, jump to true label if top value is zero (Brfalse)
                    // If relOp is Ne, jump to true label if top value is non-zero (Brtrue)
                    this.helper.Emit((relOp == QilNodeType.Eq) ? OpCodes.Brfalse : OpCodes.Brtrue, this.iterCurr.LabelBranch);
                    this.iterCurr.Storage = StorageDescriptor.None(); 
                    break;
 
                case BranchingContext.OnFalse: 
                    // If relOp is Eq, jump to false label if top value is non-zero (Brtrue)
                    // If relOp is Ne, jump to false label if top value is zero (Brfalse) 
                    this.helper.Emit((relOp == QilNodeType.Eq) ? OpCodes.Brtrue : OpCodes.Brfalse, this.iterCurr.LabelBranch);
                    this.iterCurr.Storage = StorageDescriptor.None();
                    break;
 
                default:
                    Debug.Assert(this.iterCurr.CurrentBranchingContext == BranchingContext.None); 
 
                    // Since (boolval != 0) = boolval, value on top of the stack is already correct
                    if (!isBoolVal || relOp == QilNodeType.Eq) { 
                        // If relOp is Eq, push "true" if top value is zero, "false" otherwise
                        // If relOp is Ne, push "true" if top value is non-zero, "false" otherwise
                        lblTrue = this.helper.DefineLabel();
                        this.helper.Emit((relOp == QilNodeType.Eq) ? OpCodes.Brfalse : OpCodes.Brtrue, lblTrue); 
                        this.helper.ConvBranchToBool(lblTrue, true);
                    } 
 
                    this.iterCurr.Storage = StorageDescriptor.Stack(typeof(bool), false);
                    break; 
            }
        }

        ///  
        /// Construction within a loop is starting.  If transition from non-Any to Any state occurs, then ensure
        /// that runtime state will be set. 
        ///  
        private void StartWriterLoop(QilNode nd, out bool hasOnEnd, out Label lblOnEnd) {
            XmlILConstructInfo info = XmlILConstructInfo.Read(nd); 

            // By default, do not create a new iteration label
            hasOnEnd = false;
            lblOnEnd = new Label(); 

            // If loop is not involved in Xml construction, or if loop returns exactly one value, then do nothing 
            if (!info.PushToWriterLast || nd.XmlType.IsSingleton) 
                return;
 
            if (!this.iterCurr.HasLabelNext) {
                // Iterate until all items are constructed
                hasOnEnd = true;
                lblOnEnd = this.helper.DefineLabel(); 
                this.iterCurr.SetIterator(lblOnEnd, StorageDescriptor.None());
            } 
        } 

        ///  
        /// Construction within a loop is ending.  If transition from non-Any to Any state occurs, then ensure that
        /// runtime state will be set.
        /// 
        private void EndWriterLoop(QilNode nd, bool hasOnEnd, Label lblOnEnd) { 
            XmlILConstructInfo info = XmlILConstructInfo.Read(nd);
 
            // If loop is not involved in Xml construction, then do nothing 
            if (!info.PushToWriterLast)
                return; 

            // Since results of construction were pushed to writer, there are no values to return
            this.iterCurr.Storage = StorageDescriptor.None();
 
            // If loop returns exactly one value, then do nothing further
            if (nd.XmlType.IsSingleton) 
                return; 

            if (hasOnEnd) { 
                // Loop over all items in the list, sending each to the output writer
                this.iterCurr.LoopToEnd(lblOnEnd);
            }
        } 

        ///  
        /// Returns true if the specified node's owner element might have local namespaces added to it 
        /// after attributes have already been added.
        ///  
        private bool MightHaveNamespacesAfterAttributes(XmlILConstructInfo info) {
            // Get parent element
            if (info != null)
                info = info.ParentElementInfo; 

            // If a parent element has not been statically identified, then assume that the runtime 
            // element will have namespaces added after attributes. 
            if (info == null)
                return true; 

            return info.MightHaveNamespacesAfterAttributes;
        }
 
        /// 
        /// Returns true if the specified element should cache attributes. 
        ///  
        private bool ElementCachesAttributes(XmlILConstructInfo info) {
            // Attributes will be cached if namespaces might be constructed after the attributes 
            return info.MightHaveDuplicateAttributes || info.MightHaveNamespacesAfterAttributes;
        }

        ///  
        /// This method is called before calling any WriteEnd??? method.  It generates code to perform runtime
        /// construction checks separately.  This should only be called if the XmlQueryOutput::StartElementChk 
        /// method will *not* be called. 
        /// 
        private void BeforeStartChecks(QilNode ndCtor) { 
            switch (XmlILConstructInfo.Read(ndCtor).InitialStates) {
                case PossibleXmlStates.WithinSequence:
                    // If runtime state is guaranteed to be WithinSequence, then call XmlQueryOutput.StartTree
                    this.helper.CallStartTree(QilConstructorToNodeType(ndCtor.NodeType)); 
                    break;
 
                case PossibleXmlStates.EnumAttrs: 
                    switch (ndCtor.NodeType) {
                        case QilNodeType.ElementCtor: 
                        case QilNodeType.TextCtor:
                        case QilNodeType.RawTextCtor:
                        case QilNodeType.PICtor:
                        case QilNodeType.CommentCtor: 
                            // If runtime state is guaranteed to be EnumAttrs, and content is being constructed, call
                            // XmlQueryOutput.StartElementContent 
                            this.helper.CallStartElementContent(); 
                            break;
                    } 
                    break;
            }
        }
 
        /// 
        /// This method is called after calling any WriteEnd??? method.  It generates code to perform runtime 
        /// construction checks separately.  This should only be called if the XmlQueryOutput::EndElementChk 
        /// method will *not* be called.
        ///  
        private void AfterEndChecks(QilNode ndCtor) {
            if (XmlILConstructInfo.Read(ndCtor).FinalStates == PossibleXmlStates.WithinSequence) {
                // If final runtime state is guaranteed to be WithinSequence, then call XmlQueryOutput.StartTree
                this.helper.CallEndTree(); 
            }
        } 
 
        /// 
        /// Return true if a runtime check needs to be made in order to transition into the WithinContent state. 
        /// 
        private bool CheckWithinContent(XmlILConstructInfo info) {
            switch (info.InitialStates) {
                case PossibleXmlStates.WithinSequence: 
                case PossibleXmlStates.EnumAttrs:
                case PossibleXmlStates.WithinContent: 
                    // Transition to WithinContent can be ensured at compile-time 
                    return false;
            } 

            return true;
        }
 
        /// 
        /// Return true if a runtime check needs to be made in order to transition into the EnumAttrs state. 
        ///  
        private bool CheckEnumAttrs(XmlILConstructInfo info) {
            switch (info.InitialStates) { 
                case PossibleXmlStates.WithinSequence:
                case PossibleXmlStates.EnumAttrs:
                    // Transition to EnumAttrs can be ensured at compile-time
                    return false; 
            }
 
            return true; 
        }
 
        /// 
        /// Map the XmlNodeKindFlags enumeration into the XPathNodeType enumeration.
        /// 
        private XPathNodeType QilXmlToXPathNodeType(XmlNodeKindFlags xmlTypes) { 
            switch (xmlTypes) {
                case XmlNodeKindFlags.Element: return XPathNodeType.Element; 
                case XmlNodeKindFlags.Attribute: return XPathNodeType.Attribute; 
                case XmlNodeKindFlags.Text: return XPathNodeType.Text;
                case XmlNodeKindFlags.Comment: return XPathNodeType.Comment; 
            }
            Debug.Assert(xmlTypes == XmlNodeKindFlags.PI);
            return XPathNodeType.ProcessingInstruction;
        } 

        ///  
        /// Map a QilExpression constructor type into the XPathNodeType enumeration. 
        /// 
        private XPathNodeType QilConstructorToNodeType(QilNodeType typ) { 
            switch (typ) {
                case QilNodeType.DocumentCtor: return XPathNodeType.Root;
                case QilNodeType.ElementCtor: return XPathNodeType.Element;
                case QilNodeType.TextCtor: return XPathNodeType.Text; 
                case QilNodeType.RawTextCtor: return XPathNodeType.Text;
                case QilNodeType.PICtor: return XPathNodeType.ProcessingInstruction; 
                case QilNodeType.CommentCtor: return XPathNodeType.Comment; 
                case QilNodeType.AttributeCtor: return XPathNodeType.Attribute;
                case QilNodeType.NamespaceDecl: return XPathNodeType.Namespace; 
            }

            Debug.Assert(false, "Cannot map QilNodeType " + typ + " to an XPathNodeType");
            return XPathNodeType.All; 
        }
 
        ///  
        /// Load an XmlNavigatorFilter that matches only the specified name and types onto the stack.
        ///  
        private void LoadSelectFilter(XmlNodeKindFlags xmlTypes, QilName ndName) {
            if (ndName != null) {
                // Push NameFilter
                Debug.Assert(xmlTypes == XmlNodeKindFlags.Element); 
                this.helper.CallGetNameFilter(this.helper.StaticData.DeclareNameFilter(ndName.LocalName, ndName.NamespaceUri));
            } 
            else { 
                // Either type cannot be a union, or else it must be >= union of all Content types
                bool isXmlTypeUnion = IsNodeTypeUnion(xmlTypes); 
                Debug.Assert(!isXmlTypeUnion || (xmlTypes & XmlNodeKindFlags.Content) == XmlNodeKindFlags.Content);

                if (isXmlTypeUnion) {
                    if ((xmlTypes & XmlNodeKindFlags.Attribute) != 0) { 
                        // Union includes attributes, so allow all node kinds
                        this.helper.CallGetTypeFilter(XPathNodeType.All); 
                    } 
                    else {
                        // Filter attributes 
                        this.helper.CallGetTypeFilter(XPathNodeType.Attribute);
                    }
                }
                else { 
                    // Filter nodes of all but one type
                    this.helper.CallGetTypeFilter(QilXmlToXPathNodeType(xmlTypes)); 
                } 
            }
        } 

        /// 
        /// Return true if more than one node type is set.
        ///  
        private static bool IsNodeTypeUnion(XmlNodeKindFlags xmlTypes) {
            return ((int) xmlTypes & ((int) xmlTypes - 1)) != 0; 
        } 

        ///  
        /// Start construction of a new nested iterator.  If this.iterCurr == null, then the new iterator
        /// is a top-level, or root iterator.  Otherwise, the new iterator will be nested within the
        /// current iterator.
        ///  
        private void StartNestedIterator(QilNode nd) {
            IteratorDescriptor iterParent = this.iterCurr; 
 
            // Create a new, nested iterator
            if (iterParent == null) { 
                // Create a "root" iterator info that has no parernt
                this.iterCurr = new IteratorDescriptor(this.helper);
            }
            else { 
                // Create a nested iterator
                this.iterCurr = new IteratorDescriptor(iterParent); 
            } 

            this.iterNested = null; 
        }

        /// 
        /// Calls StartNestedIterator(nd) and also sets up the nested iterator to branch to "lblOnEnd" when iteration 
        /// is complete.
        ///  
        private void StartNestedIterator(QilNode nd, Label lblOnEnd) { 
            StartNestedIterator(nd);
            this.iterCurr.SetIterator(lblOnEnd, StorageDescriptor.None()); 
        }

        /// 
        /// End construction of the current iterator. 
        /// 
        private void EndNestedIterator(QilNode nd) { 
            Debug.Assert(this.iterCurr.Storage.Location == ItemLocation.None || 
                         this.iterCurr.Storage.ItemStorageType == GetItemStorageType(nd) ||
                         this.iterCurr.Storage.ItemStorageType == typeof(XPathItem) || 
                         nd.XmlType.TypeCode == XmlTypeCode.None,
                         "QilNodeType " + nd.NodeType + " cannot be stored using type " + this.iterCurr.Storage.ItemStorageType + ".");

            // If the nested iterator was constructed in branching mode, 
            if (this.iterCurr.IsBranching) {
                // Then if branching hasn't already taken place, do so now 
                if (this.iterCurr.Storage.Location != ItemLocation.None) { 
                    this.iterCurr.EnsureItemStorageType(nd.XmlType, typeof(bool));
                    this.iterCurr.EnsureStackNoCache(); 

                    if (this.iterCurr.CurrentBranchingContext == BranchingContext.OnTrue)
                        this.helper.Emit(OpCodes.Brtrue, this.iterCurr.LabelBranch);
                    else 
                        this.helper.Emit(OpCodes.Brfalse, this.iterCurr.LabelBranch);
 
                    this.iterCurr.Storage = StorageDescriptor.None(); 
                }
            } 

            // Save current iterator as nested iterator
            this.iterNested = this.iterCurr;
 
            // Update current iterator to be parent iterator
            this.iterCurr = this.iterCurr.ParentIterator; 
        } 

        ///  
        /// Recursively generate code to iterate over the results of the "nd" expression.  If "nd" is pushed
        /// to the writer, then there are no results.  If "nd" is a singleton expression and isCached is false,
        /// then generate code to construct the singleton.  Otherwise, cache the sequence in an XmlQuerySequence
        /// object.  Ensure that all items are converted to the specified "itemStorageType". 
        /// 
        private void NestedVisit(QilNode nd, Type itemStorageType, bool isCached) { 
            if (XmlILConstructInfo.Read(nd).PushToWriterLast) { 
                // Push results to output, so nothing is left to store
                StartNestedIterator(nd); 
                Visit(nd);
                EndNestedIterator(nd);
                this.iterCurr.Storage = StorageDescriptor.None();
            } 
            else if (!isCached && nd.XmlType.IsSingleton) {
                // Storage of result will be a non-cached singleton 
                StartNestedIterator(nd); 
                Visit(nd);
                this.iterCurr.EnsureNoCache(); 
                this.iterCurr.EnsureItemStorageType(nd.XmlType, itemStorageType);
                EndNestedIterator(nd);
                this.iterCurr.Storage = this.iterNested.Storage;
            } 
            else {
                NestedVisitEnsureCache(nd, itemStorageType); 
            } 
        }
 
        /// 
        /// Calls NestedVisit(QilNode, Type, bool), storing result in the default storage type for "nd".
        /// 
        private void NestedVisit(QilNode nd) { 
            NestedVisit(nd, GetItemStorageType(nd), !nd.XmlType.IsSingleton);
        } 
 
        /// 
        /// Recursively generate code to iterate over the results of the "nd" expression.  When the expression 
        /// has been fully iterated, it will jump to "lblOnEnd".
        /// 
        private void NestedVisit(QilNode nd, Label lblOnEnd) {
            Debug.Assert(!XmlILConstructInfo.Read(nd).PushToWriterLast); 
            StartNestedIterator(nd, lblOnEnd);
            Visit(nd); 
            this.iterCurr.EnsureNoCache(); 
            this.iterCurr.EnsureItemStorageType(nd.XmlType, GetItemStorageType(nd));
            EndNestedIterator(nd); 
            this.iterCurr.Storage = this.iterNested.Storage;
        }

        ///  
        /// Call NestedVisit(QilNode) and ensure that result is pushed onto the IL stack.
        ///  
        private void NestedVisitEnsureStack(QilNode nd) { 
            Debug.Assert(!XmlILConstructInfo.Read(nd).PushToWriterLast);
            NestedVisit(nd); 
            this.iterCurr.EnsureStack();
        }

        ///  
        /// Generate code for both QilExpression nodes and ensure that each result is pushed onto the IL stack.
        ///  
        private void NestedVisitEnsureStack(QilNode ndLeft, QilNode ndRight) { 
            NestedVisitEnsureStack(ndLeft);
            NestedVisitEnsureStack(ndRight); 
        }

        /// 
        /// Call NestedVisit(QilNode, Type, bool) and ensure that result is pushed onto the IL stack. 
        /// 
        private void NestedVisitEnsureStack(QilNode nd, Type itemStorageType, bool isCached) { 
            Debug.Assert(!XmlILConstructInfo.Read(nd).PushToWriterLast); 
            NestedVisit(nd, itemStorageType, isCached);
            this.iterCurr.EnsureStack(); 
        }

        /// 
        /// Call NestedVisit(QilNode) and ensure that result is stored in local variable "loc". 
        /// 
        private void NestedVisitEnsureLocal(QilNode nd, LocalBuilder loc) { 
            Debug.Assert(!XmlILConstructInfo.Read(nd).PushToWriterLast); 
            NestedVisit(nd);
            this.iterCurr.EnsureLocal(loc); 
        }

        /// 
        /// Start a nested iterator in a branching context and recursively generate code for the specified QilExpression node. 
        /// 
        private void NestedVisitWithBranch(QilNode nd, BranchingContext brctxt, Label lblBranch) { 
            Debug.Assert(nd.XmlType.IsSingleton && !XmlILConstructInfo.Read(nd).PushToWriterLast); 
            StartNestedIterator(nd);
            this.iterCurr.SetBranching(brctxt, lblBranch); 
            Visit(nd);
            EndNestedIterator(nd);
            this.iterCurr.Storage = StorageDescriptor.None();
        } 

        ///  
        /// Generate code for the QilExpression node and ensure that results are fully cached as an XmlQuerySequence.  All results 
        /// should be converted to "itemStorageType" before being added to the cache.
        ///  
        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) 
            cacheType = (GetItemStorageType(nd) == typeof(XPathNavigator)) ? typeof(XPathNavigator) : itemStorageType;
 
            // XmlQuerySequence 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 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);
        } 

        ///  
        /// Returns true if the specified QilExpression node type is *guaranteed* to cache its results in an XmlQuerySequence, 
        /// where items in the cache are stored using the default storage type.
        ///  
        private bool CachesResult(QilNode nd) {
            OptimizerPatterns patt;

            switch (nd.NodeType) { 
                case QilNodeType.Let:
                case QilNodeType.Parameter: 
                case QilNodeType.Invoke: 
                case QilNodeType.XsltInvokeLateBound:
                case QilNodeType.XsltInvokeEarlyBound: 
                    return !nd.XmlType.IsSingleton;

                case QilNodeType.Filter:
                    // EqualityIndex pattern caches results 
                    patt = OptimizerPatterns.Read(nd);
                    return patt.MatchesPattern(OptimizerPatternName.EqualityIndex); 
 
                case QilNodeType.DocOrderDistinct:
                    if (nd.XmlType.IsSingleton) 
                        return false;

                    // JoinAndDod and DodReverse patterns don't cache results
                    patt = OptimizerPatterns.Read(nd); 
                    return !patt.MatchesPattern(OptimizerPatternName.JoinAndDod) && !patt.MatchesPattern(OptimizerPatternName.DodReverse);
 
                case QilNodeType.TypeAssert: 
                    QilTargetType ndTypeAssert = (QilTargetType) nd;
                    // Check if TypeAssert would be no-op 
                    return CachesResult(ndTypeAssert.Source) && GetItemStorageType(ndTypeAssert.Source) == GetItemStorageType(ndTypeAssert);
            }

            return false; 
        }
 
        ///  
        /// Shortcut call to XmlILTypeHelper.GetStorageType.
        ///  
        private Type GetStorageType(QilNode nd) {
            return XmlILTypeHelper.GetStorageType(nd.XmlType);
        }
 
        /// 
        /// Shortcut call to XmlILTypeHelper.GetStorageType. 
        ///  
        private Type GetStorageType(XmlQueryType typ) {
            return XmlILTypeHelper.GetStorageType(typ); 
        }

        /// 
        /// Shortcut call to XmlILTypeHelper.GetStorageType, using an expression's prime type. 
        /// 
        private Type GetItemStorageType(QilNode nd) { 
            return XmlILTypeHelper.GetStorageType(nd.XmlType.Prime); 
        }
 
        /// 
        /// Shortcut call to XmlILTypeHelper.GetStorageType, using the prime type.
        /// 
        private Type GetItemStorageType(XmlQueryType typ) { 
            return XmlILTypeHelper.GetStorageType(typ.Prime);
        } 
    } 
}

// File provided for Reference Use Only by Microsoft Corporation (c) 2007.


                        

Link Menu

Network programming in C#, Network Programming in VB.NET, Network Programming in .NET
This book is available now!
Buy at Amazon US or
Buy at Amazon UK