The Labs \ Source Viewer \ SSCLI \ System.Xml.Xsl.IlGen \ XmlILVisitor

  1. //------------------------------------------------------------------------------
  2. // <copyright file="XmlIlVisitor.cs" company="Microsoft">
  3. //
  4. // Copyright (c) 2006 Microsoft Corporation. All rights reserved.
  5. //
  6. // The use and distribution terms for this software are contained in the file
  7. // named license.txt, which can be found in the root of this distribution.
  8. // By using this software in any fashion, you are agreeing to be bound by the
  9. // terms of this license.
  10. //
  11. // You must not remove this notice, or any other, from this software.
  12. //
  13. // </copyright>
  14. //------------------------------------------------------------------------------
  15. using System;
  16. using System.Xml;
  17. using System.Xml.XPath;
  18. using System.Xml.Schema;
  19. using System.Globalization;
  20. using System.Collections;
  21. using System.Collections.Generic;
  22. using System.Diagnostics;
  23. using System.Reflection;
  24. using System.Reflection.Emit;
  25. using System.Xml.Xsl;
  26. using System.Xml.Xsl.Qil;
  27. using System.Xml.Xsl.Runtime;
  28. namespace System.Xml.Xsl.IlGen
  29. {
  30.     using TypeFactory = System.Xml.Xsl.XmlQueryTypeFactory;
  31.     using Res = System.Xml.Utils.Res;
  32.    
  33.     /// <summary>
  34.     /// Creates Msil code for an entire QilExpression graph. Code is generated in one of two modes: push or
  35.     /// pull. In push mode, code is generated to push the values in an iterator to the XmlWriter
  36.     /// interface. In pull mode, the values in an iterator are stored in a physical location such as
  37.     /// the stack or a local variable by an iterator. The iterator is passive, and will just wait for
  38.     /// a caller to pull the data and/or instruct the iterator to enumerate the next value.
  39.     /// </summary>
  40.     internal class XmlILVisitor : QilVisitor
  41.     {
  42.         private QilExpression qil;
  43.         private GenerateHelper helper;
  44.         private IteratorDescriptor iterCurr, iterNested;
  45.         private MethodInfo methRoot;
  46.         private int indexId;
  47.        
  48.        
  49.         //-----------------------------------------------
  50.         // Entry
  51.         //-----------------------------------------------
  52.        
  53.         /// <summary>
  54.         /// Visits the specified QilExpression graph and generates MSIL code.
  55.         /// </summary>
  56.         public void Visit(QilExpression qil, GenerateHelper helper, MethodInfo methRoot)
  57.         {
  58.             this.qil = qil;
  59.             this.helper = helper;
  60.             this.methRoot = methRoot;
  61.             this.iterNested = null;
  62.             this.indexId = 0;
  63.            
  64.             // Prepare each global parameter and global variable to be visited
  65.             PrepareGlobalValues(qil.GlobalParameterList);
  66.             PrepareGlobalValues(qil.GlobalVariableList);
  67.            
  68.             // Visit each global parameter and global variable
  69.             VisitGlobalValues(qil.GlobalParameterList);
  70.             VisitGlobalValues(qil.GlobalVariableList);
  71.            
  72.             // Build each function
  73.             foreach (QilFunction ndFunc in qil.FunctionList) {
  74.                 // Visit each parameter and the function body
  75.                 Function(ndFunc);
  76.             }
  77.            
  78.             // Build the root expression
  79.             this.helper.MethodBegin(this.methRoot, null, true);
  80.             StartNestedIterator(qil.Root);
  81.             Visit(qil.Root);
  82.             Debug.Assert(this.iterCurr.Storage.Location == ItemLocation.None, "Root expression should have been pushed to the writer.");
  83.             EndNestedIterator(qil.Root);
  84.             this.helper.MethodEnd();
  85.         }
  86.        
  87.         /// <summary>
  88.         /// Create IteratorDescriptor for each global value. This pre-visit is necessary because a global early
  89.         /// in the list may reference a global later in the list and therefore expect its IteratorDescriptor to already
  90.         /// be initialized.
  91.         /// </summary>
  92.         private void PrepareGlobalValues(QilList globalIterators)
  93.         {
  94.             MethodInfo methGlobal;
  95.             IteratorDescriptor iterInfo;
  96.            
  97.             foreach (QilIterator iter in globalIterators) {
  98.                 Debug.Assert(iter.NodeType == QilNodeType.Let || iter.NodeType == QilNodeType.Parameter);
  99.                
  100.                 // Get metadata for method which computes this global's value
  101.                 methGlobal = XmlILAnnotation.Write(iter).FunctionBinding;
  102.                 Debug.Assert(methGlobal != null, "Metadata for global value should have already been computed");
  103.                
  104.                 // Create an IteratorDescriptor for this global value
  105.                 iterInfo = new IteratorDescriptor(this.helper);
  106.                
  107.                 // Iterator items will be stored in a global location
  108.                 iterInfo.Storage = StorageDescriptor.Global(methGlobal, GetItemStorageType(iter), !iter.XmlType.IsSingleton);
  109.                
  110.                 // Associate IteratorDescriptor with parameter
  111.                 XmlILAnnotation.Write(iter).CachedIteratorDescriptor = iterInfo;
  112.             }
  113.         }
  114.        
  115.         /// <summary>
  116.         /// Visit each global variable or parameter. Create a IteratorDescriptor for each global value. Generate code for
  117.         /// default values.
  118.         /// </summary>
  119.         private void VisitGlobalValues(QilList globalIterators)
  120.         {
  121.             MethodInfo methGlobal;
  122.             Label lblGetGlobal;
  123.             Label lblComputeGlobal;
  124.             bool isCached;
  125.             int idxValue;
  126.            
  127.             foreach (QilIterator iter in globalIterators) {
  128.                 QilParameter param = iter as QilParameter;
  129.                
  130.                 // Get MethodInfo for method that computes the value of this global
  131.                 methGlobal = XmlILAnnotation.Write(iter).CachedIteratorDescriptor.Storage.GlobalLocation;
  132.                 isCached = !iter.XmlType.IsSingleton;
  133.                
  134.                 // Notify the StaticDataManager of the new global value
  135.                 idxValue = this.helper.StaticData.DeclareGlobalValue(iter.DebugName);
  136.                
  137.                 // Generate code for this method
  138.                 this.helper.MethodBegin(methGlobal, iter.SourceLine, false);
  139.                
  140.                 lblGetGlobal = this.helper.DefineLabel();
  141.                 lblComputeGlobal = this.helper.DefineLabel();
  142.                
  143.                 // if (runtime.IsGlobalComputed(idx)) goto LabelGetGlobal;
  144.                 this.helper.LoadQueryRuntime();
  145.                 this.helper.LoadInteger(idxValue);
  146.                 this.helper.Call(XmlILMethods.GlobalComputed);
  147.                 this.helper.Emit(OpCodes.Brtrue, lblGetGlobal);
  148.                
  149.                 // Compute value of global value
  150.                 StartNestedIterator(iter);
  151.                
  152.                 if (param != null) {
  153.                     Debug.Assert(iter.XmlType == TypeFactory.ItemS, "IlGen currently only supports parameters of type item*.");
  154.                    
  155.                     // param = runtime.ExternalContext.GetParameter(localName, namespaceUri);
  156.                     // if (param == null) goto LabelComputeGlobal;
  157.                     LocalBuilder locParam = this.helper.DeclareLocal("$$$param", typeof(object));
  158.                     this.helper.CallGetParameter(param.Name.LocalName, param.Name.NamespaceUri);
  159.                     this.helper.Emit(OpCodes.Stloc, locParam);
  160.                     this.helper.Emit(OpCodes.Ldloc, locParam);
  161.                     this.helper.Emit(OpCodes.Brfalse, lblComputeGlobal);
  162.                    
  163.                     // runtime.SetGlobalValue(idxValue, runtime.ChangeTypeXsltResult(idxType, value));
  164.                     // Ensure that the storage type of the parameter corresponds to static type
  165.                     this.helper.LoadQueryRuntime();
  166.                     this.helper.LoadInteger(idxValue);
  167.                    
  168.                     this.helper.LoadQueryRuntime();
  169.                     this.helper.LoadInteger(this.helper.StaticData.DeclareXmlType(XmlQueryTypeFactory.ItemS));
  170.                     this.helper.Emit(OpCodes.Ldloc, locParam);
  171.                     this.helper.Call(XmlILMethods.ChangeTypeXsltResult);
  172.                    
  173.                     this.helper.CallSetGlobalValue(typeof(object));
  174.                    
  175.                     // goto LabelGetGlobal;
  176.                     this.helper.EmitUnconditionalBranch(OpCodes.Br, lblGetGlobal);
  177.                 }
  178.                
  179.                 // LabelComputeGlobal:
  180.                 this.helper.MarkLabel(lblComputeGlobal);
  181.                
  182.                 if (iter.Binding != null) {
  183.                     // runtime.SetGlobalValue(idxValue, (object) value);
  184.                     this.helper.LoadQueryRuntime();
  185.                     this.helper.LoadInteger(idxValue);
  186.                    
  187.                     // Compute value of global value
  188.                     NestedVisitEnsureStack(iter.Binding, GetItemStorageType(iter), isCached);
  189.                    
  190.                     this.helper.CallSetGlobalValue(GetStorageType(iter));
  191.                 }
  192.                 else {
  193.                     // Throw exception, as there is no default value for this parameter
  194.                     // XmlQueryRuntime.ThrowException("...");
  195.                     Debug.Assert(iter.NodeType == QilNodeType.Parameter, "Only parameters may not have a default value");
  196.                     this.helper.LoadQueryRuntime();
  197.                     this.helper.Emit(OpCodes.Ldstr, Res.GetString(Res.XmlIl_UnknownParam, new string[] {param.Name.LocalName, param.Name.NamespaceUri}));
  198.                     this.helper.Call(XmlILMethods.ThrowException);
  199.                 }
  200.                
  201.                 EndNestedIterator(iter);
  202.                
  203.                 // LabelGetGlobal:
  204.                 // return (T) runtime.GetGlobalValue(idxValue);
  205.                 this.helper.MarkLabel(lblGetGlobal);
  206.                 this.helper.CallGetGlobalValue(idxValue, GetStorageType(iter));
  207.                
  208.                 this.helper.MethodEnd();
  209.             }
  210.         }
  211.        
  212.         /// <summary>
  213.         /// Generate code for the specified function.
  214.         /// </summary>
  215.         private void Function(QilFunction ndFunc)
  216.         {
  217.             MethodInfo methFunc;
  218.             int paramId;
  219.             IteratorDescriptor iterInfo;
  220.             bool useWriter;
  221.            
  222.             // Annotate each function parameter with a IteratorDescriptor
  223.             foreach (QilIterator iter in ndFunc.Arguments) {
  224.                 Debug.Assert(iter.NodeType == QilNodeType.Parameter);
  225.                
  226.                 // Create an IteratorDescriptor for this parameter
  227.                 iterInfo = new IteratorDescriptor(this.helper);
  228.                
  229.                 // Add one to parameter index, as 0th parameter is always "this"
  230.                 paramId = XmlILAnnotation.Write(iter).ArgumentPosition + 1;
  231.                
  232.                 // The ParameterInfo for each argument should be set as its location
  233.                 iterInfo.Storage = StorageDescriptor.Parameter(paramId, GetItemStorageType(iter), !iter.XmlType.IsSingleton);
  234.                
  235.                 // Associate IteratorDescriptor with Let iterator
  236.                 XmlILAnnotation.Write(iter).CachedIteratorDescriptor = iterInfo;
  237.             }
  238.            
  239.             methFunc = XmlILAnnotation.Write(ndFunc).FunctionBinding;
  240.             useWriter = (XmlILConstructInfo.Read(ndFunc).ConstructMethod == XmlILConstructMethod.Writer);
  241.            
  242.             // Generate query code from QilExpression tree
  243.             this.helper.MethodBegin(methFunc, ndFunc.SourceLine, useWriter);
  244.            
  245.             foreach (QilIterator iter in ndFunc.Arguments) {
  246.                 // DebugInfo: Sequence point just before generating code for the bound expression
  247.                 if (this.qil.IsDebug && iter.SourceLine != null)
  248.                     this.helper.DebugSequencePoint(iter.SourceLine);
  249.                
  250.                 // Calculate default value of this parameter
  251.                 if (iter.Binding != null) {
  252.                     Debug.Assert(iter.XmlType == TypeFactory.ItemS, "IlGen currently only supports default values in parameters of type item*.");
  253.                     paramId = (iter.Annotation as XmlILAnnotation).ArgumentPosition + 1;
  254.                    
  255.                     // runtime.MatchesXmlType(param, XmlTypeCode.QName);
  256.                     Label lblLocalComputed = this.helper.DefineLabel();
  257.                     this.helper.LoadQueryRuntime();
  258.                     this.helper.LoadParameter(paramId);
  259.                     this.helper.LoadInteger((int)XmlTypeCode.QName);
  260.                     this.helper.Call(XmlILMethods.SeqMatchesCode);
  261.                    
  262.                     this.helper.Emit(OpCodes.Brfalse, lblLocalComputed);
  263.                    
  264.                     // Compute default value of this parameter
  265.                     StartNestedIterator(iter);
  266.                         /*isCached:*/                    NestedVisitEnsureStack(iter.Binding, GetItemStorageType(iter), !iter.XmlType.IsSingleton);
  267.                     EndNestedIterator(iter);
  268.                    
  269.                     this.helper.SetParameter(paramId);
  270.                     this.helper.MarkLabel(lblLocalComputed);
  271.                 }
  272.             }
  273.            
  274.             StartNestedIterator(ndFunc);
  275.            
  276.             // If function did not push results to writer, then function will return value(s) (rather than void)
  277.             if (useWriter)
  278.                 NestedVisit(ndFunc.Definition);
  279.             else
  280.                 NestedVisitEnsureStack(ndFunc.Definition, GetItemStorageType(ndFunc), !ndFunc.XmlType.IsSingleton);
  281.            
  282.             EndNestedIterator(ndFunc);
  283.            
  284.             this.helper.MethodEnd();
  285.         }
  286.        
  287.         //-----------------------------------------------
  288.         // QilVisitor
  289.         //-----------------------------------------------
  290.        
  291.         /// <summary>
  292.         /// Generate a query plan for the QilExpression subgraph.
  293.         /// </summary>
  294.         protected override QilNode Visit(QilNode nd)
  295.         {
  296.             if (nd == null)
  297.                 return null;
  298.            
  299.             // DebugInfo: Sequence point just before generating code for this expression
  300.             if (this.qil.IsDebug && nd.SourceLine != null && !(nd is QilIterator))
  301.                 this.helper.DebugSequencePoint(nd.SourceLine);
  302.            
  303.             // Expressions are constructed using one of several possible methods
  304.             switch (XmlILConstructInfo.Read(nd).ConstructMethod) {
  305.                 case XmlILConstructMethod.WriterThenIterator:
  306.                     // Push results of expression to cached writer; then iterate over cached results
  307.                     NestedConstruction(nd);
  308.                     break;
  309.                 case XmlILConstructMethod.IteratorThenWriter:
  310.                    
  311.                     // Iterate over items in the sequence; send items to writer
  312.                     CopySequence(nd);
  313.                     break;
  314.                 case XmlILConstructMethod.Iterator:
  315.                    
  316.                     Debug.Assert(nd.XmlType.IsSingleton || CachesResult(nd) || this.iterCurr.HasLabelNext, "When generating code for a non-singleton expression, LabelNext must be defined.");
  317.                     goto default;
  318.                     break;
  319.                 default:
  320.                    
  321.                     // Allow base internal class to dispatch to correct Visit method
  322.                     base.Visit(nd);
  323.                     break;
  324.             }
  325.            
  326.             return nd;
  327.         }
  328.        
  329.         /// <summary>
  330.         /// VisitChildren should never be called.
  331.         /// </summary>
  332.         protected override QilNode VisitChildren(QilNode parent)
  333.         {
  334.             Debug.Fail("Visit" + parent.NodeType + " should never be called");
  335.             return parent;
  336.         }
  337.        
  338.         /// <summary>
  339.         /// Generate code to cache a sequence of items that are pushed to output.
  340.         /// </summary>
  341.         private void NestedConstruction(QilNode nd)
  342.         {
  343.             // Start nested construction of a sequence of items
  344.             this.helper.CallStartSequenceConstruction();
  345.            
  346.             // Allow base internal class to dispatch to correct Visit method
  347.             base.Visit(nd);
  348.            
  349.             // Get the result sequence
  350.             this.helper.CallEndSequenceConstruction();
  351.             this.iterCurr.Storage = StorageDescriptor.Stack(typeof(XPathItem), true);
  352.         }
  353.        
  354.         /// <summary>
  355.         /// Iterate over items produced by the "nd" expression and copy each item to output.
  356.         /// </summary>
  357.         private void CopySequence(QilNode nd)
  358.         {
  359.             XmlQueryType typ = nd.XmlType;
  360.             bool hasOnEnd;
  361.             Label lblOnEnd;
  362.            
  363.             StartWriterLoop(nd, out hasOnEnd, out lblOnEnd);
  364.            
  365.             if (typ.IsSingleton) {
  366.                 // Always write atomic values via XmlQueryOutput
  367.                 this.helper.LoadQueryOutput();
  368.                
  369.                 // Allow base internal class to dispatch to correct Visit method
  370.                 base.Visit(nd);
  371.                 this.iterCurr.EnsureItemStorageType(nd.XmlType, typeof(XPathItem));
  372.             }
  373.             else {
  374.                 // Allow base internal class to dispatch to correct Visit method
  375.                 base.Visit(nd);
  376.                 this.iterCurr.EnsureItemStorageType(nd.XmlType, typeof(XPathItem));
  377.                
  378.                 // Save any stack values in a temporary local
  379.                 this.iterCurr.EnsureNoStackNoCache("$$$copyTemp");
  380.                
  381.                 this.helper.LoadQueryOutput();
  382.             }
  383.            
  384.             // Write value to output
  385.             this.iterCurr.EnsureStackNoCache();
  386.             this.helper.Call(XmlILMethods.WriteItem);
  387.            
  388.             EndWriterLoop(nd, hasOnEnd, lblOnEnd);
  389.         }
  390.        
  391.         /// <summary>
  392.         /// Generate code for QilNodeType.DataSource.
  393.         /// </summary>
  394.         /// <remarks>
  395.         /// Generates code to retrieve a document using the XmlResolver.
  396.         /// </remarks>
  397.         protected override QilNode VisitDataSource(QilDataSource ndSrc)
  398.         {
  399.             LocalBuilder locNav;
  400.            
  401.             // XPathNavigator navDoc = runtime.ExternalContext.GetEntity(uri)
  402.             this.helper.LoadQueryContext();
  403.             NestedVisitEnsureStack(ndSrc.Name);
  404.             NestedVisitEnsureStack(ndSrc.BaseUri);
  405.             this.helper.Call(XmlILMethods.GetDataSource);
  406.            
  407.             locNav = this.helper.DeclareLocal("$$$navDoc", typeof(XPathNavigator));
  408.             this.helper.Emit(OpCodes.Stloc, locNav);
  409.            
  410.             // if (navDoc == null) goto LabelNextCtxt;
  411.             this.helper.Emit(OpCodes.Ldloc, locNav);
  412.             this.helper.Emit(OpCodes.Brfalse, this.iterCurr.GetLabelNext());
  413.            
  414.             this.iterCurr.Storage = StorageDescriptor.Local(locNav, typeof(XPathNavigator), false);
  415.            
  416.             return ndSrc;
  417.         }
  418.        
  419.         /// <summary>
  420.         /// Generate code for QilNodeType.Nop.
  421.         /// </summary>
  422.         protected override QilNode VisitNop(QilUnary ndNop)
  423.         {
  424.             return Visit(ndNop.Child);
  425.         }
  426.        
  427.         /// <summary>
  428.         /// Generate code for QilNodeType.OptimizeBarrier.
  429.         /// </summary>
  430.         protected override QilNode VisitOptimizeBarrier(QilUnary ndBarrier)
  431.         {
  432.             return Visit(ndBarrier.Child);
  433.         }
  434.        
  435.         /// <summary>
  436.         /// Generate code for QilNodeType.Error.
  437.         /// </summary>
  438.         protected override QilNode VisitError(QilUnary ndErr)
  439.         {
  440.             // XmlQueryRuntime.ThrowException(strErr);
  441.             this.helper.LoadQueryRuntime();
  442.             NestedVisitEnsureStack(ndErr.Child);
  443.             this.helper.Call(XmlILMethods.ThrowException);
  444.            
  445.             if (XmlILConstructInfo.Read(ndErr).ConstructMethod == XmlILConstructMethod.Writer) {
  446.                 this.iterCurr.Storage = StorageDescriptor.None();
  447.             }
  448.             else {
  449.                 // Push dummy value so that Location is not None and IL rules are met
  450.                 this.helper.Emit(OpCodes.Ldnull);
  451.                 this.iterCurr.Storage = StorageDescriptor.Stack(typeof(XPathItem), false);
  452.             }
  453.            
  454.             return ndErr;
  455.         }
  456.        
  457.         /// <summary>
  458.         /// Generate code for QilNodeType.Warning.
  459.         /// </summary>
  460.         protected override QilNode VisitWarning(QilUnary ndWarning)
  461.         {
  462.             // runtime.SendMessage(strWarning);
  463.             this.helper.LoadQueryRuntime();
  464.             NestedVisitEnsureStack(ndWarning.Child);
  465.             this.helper.Call(XmlILMethods.SendMessage);
  466.            
  467.             if (XmlILConstructInfo.Read(ndWarning).ConstructMethod == XmlILConstructMethod.Writer)
  468.                 this.iterCurr.Storage = StorageDescriptor.None();
  469.             else
  470.                 VisitEmpty(ndWarning);
  471.            
  472.             return ndWarning;
  473.         }
  474.        
  475.         /// <summary>
  476.         /// Generate code for QilNodeType.True.
  477.         /// </summary>
  478.         /// <remarks>
  479.         /// BranchingContext.OnFalse context: [nothing]
  480.         /// BranchingContext.OnTrue context: goto LabelParent;
  481.         /// BranchingContext.None context: push true();
  482.         /// </remarks>
  483.         protected override QilNode VisitTrue(QilNode ndTrue)
  484.         {
  485.             if (this.iterCurr.CurrentBranchingContext != BranchingContext.None) {
  486.                 // Make sure there's an IL code path to both the true and false branches in order to avoid dead
  487.                 // code which can cause IL verification errors.
  488.                 this.helper.EmitUnconditionalBranch(this.iterCurr.CurrentBranchingContext == BranchingContext.OnTrue ? OpCodes.Brtrue : OpCodes.Brfalse, this.iterCurr.LabelBranch);
  489.                
  490.                 this.iterCurr.Storage = StorageDescriptor.None();
  491.             }
  492.             else {
  493.                 // Push boolean result onto the stack
  494.                 this.helper.LoadBoolean(true);
  495.                 this.iterCurr.Storage = StorageDescriptor.Stack(typeof(bool), false);
  496.             }
  497.            
  498.             return ndTrue;
  499.         }
  500.        
  501.         /// <summary>
  502.         /// Generate code for QilNodeType.False.
  503.         /// </summary>
  504.         /// <remarks>
  505.         /// BranchingContext.OnFalse context: goto LabelParent;
  506.         /// BranchingContext.OnTrue context: [nothing]
  507.         /// BranchingContext.None context: push false();
  508.         /// </remarks>
  509.         protected override QilNode VisitFalse(QilNode ndFalse)
  510.         {
  511.             if (this.iterCurr.CurrentBranchingContext != BranchingContext.None) {
  512.                 // Make sure there's an IL code path to both the true and false branches in order to avoid dead
  513.                 // code which can cause IL verification errors.
  514.                 this.helper.EmitUnconditionalBranch(this.iterCurr.CurrentBranchingContext == BranchingContext.OnFalse ? OpCodes.Brtrue : OpCodes.Brfalse, this.iterCurr.LabelBranch);
  515.                
  516.                 this.iterCurr.Storage = StorageDescriptor.None();
  517.             }
  518.             else {
  519.                 // Push boolean result onto the stack
  520.                 this.helper.LoadBoolean(false);
  521.                 this.iterCurr.Storage = StorageDescriptor.Stack(typeof(bool), false);
  522.             }
  523.            
  524.             return ndFalse;
  525.         }
  526.        
  527.         /// <summary>
  528.         /// Generate code for QilNodeType.LiteralString.
  529.         /// </summary>
  530.         protected override QilNode VisitLiteralString(QilLiteral ndStr)
  531.         {
  532.             this.helper.Emit(OpCodes.Ldstr, (string)ndStr);
  533.             this.iterCurr.Storage = StorageDescriptor.Stack(typeof(string), false);
  534.             return ndStr;
  535.         }
  536.        
  537.         /// <summary>
  538.         /// Generate code for QilNodeType.LiteralInt32.
  539.         /// </summary>
  540.         protected override QilNode VisitLiteralInt32(QilLiteral ndInt)
  541.         {
  542.             this.helper.LoadInteger((int)ndInt);
  543.             this.iterCurr.Storage = StorageDescriptor.Stack(typeof(int), false);
  544.             return ndInt;
  545.         }
  546.        
  547.         /// <summary>
  548.         /// Generate code for QilNodeType.LiteralInt64.
  549.         /// </summary>
  550.         protected override QilNode VisitLiteralInt64(QilLiteral ndLong)
  551.         {
  552.             this.helper.Emit(OpCodes.Ldc_I8, (long)ndLong);
  553.             this.iterCurr.Storage = StorageDescriptor.Stack(typeof(long), false);
  554.             return ndLong;
  555.         }
  556.        
  557.         /// <summary>
  558.         /// Generate code for QilNodeType.LiteralDouble.
  559.         /// </summary>
  560.         protected override QilNode VisitLiteralDouble(QilLiteral ndDbl)
  561.         {
  562.             this.helper.Emit(OpCodes.Ldc_R8, (double)ndDbl);
  563.             this.iterCurr.Storage = StorageDescriptor.Stack(typeof(double), false);
  564.             return ndDbl;
  565.         }
  566.        
  567.         /// <summary>
  568.         /// Generate code for QilNodeType.LiteralDecimal.
  569.         /// </summary>
  570.         protected override QilNode VisitLiteralDecimal(QilLiteral ndDec)
  571.         {
  572.             this.helper.ConstructLiteralDecimal((decimal)ndDec);
  573.             this.iterCurr.Storage = StorageDescriptor.Stack(typeof(decimal), false);
  574.             return ndDec;
  575.         }
  576.        
  577.         /// <summary>
  578.         /// Generate code for QilNodeType.LiteralQName.
  579.         /// </summary>
  580.         protected override QilNode VisitLiteralQName(QilName ndQName)
  581.         {
  582.             this.helper.ConstructLiteralQName(ndQName.LocalName, ndQName.NamespaceUri);
  583.             this.iterCurr.Storage = StorageDescriptor.Stack(typeof(XmlQualifiedName), false);
  584.             return ndQName;
  585.         }
  586.        
  587.         /// <summary>
  588.         /// Generate code for QilNodeType.And.
  589.         /// </summary>
  590.         /// <remarks>
  591.         /// BranchingContext.OnFalse context: (expr1) and (expr2)
  592.         /// ==> if (!expr1) goto LabelParent;
  593.         /// if (!expr2) goto LabelParent;
  594.         ///
  595.         /// BranchingContext.OnTrue context: (expr1) and (expr2)
  596.         /// ==> if (!expr1) goto LabelTemp;
  597.         /// if (expr1) goto LabelParent;
  598.         /// LabelTemp:
  599.         ///
  600.         /// BranchingContext.None context: (expr1) and (expr2)
  601.         /// ==> if (!expr1) goto LabelTemp;
  602.         /// if (!expr1) goto LabelTemp;
  603.         /// push true();
  604.         /// goto LabelSkip;
  605.         /// LabelTemp:
  606.         /// push false();
  607.         /// LabelSkip:
  608.         ///
  609.         /// </remarks>
  610.         protected override QilNode VisitAnd(QilBinary ndAnd)
  611.         {
  612.             IteratorDescriptor iterParent = this.iterCurr;
  613.             Label lblOnFalse;
  614.            
  615.             // Visit left branch
  616.             StartNestedIterator(ndAnd.Left);
  617.             lblOnFalse = StartConjunctiveTests(iterParent.CurrentBranchingContext, iterParent.LabelBranch);
  618.             Visit(ndAnd.Left);
  619.             EndNestedIterator(ndAnd.Left);
  620.            
  621.             // Visit right branch
  622.             StartNestedIterator(ndAnd.Right);
  623.             StartLastConjunctiveTest(iterParent.CurrentBranchingContext, iterParent.LabelBranch, lblOnFalse);
  624.             Visit(ndAnd.Right);
  625.             EndNestedIterator(ndAnd.Right);
  626.            
  627.             // End And expression
  628.             EndConjunctiveTests(iterParent.CurrentBranchingContext, iterParent.LabelBranch, lblOnFalse);
  629.            
  630.             return ndAnd;
  631.         }
  632.        
  633.         /// <summary>
  634.         /// Fixup branching context for all but the last test in a conjunctive (Logical And) expression.
  635.         /// Return a temporary label which will be passed to StartLastAndBranch() and EndAndBranch().
  636.         /// </summary>
  637.         private Label StartConjunctiveTests(BranchingContext brctxt, Label lblBranch)
  638.         {
  639.             Label lblOnFalse;
  640.            
  641.             switch (brctxt) {
  642.                 case BranchingContext.OnFalse:
  643.                     // If condition evaluates to false, branch to false label
  644.                     this.iterCurr.SetBranching(BranchingContext.OnFalse, lblBranch);
  645.                     return lblBranch;
  646.                 default:
  647.                    
  648.                     // If condition evaluates to false:
  649.                     // 1. Jump to new false label that will be fixed just beyond the second condition
  650.                     // 2. Or, jump to code that pushes "false"
  651.                     lblOnFalse = this.helper.DefineLabel();
  652.                     this.iterCurr.SetBranching(BranchingContext.OnFalse, lblOnFalse);
  653.                     return lblOnFalse;
  654.             }
  655.         }
  656.        
  657.         /// <summary>
  658.         /// Fixup branching context for the last test in a conjunctive (Logical And) expression.
  659.         /// </summary>
  660.         private void StartLastConjunctiveTest(BranchingContext brctxt, Label lblBranch, Label lblOnFalse)
  661.         {
  662.             switch (brctxt) {
  663.                 case BranchingContext.OnTrue:
  664.                     // If last condition evaluates to true, branch to true label
  665.                     this.iterCurr.SetBranching(BranchingContext.OnTrue, lblBranch);
  666.                     break;
  667.                 default:
  668.                    
  669.                     // If last condition evalutes to false, branch to false label
  670.                     // Else fall through to true code path
  671.                     this.iterCurr.SetBranching(BranchingContext.OnFalse, lblOnFalse);
  672.                     break;
  673.             }
  674.         }
  675.        
  676.         /// <summary>
  677.         /// Anchor any remaining labels.
  678.         /// </summary>
  679.         private void EndConjunctiveTests(BranchingContext brctxt, Label lblBranch, Label lblOnFalse)
  680.         {
  681.             switch (brctxt) {
  682.                 case BranchingContext.OnTrue:
  683.                     // Anchor false label
  684.                     this.helper.MarkLabel(lblOnFalse);
  685.                     goto case BranchingContext.OnFalse;
  686.                     break;
  687.                 case BranchingContext.OnFalse:
  688.                    
  689.                     this.iterCurr.Storage = StorageDescriptor.None();
  690.                     break;
  691.                 case BranchingContext.None:
  692.                    
  693.                     // Convert branch targets into push of true/false
  694.                     this.helper.ConvBranchToBool(lblOnFalse, false);
  695.                     this.iterCurr.Storage = StorageDescriptor.Stack(typeof(bool), false);
  696.                     break;
  697.             }
  698.         }
  699.        
  700.         /// <summary>
  701.         /// Generate code for QilNodeType.Or.
  702.         /// </summary>
  703.         /// <remarks>
  704.         /// BranchingContext.OnFalse context: (expr1) or (expr2)
  705.         /// ==> if (expr1) goto LabelTemp;
  706.         /// if (!expr2) goto LabelParent;
  707.         /// LabelTemp:
  708.         ///
  709.         /// BranchingContext.OnTrue context: (expr1) or (expr2)
  710.         /// ==> if (expr1) goto LabelParent;
  711.         /// if (expr1) goto LabelParent;
  712.         ///
  713.         /// BranchingContext.None context: (expr1) or (expr2)
  714.         /// ==> if (expr1) goto LabelTemp;
  715.         /// if (expr1) goto LabelTemp;
  716.         /// push false();
  717.         /// goto LabelSkip;
  718.         /// LabelTemp:
  719.         /// push true();
  720.         /// LabelSkip:
  721.         ///
  722.         /// </remarks>
  723.         protected override QilNode VisitOr(QilBinary ndOr)
  724.         {
  725.             Label lblTemp = new Label();
  726.            
  727.             // Visit left branch
  728.             switch (this.iterCurr.CurrentBranchingContext) {
  729.                 case BranchingContext.OnFalse:
  730.                     // If left condition evaluates to true, jump to new label that will be fixed
  731.                     // just beyond the second condition
  732.                     lblTemp = this.helper.DefineLabel();
  733.                     NestedVisitWithBranch(ndOr.Left, BranchingContext.OnTrue, lblTemp);
  734.                     break;
  735.                 case BranchingContext.OnTrue:
  736.                    
  737.                     // If left condition evaluates to true, branch to true label
  738.                     NestedVisitWithBranch(ndOr.Left, BranchingContext.OnTrue, this.iterCurr.LabelBranch);
  739.                     break;
  740.                 default:
  741.                    
  742.                     // If left condition evalutes to true, jump to code that pushes "true"
  743.                     Debug.Assert(this.iterCurr.CurrentBranchingContext == BranchingContext.None);
  744.                     lblTemp = this.helper.DefineLabel();
  745.                     NestedVisitWithBranch(ndOr.Left, BranchingContext.OnTrue, lblTemp);
  746.                     break;
  747.             }
  748.            
  749.             // Visit right branch
  750.             switch (this.iterCurr.CurrentBranchingContext) {
  751.                 case BranchingContext.OnFalse:
  752.                     // If right condition evaluates to false, branch to false label
  753.                     NestedVisitWithBranch(ndOr.Right, BranchingContext.OnFalse, this.iterCurr.LabelBranch);
  754.                     break;
  755.                 case BranchingContext.OnTrue:
  756.                    
  757.                     // If right condition evaluates to true, branch to true label
  758.                     NestedVisitWithBranch(ndOr.Right, BranchingContext.OnTrue, this.iterCurr.LabelBranch);
  759.                     break;
  760.                 default:
  761.                    
  762.                     // If right condition evalutes to true, jump to code that pushes "true".
  763.                     // Otherwise, if both conditions evaluate to false, fall through code path
  764.                     // will push "false".
  765.                     NestedVisitWithBranch(ndOr.Right, BranchingContext.OnTrue, lblTemp);
  766.                     break;
  767.             }
  768.            
  769.             switch (this.iterCurr.CurrentBranchingContext) {
  770.                 case BranchingContext.OnFalse:
  771.                     // Anchor true label
  772.                     this.helper.MarkLabel(lblTemp);
  773.                     goto case BranchingContext.OnTrue;
  774.                     break;
  775.                 case BranchingContext.OnTrue:
  776.                    
  777.                     this.iterCurr.Storage = StorageDescriptor.None();
  778.                     break;
  779.                 case BranchingContext.None:
  780.                    
  781.                     // Convert branch targets into push of true/false
  782.                     this.helper.ConvBranchToBool(lblTemp, true);
  783.                     this.iterCurr.Storage = StorageDescriptor.Stack(typeof(bool), false);
  784.                     break;
  785.             }
  786.            
  787.             return ndOr;
  788.         }
  789.        
  790.         /// <summary>
  791.         /// Generate code for QilNodeType.Not.
  792.         /// </summary>
  793.         /// <remarks>
  794.         /// BranchingContext.OnFalse context: not(expr1)
  795.         /// ==> if (expr1) goto LabelParent;
  796.         ///
  797.         /// BranchingContext.OnTrue context: not(expr1)
  798.         /// ==> if (!expr1) goto LabelParent;
  799.         ///
  800.         /// BranchingContext.None context: not(expr1)
  801.         /// ==> if (expr1) goto LabelTemp;
  802.         /// push false();
  803.         /// goto LabelSkip;
  804.         /// LabelTemp:
  805.         /// push true();
  806.         /// LabelSkip:
  807.         ///
  808.         /// </remarks>
  809.         protected override QilNode VisitNot(QilUnary ndNot)
  810.         {
  811.             Label lblTemp = new Label();
  812.            
  813.             // Visit operand
  814.             // Reverse branch types
  815.             switch (this.iterCurr.CurrentBranchingContext) {
  816.                 case BranchingContext.OnFalse:
  817.                     NestedVisitWithBranch(ndNot.Child, BranchingContext.OnTrue, this.iterCurr.LabelBranch);
  818.                     break;
  819.                 case BranchingContext.OnTrue:
  820.                    
  821.                     NestedVisitWithBranch(ndNot.Child, BranchingContext.OnFalse, this.iterCurr.LabelBranch);
  822.                     break;
  823.                 default:
  824.                    
  825.                     // Replace boolean argument on top of stack with its inverse
  826.                     Debug.Assert(this.iterCurr.CurrentBranchingContext == BranchingContext.None);
  827.                     lblTemp = this.helper.DefineLabel();
  828.                     NestedVisitWithBranch(ndNot.Child, BranchingContext.OnTrue, lblTemp);
  829.                     break;
  830.             }
  831.            
  832.             if (this.iterCurr.CurrentBranchingContext == BranchingContext.None) {
  833.                 // If condition evaluates to true, then jump to code that pushes false
  834.                 this.helper.ConvBranchToBool(lblTemp, false);
  835.                 this.iterCurr.Storage = StorageDescriptor.Stack(typeof(bool), false);
  836.             }
  837.             else {
  838.                 this.iterCurr.Storage = StorageDescriptor.None();
  839.             }
  840.            
  841.             return ndNot;
  842.         }
  843.        
  844.         /// <summary>
  845.         /// Generate code for QilNodeType.Conditional.
  846.         /// </summary>
  847.         protected override QilNode VisitConditional(QilTernary ndCond)
  848.         {
  849.             XmlILConstructInfo info = XmlILConstructInfo.Read(ndCond);
  850.            
  851.             if (info.ConstructMethod == XmlILConstructMethod.Writer) {
  852.                 Label lblFalse;
  853.                 Label lblDone;
  854.                
  855.                 // Evaluate if test
  856.                 lblFalse = this.helper.DefineLabel();
  857.                 NestedVisitWithBranch(ndCond.Left, BranchingContext.OnFalse, lblFalse);
  858.                
  859.                 // Generate true branch code
  860.                 NestedVisit(ndCond.Center);
  861.                
  862.                 // Generate false branch code. If false branch is the empty list,
  863.                 if (ndCond.Right.NodeType == QilNodeType.Sequence && ndCond.Right.Count == 0) {
  864.                     // Then generate simplified code that doesn't contain a false branch
  865.                     this.helper.MarkLabel(lblFalse);
  866.                     NestedVisit(ndCond.Right);
  867.                 }
  868.                 else {
  869.                     // Jump past false branch
  870.                     lblDone = this.helper.DefineLabel();
  871.                     this.helper.EmitUnconditionalBranch(OpCodes.Br, lblDone);
  872.                    
  873.                     // Generate false branch code
  874.                     this.helper.MarkLabel(lblFalse);
  875.                     NestedVisit(ndCond.Right);
  876.                    
  877.                     this.helper.MarkLabel(lblDone);
  878.                 }
  879.                
  880.                 this.iterCurr.Storage = StorageDescriptor.None();
  881.             }
  882.             else {
  883.                 IteratorDescriptor iterInfoTrue;
  884.                 LocalBuilder locBool = null;
  885.                 LocalBuilder locCond = null;
  886.                 Label lblFalse;
  887.                 Label lblDone;
  888.                 Label lblNext;
  889.                 Type itemStorageType = GetItemStorageType(ndCond);
  890.                 Debug.Assert(info.ConstructMethod == XmlILConstructMethod.Iterator);
  891.                
  892.                 // Evaluate conditional test -- save boolean result in boolResult
  893.                 Debug.Assert(ndCond.Left.XmlType.TypeCode == XmlTypeCode.Boolean);
  894.                 lblFalse = this.helper.DefineLabel();
  895.                
  896.                 if (ndCond.XmlType.IsSingleton) {
  897.                     // if (!bool-expr) goto LabelFalse;
  898.                     NestedVisitWithBranch(ndCond.Left, BranchingContext.OnFalse, lblFalse);
  899.                 }
  900.                 else {
  901.                     // CondType itemCond;
  902.                     // int boolResult = bool-expr;
  903.                     locCond = this.helper.DeclareLocal("$$$cond", itemStorageType);
  904.                     locBool = this.helper.DeclareLocal("$$$boolResult", typeof(bool));
  905.                     NestedVisitEnsureLocal(ndCond.Left, locBool);
  906.                    
  907.                     // if (!boolResult) goto LabelFalse;
  908.                     this.helper.Emit(OpCodes.Ldloc, locBool);
  909.                     this.helper.Emit(OpCodes.Brfalse, lblFalse);
  910.                 }
  911.                
  912.                 // Generate code for true branch
  913.                 ConditionalBranch(ndCond.Center, itemStorageType, locCond);
  914.                 iterInfoTrue = this.iterNested;
  915.                
  916.                 // goto LabelDone;
  917.                 lblDone = this.helper.DefineLabel();
  918.                 this.helper.EmitUnconditionalBranch(OpCodes.Br, lblDone);
  919.                
  920.                 // Generate code for false branch
  921.                 // LabelFalse:
  922.                 this.helper.MarkLabel(lblFalse);
  923.                 ConditionalBranch(ndCond.Right, itemStorageType, locCond);
  924.                
  925.                 // If conditional is not cardinality one, then need to iterate through all values
  926.                 if (!ndCond.XmlType.IsSingleton) {
  927.                     Debug.Assert(!ndCond.Center.XmlType.IsSingleton || !ndCond.Right.XmlType.IsSingleton);
  928.                    
  929.                     // IL's rules do not allow OpCodes.Br here
  930.                     // goto LabelDone;
  931.                     this.helper.EmitUnconditionalBranch(OpCodes.Brtrue, lblDone);
  932.                    
  933.                     // LabelNext:
  934.                     lblNext = this.helper.DefineLabel();
  935.                     this.helper.MarkLabel(lblNext);
  936.                    
  937.                     // if (boolResult) goto LabelNextTrue else goto LabelNextFalse;
  938.                     this.helper.Emit(OpCodes.Ldloc, locBool);
  939.                     this.helper.Emit(OpCodes.Brtrue, iterInfoTrue.GetLabelNext());
  940.                     this.helper.EmitUnconditionalBranch(OpCodes.Br, this.iterNested.GetLabelNext());
  941.                    
  942.                     this.iterCurr.SetIterator(lblNext, StorageDescriptor.Local(locCond, itemStorageType, false));
  943.                 }
  944.                
  945.                 // LabelDone:
  946.                 this.helper.MarkLabel(lblDone);
  947.             }
  948.            
  949.             return ndCond;
  950.         }
  951.        
  952.         /// <summary>
  953.         /// Generate code for one of the branches of QilNodeType.Conditional.
  954.         /// </summary>
  955.         private void ConditionalBranch(QilNode ndBranch, Type itemStorageType, LocalBuilder locResult)
  956.         {
  957.             if (locResult == null) {
  958.                 Debug.Assert(ndBranch.XmlType.IsSingleton, "Conditional must produce a singleton");
  959.                
  960.                 // If in a branching context, then inherit branch target from parent context
  961.                 if (this.iterCurr.IsBranching) {
  962.                     Debug.Assert(itemStorageType == typeof(bool));
  963.                     NestedVisitWithBranch(ndBranch, this.iterCurr.CurrentBranchingContext, this.iterCurr.LabelBranch);
  964.                 }
  965.                 else {
  966.                     NestedVisitEnsureStack(ndBranch, itemStorageType, false);
  967.                 }
  968.             }
  969.             else {
  970.                 // Link nested iterator to parent conditional's iterator
  971.                 NestedVisit(ndBranch, this.iterCurr.GetLabelNext());
  972.                 this.iterCurr.EnsureItemStorageType(ndBranch.XmlType, itemStorageType);
  973.                 this.iterCurr.EnsureLocalNoCache(locResult);
  974.             }
  975.         }
  976.        
  977.         /// <summary>
  978.         /// Generate code for QilNodeType.Choice.
  979.         /// </summary>
  980.         protected override QilNode VisitChoice(QilChoice ndChoice)
  981.         {
  982.             QilNode ndBranches;
  983.             Label[] switchLabels;
  984.             Label lblOtherwise;
  985.             Label lblDone;
  986.             int regBranches;
  987.             int idx;
  988.             Debug.Assert(XmlILConstructInfo.Read(ndChoice).PushToWriterFirst);
  989.            
  990.             // Evaluate the expression
  991.             NestedVisit(ndChoice.Expression);
  992.            
  993.             // Generate switching code
  994.             ndBranches = ndChoice.Branches;
  995.             regBranches = ndBranches.Count - 1;
  996.             switchLabels = new Label[regBranches];
  997.             for (idx = 0; idx < regBranches; idx++)
  998.                 switchLabels[idx] = this.helper.DefineLabel();
  999.            
  1000.             lblOtherwise = this.helper.DefineLabel();
  1001.             lblDone = this.helper.DefineLabel();
  1002.            
  1003.             // switch (value)
  1004.             // case 0: goto Label[0];
  1005.             // ...
  1006.             // case N-1: goto Label[N-1];
  1007.             // default: goto LabelOtherwise;
  1008.             this.helper.Emit(OpCodes.Switch, switchLabels);
  1009.             this.helper.EmitUnconditionalBranch(OpCodes.Br, lblOtherwise);
  1010.            
  1011.             for (idx = 0; idx < regBranches; idx++) {
  1012.                 // Label[i]:
  1013.                 this.helper.MarkLabel(switchLabels[idx]);
  1014.                
  1015.                 // Generate regular branch code
  1016.                 NestedVisit(ndBranches[idx]);
  1017.                
  1018.                 // goto LabelDone
  1019.                 this.helper.EmitUnconditionalBranch(OpCodes.Br, lblDone);
  1020.             }
  1021.            
  1022.             // LabelOtherwise:
  1023.             this.helper.MarkLabel(lblOtherwise);
  1024.            
  1025.             // Generate otherwise branch code
  1026.             NestedVisit(ndBranches[idx]);
  1027.            
  1028.             // LabelDone:
  1029.             this.helper.MarkLabel(lblDone);
  1030.            
  1031.             this.iterCurr.Storage = StorageDescriptor.None();
  1032.            
  1033.             return ndChoice;
  1034.         }
  1035.        
  1036.         /// <summary>
  1037.         /// Generate code for QilNodeType.Length.
  1038.         /// </summary>
  1039.         /// <remarks>
  1040.         /// int length = 0;
  1041.         /// foreach (item in expr)
  1042.         /// length++;
  1043.         /// </remarks>
  1044.         protected override QilNode VisitLength(QilUnary ndSetLen)
  1045.         {
  1046.             Label lblOnEnd = this.helper.DefineLabel();
  1047.             OptimizerPatterns patt = OptimizerPatterns.Read(ndSetLen);
  1048.            
  1049.             if (CachesResult(ndSetLen.Child)) {
  1050.                 NestedVisitEnsureStack(ndSetLen.Child);
  1051.                 this.helper.CallCacheCount(this.iterNested.Storage.ItemStorageType);
  1052.             }
  1053.             else {
  1054.                 // length = 0;
  1055.                 this.helper.Emit(OpCodes.Ldc_I4_0);
  1056.                
  1057.                 StartNestedIterator(ndSetLen.Child, lblOnEnd);
  1058.                
  1059.                 // foreach (item in expr) {
  1060.                 Visit(ndSetLen.Child);
  1061.                
  1062.                 // Pop values of SetLength expression from the stack if necessary
  1063.                 this.iterCurr.EnsureNoCache();
  1064.                 this.iterCurr.DiscardStack();
  1065.                
  1066.                 // length++;
  1067.                 this.helper.Emit(OpCodes.Ldc_I4_1);
  1068.                 this.helper.Emit(OpCodes.Add);
  1069.                
  1070.                 if (patt.MatchesPattern(OptimizerPatternName.MaxPosition)) {
  1071.                     // Short-circuit rest of loop if max position has been exceeded
  1072.                     this.helper.Emit(OpCodes.Dup);
  1073.                     this.helper.LoadInteger((int)patt.GetArgument(OptimizerPatternArgument.MaxPosition));
  1074.                     this.helper.Emit(OpCodes.Bgt, lblOnEnd);
  1075.                 }
  1076.                
  1077.                 // }
  1078.                 this.iterCurr.LoopToEnd(lblOnEnd);
  1079.                
  1080.                 EndNestedIterator(ndSetLen.Child);
  1081.             }
  1082.            
  1083.             this.iterCurr.Storage = StorageDescriptor.Stack(typeof(int), false);
  1084.            
  1085.             return ndSetLen;
  1086.         }
  1087.        
  1088.         /// <summary>
  1089.         /// Find physical query plan for QilNodeType.Sequence.
  1090.         /// </summary>
  1091.         protected override QilNode VisitSequence(QilList ndSeq)
  1092.         {
  1093.             if (XmlILConstructInfo.Read(ndSeq).ConstructMethod == XmlILConstructMethod.Writer) {
  1094.                 // Push each item in the list to output
  1095.                 foreach (QilNode nd in ndSeq)
  1096.                     NestedVisit(nd);
  1097.             }
  1098.             else {
  1099.                 // Empty sequence is special case
  1100.                 if (ndSeq.Count == 0)
  1101.                     VisitEmpty(ndSeq);
  1102.                 else
  1103.                     Sequence(ndSeq);
  1104.             }
  1105.            
  1106.             return ndSeq;
  1107.         }
  1108.        
  1109.         /// <summary>
  1110.         /// Generate code for the empty sequence.
  1111.         /// </summary>
  1112.         private void VisitEmpty(QilNode nd)
  1113.         {
  1114.             Debug.Assert(XmlILConstructInfo.Read(nd).PullFromIteratorFirst, "VisitEmpty should only be called if items are iterated");
  1115.            
  1116.             // IL's rules prevent OpCodes.Br here
  1117.             // Empty sequence
  1118.             this.helper.EmitUnconditionalBranch(OpCodes.Brtrue, this.iterCurr.GetLabelNext());
  1119.            
  1120.             // Push dummy value so that Location is not None and IL rules are met
  1121.             this.helper.Emit(OpCodes.Ldnull);
  1122.             this.iterCurr.Storage = StorageDescriptor.Stack(typeof(XPathItem), false);
  1123.         }
  1124.        
  1125.         /// <summary>
  1126.         /// Generate code for QilNodeType.Sequence, when sort-merging to retain document order is not necessary.
  1127.         /// </summary>
  1128.         private void Sequence(QilList ndSeq)
  1129.         {
  1130.             LocalBuilder locIdx;
  1131.             LocalBuilder locList;
  1132.             Label lblStart;
  1133.             Label lblNext;
  1134.             Label lblOnEnd = new Label();
  1135.             Label[] arrSwitchLabels;
  1136.             int i;
  1137.             Type itemStorageType = GetItemStorageType(ndSeq);
  1138.             Debug.Assert(XmlILConstructInfo.Read(ndSeq).ConstructMethod == XmlILConstructMethod.Iterator, "This method should only be called if items in list are pulled from a code iterator.");
  1139.            
  1140.             // Singleton list is a special case if in addition to the singleton there are warnings or errors which should be executed
  1141.             if (ndSeq.XmlType.IsSingleton) {
  1142.                 foreach (QilNode nd in ndSeq) {
  1143.                     // Generate nested iterator's code
  1144.                     if (nd.XmlType.IsSingleton) {
  1145.                         NestedVisitEnsureStack(nd);
  1146.                     }
  1147.                     else {
  1148.                         lblOnEnd = this.helper.DefineLabel();
  1149.                         NestedVisit(nd, lblOnEnd);
  1150.                         this.iterCurr.DiscardStack();
  1151.                         this.helper.MarkLabel(lblOnEnd);
  1152.                     }
  1153.                 }
  1154.                 this.iterCurr.Storage = StorageDescriptor.Stack(itemStorageType, false);
  1155.             }
  1156.             else {
  1157.                 // Type itemList;
  1158.                 // int idxList;
  1159.                 locList = this.helper.DeclareLocal("$$$itemList", itemStorageType);
  1160.                 locIdx = this.helper.DeclareLocal("$$$idxList", typeof(int));
  1161.                
  1162.                 arrSwitchLabels = new Label[ndSeq.Count];
  1163.                 lblStart = this.helper.DefineLabel();
  1164.                
  1165.                 for (i = 0; i < ndSeq.Count; i++) {
  1166.                     // LabelOnEnd[i - 1]:
  1167.                     // When previous nested iterator is exhausted, it should jump to this (the next) iterator
  1168.                     if (i != 0)
  1169.                         this.helper.MarkLabel(lblOnEnd);
  1170.                    
  1171.                     // Create new LabelOnEnd for all but the last iterator, which jumps back to parent iterator when exhausted
  1172.                     if (i == ndSeq.Count - 1)
  1173.                         lblOnEnd = this.iterCurr.GetLabelNext();
  1174.                     else
  1175.                         lblOnEnd = this.helper.DefineLabel();
  1176.                    
  1177.                     // idxList = [i];
  1178.                     this.helper.LoadInteger(i);
  1179.                     this.helper.Emit(OpCodes.Stloc, locIdx);
  1180.                    
  1181.                     // Generate nested iterator's code
  1182.                     NestedVisit(ndSeq[i], lblOnEnd);
  1183.                    
  1184.                     // Result of list should be saved to a common type and location
  1185.                     this.iterCurr.EnsureItemStorageType(ndSeq[i].XmlType, itemStorageType);
  1186.                     this.iterCurr.EnsureLocalNoCache(locList);
  1187.                    
  1188.                     // Switch statement will jump to nested iterator's LabelNext
  1189.                     arrSwitchLabels[i] = this.iterNested.GetLabelNext();
  1190.                    
  1191.                     // IL's rules prevent OpCodes.Br here
  1192.                     // goto LabelStart;
  1193.                     this.helper.EmitUnconditionalBranch(OpCodes.Brtrue, lblStart);
  1194.                 }
  1195.                
  1196.                 // LabelNext:
  1197.                 lblNext = this.helper.DefineLabel();
  1198.                 this.helper.MarkLabel(lblNext);
  1199.                
  1200.                 // switch (idxList)
  1201.                 // case 0: goto LabelNext1;
  1202.                 // ...
  1203.                 // case N-1: goto LabelNext[N];
  1204.                 this.helper.Emit(OpCodes.Ldloc, locIdx);
  1205.                 this.helper.Emit(OpCodes.Switch, arrSwitchLabels);
  1206.                
  1207.                 // LabelStart:
  1208.                 this.helper.MarkLabel(lblStart);
  1209.                
  1210.                 this.iterCurr.SetIterator(lblNext, StorageDescriptor.Local(locList, itemStorageType, false));
  1211.             }
  1212.         }
  1213.        
  1214.         /// <summary>
  1215.         /// Generate code for QilNodeType.Union.
  1216.         /// </summary>
  1217.         protected override QilNode VisitUnion(QilBinary ndUnion)
  1218.         {
  1219.             return CreateSetIterator(ndUnion, "$$$iterUnion", typeof(UnionIterator), XmlILMethods.UnionCreate, XmlILMethods.UnionNext);
  1220.         }
  1221.        
  1222.         /// <summary>
  1223.         /// Generate code for QilNodeType.Intersection.
  1224.         /// </summary>
  1225.         protected override QilNode VisitIntersection(QilBinary ndInter)
  1226.         {
  1227.             return CreateSetIterator(ndInter, "$$$iterInter", typeof(IntersectIterator), XmlILMethods.InterCreate, XmlILMethods.InterNext);
  1228.         }
  1229.        
  1230.         /// <summary>
  1231.         /// Generate code for QilNodeType.Difference.
  1232.         /// </summary>
  1233.         protected override QilNode VisitDifference(QilBinary ndDiff)
  1234.         {
  1235.             return CreateSetIterator(ndDiff, "$$$iterDiff", typeof(DifferenceIterator), XmlILMethods.DiffCreate, XmlILMethods.DiffNext);
  1236.         }
  1237.        
  1238.         /// <summary>
  1239.         /// Generate code to combine nodes from two nested iterators using Union, Intersection, or Difference semantics.
  1240.         /// </summary>
  1241.         private QilNode CreateSetIterator(QilBinary ndSet, string iterName, Type iterType, MethodInfo methCreate, MethodInfo methNext)
  1242.         {
  1243.             LocalBuilder locIter;
  1244.             LocalBuilder locNav;
  1245.             Label lblNext;
  1246.             Label lblCall;
  1247.             Label lblNextLeft;
  1248.             Label lblNextRight;
  1249.             Label lblInitRight;
  1250.            
  1251.             // SetIterator iterSet;
  1252.             // XPathNavigator navSet;
  1253.             locIter = this.helper.DeclareLocal(iterName, iterType);
  1254.             locNav = this.helper.DeclareLocal("$$$navSet", typeof(XPathNavigator));
  1255.            
  1256.             // iterSet.Create(runtime);
  1257.             this.helper.Emit(OpCodes.Ldloca, locIter);
  1258.             this.helper.LoadQueryRuntime();
  1259.             this.helper.Call(methCreate);
  1260.            
  1261.             // Define labels that will be used
  1262.             lblNext = this.helper.DefineLabel();
  1263.             lblCall = this.helper.DefineLabel();
  1264.             lblInitRight = this.helper.DefineLabel();
  1265.            
  1266.             // Generate left nested iterator. When it is empty, it will branch to lblNext.
  1267.             // goto LabelCall;
  1268.             NestedVisit(ndSet.Left, lblNext);
  1269.             lblNextLeft = this.iterNested.GetLabelNext();
  1270.             this.iterCurr.EnsureLocal(locNav);
  1271.             this.helper.EmitUnconditionalBranch(OpCodes.Brtrue, lblCall);
  1272.            
  1273.             // Generate right nested iterator. When it is empty, it will branch to lblNext.
  1274.             // LabelInitRight:
  1275.             // goto LabelCall;
  1276.             this.helper.MarkLabel(lblInitRight);
  1277.             NestedVisit(ndSet.Right, lblNext);
  1278.             lblNextRight = this.iterNested.GetLabelNext();
  1279.             this.iterCurr.EnsureLocal(locNav);
  1280.             this.helper.EmitUnconditionalBranch(OpCodes.Brtrue, lblCall);
  1281.            
  1282.             // LabelNext:
  1283.             this.helper.MarkLabel(lblNext);
  1284.             this.helper.Emit(OpCodes.Ldnull);
  1285.             this.helper.Emit(OpCodes.Stloc, locNav);
  1286.            
  1287.             // LabelCall:
  1288.             // switch (iterSet.MoveNext(nestedNested)) {
  1289.             // case SetIteratorResult.NoMoreNodes: goto LabelNextCtxt;
  1290.             // case SetIteratorResult.InitRightIterator: goto LabelInitRight;
  1291.             // case SetIteratorResult.NeedLeftNode: goto LabelNextLeft;
  1292.             // case SetIteratorResult.NeedRightNode: goto LabelNextRight;
  1293.             // }
  1294.             this.helper.MarkLabel(lblCall);
  1295.             this.helper.Emit(OpCodes.Ldloca, locIter);
  1296.             this.helper.Emit(OpCodes.Ldloc, locNav);
  1297.             this.helper.Call(methNext);
  1298.            
  1299.             // If this iterator always returns a single node, then NoMoreNodes will never be returned
  1300.             // Don't expose Next label if this iterator always returns a single node
  1301.             if (ndSet.XmlType.IsSingleton) {
  1302.                 this.helper.Emit(OpCodes.Switch, new Label[] {lblInitRight, lblNextLeft, lblNextRight});
  1303.                 this.iterCurr.Storage = StorageDescriptor.Current(locIter, typeof(XPathNavigator));
  1304.             }
  1305.             else {
  1306.                 this.helper.Emit(OpCodes.Switch, new Label[] {this.iterCurr.GetLabelNext(), lblInitRight, lblNextLeft, lblNextRight});
  1307.                 this.iterCurr.SetIterator(lblNext, StorageDescriptor.Current(locIter, typeof(XPathNavigator)));
  1308.             }
  1309.            
  1310.             return ndSet;
  1311.         }
  1312.        
  1313.         /// <summary>
  1314.         /// Generate code for QilNodeType.Average.
  1315.         /// </summary>
  1316.         protected override QilNode VisitAverage(QilUnary ndAvg)
  1317.         {
  1318.             XmlILStorageMethods meths = XmlILMethods.StorageMethods[GetItemStorageType(ndAvg)];
  1319.             return CreateAggregator(ndAvg, "$$$aggAvg", meths, meths.AggAvg, meths.AggAvgResult);
  1320.         }
  1321.        
  1322.         /// <summary>
  1323.         /// Generate code for QilNodeType.Sum.
  1324.         /// </summary>
  1325.         protected override QilNode VisitSum(QilUnary ndSum)
  1326.         {
  1327.             XmlILStorageMethods meths = XmlILMethods.StorageMethods[GetItemStorageType(ndSum)];
  1328.             return CreateAggregator(ndSum, "$$$aggSum", meths, meths.AggSum, meths.AggSumResult);
  1329.         }
  1330.        
  1331.         /// <summary>
  1332.         /// Generate code for QilNodeType.Minimum.
  1333.         /// </summary>
  1334.         protected override QilNode VisitMinimum(QilUnary ndMin)
  1335.         {
  1336.             XmlILStorageMethods meths = XmlILMethods.StorageMethods[GetItemStorageType(ndMin)];
  1337.             return CreateAggregator(ndMin, "$$$aggMin", meths, meths.AggMin, meths.AggMinResult);
  1338.         }
  1339.        
  1340.         /// <summary>
  1341.         /// Generate code for QilNodeType.Maximum.
  1342.         /// </summary>
  1343.         protected override QilNode VisitMaximum(QilUnary ndMax)
  1344.         {
  1345.             XmlILStorageMethods meths = XmlILMethods.StorageMethods[GetItemStorageType(ndMax)];
  1346.             return CreateAggregator(ndMax, "$$$aggMax", meths, meths.AggMax, meths.AggMaxResult);
  1347.         }
  1348.        
  1349.         /// <summary>
  1350.         /// Generate code for QilNodeType.Sum, QilNodeType.Average, QilNodeType.Minimum, and QilNodeType.Maximum.
  1351.         /// </summary>
  1352.         private QilNode CreateAggregator(QilUnary ndAgg, string aggName, XmlILStorageMethods methods, MethodInfo methAgg, MethodInfo methResult)
  1353.         {
  1354.             Label lblOnEnd = this.helper.DefineLabel();
  1355.             Type typAgg = methAgg.DeclaringType;
  1356.             LocalBuilder locAgg;
  1357.            
  1358.             // Aggregate agg;
  1359.             // agg.Create();
  1360.             locAgg = this.helper.DeclareLocal(aggName, typAgg);
  1361.             this.helper.Emit(OpCodes.Ldloca, locAgg);
  1362.             this.helper.Call(methods.AggCreate);
  1363.            
  1364.             // foreach (num in expr) {
  1365.             StartNestedIterator(ndAgg.Child, lblOnEnd);
  1366.             this.helper.Emit(OpCodes.Ldloca, locAgg);
  1367.             Visit(ndAgg.Child);
  1368.            
  1369.             // agg.Aggregate(num);
  1370.             this.iterCurr.EnsureStackNoCache();
  1371.             this.iterCurr.EnsureItemStorageType(ndAgg.XmlType, GetItemStorageType(ndAgg));
  1372.             this.helper.Call(methAgg);
  1373.             this.helper.Emit(OpCodes.Ldloca, locAgg);
  1374.            
  1375.             // }
  1376.             this.iterCurr.LoopToEnd(lblOnEnd);
  1377.            
  1378.             // End nested iterator
  1379.             EndNestedIterator(ndAgg.Child);
  1380.            
  1381.             // If aggregate might be empty sequence, then generate code to handle this possibility
  1382.             if (ndAgg.XmlType.MaybeEmpty) {
  1383.                 // if (agg.IsEmpty) goto LabelNextCtxt;
  1384.                 this.helper.Call(methods.AggIsEmpty);
  1385.                 this.helper.Emit(OpCodes.Brtrue, this.iterCurr.GetLabelNext());
  1386.                 this.helper.Emit(OpCodes.Ldloca, locAgg);
  1387.             }
  1388.            
  1389.             // result = agg.Result;
  1390.             this.helper.Call(methResult);
  1391.             this.iterCurr.Storage = StorageDescriptor.Stack(GetItemStorageType(ndAgg), false);
  1392.            
  1393.             return ndAgg;
  1394.         }
  1395.        
  1396.         /// <summary>
  1397.         /// Generate code for QilNodeType.Negate.
  1398.         /// </summary>
  1399.         protected override QilNode VisitNegate(QilUnary ndNeg)
  1400.         {
  1401.             NestedVisitEnsureStack(ndNeg.Child);
  1402.             this.helper.CallArithmeticOp(QilNodeType.Negate, ndNeg.XmlType.TypeCode);
  1403.             this.iterCurr.Storage = StorageDescriptor.Stack(GetItemStorageType(ndNeg), false);
  1404.             return ndNeg;
  1405.         }
  1406.        
  1407.         /// <summary>
  1408.         /// Generate code for QilNodeType.Add.
  1409.         /// </summary>
  1410.         protected override QilNode VisitAdd(QilBinary ndPlus)
  1411.         {
  1412.             return ArithmeticOp(ndPlus);
  1413.         }
  1414.        
  1415.         /// <summary>
  1416.         /// Generate code for QilNodeType.Subtract.
  1417.         /// </summary>
  1418.         protected override QilNode VisitSubtract(QilBinary ndMinus)
  1419.         {
  1420.             return ArithmeticOp(ndMinus);
  1421.         }
  1422.        
  1423.         /// <summary>
  1424.         /// Generate code for QilNodeType.Multiply.
  1425.         /// </summary>
  1426.         protected override QilNode VisitMultiply(QilBinary ndMul)
  1427.         {
  1428.             return ArithmeticOp(ndMul);
  1429.         }
  1430.        
  1431.         /// <summary>
  1432.         /// Generate code for QilNodeType.Divide.
  1433.         /// </summary>
  1434.         protected override QilNode VisitDivide(QilBinary ndDiv)
  1435.         {
  1436.             return ArithmeticOp(ndDiv);
  1437.         }
  1438.        
  1439.         /// <summary>
  1440.         /// Generate code for QilNodeType.Modulo.
  1441.         /// </summary>
  1442.         protected override QilNode VisitModulo(QilBinary ndMod)
  1443.         {
  1444.             return ArithmeticOp(ndMod);
  1445.         }
  1446.        
  1447.         /// <summary>
  1448.         /// Generate code for two-argument arithmetic operations.
  1449.         /// </summary>
  1450.         private QilNode ArithmeticOp(QilBinary ndOp)
  1451.         {
  1452.             NestedVisitEnsureStack(ndOp.Left, ndOp.Right);
  1453.             this.helper.CallArithmeticOp(ndOp.NodeType, ndOp.XmlType.TypeCode);
  1454.             this.iterCurr.Storage = StorageDescriptor.Stack(GetItemStorageType(ndOp), false);
  1455.             return ndOp;
  1456.         }
  1457.        
  1458.         /// <summary>
  1459.         /// Generate code for QilNodeType.StrLength.
  1460.         /// </summary>
  1461.         protected override QilNode VisitStrLength(QilUnary ndLen)
  1462.         {
  1463.             NestedVisitEnsureStack(ndLen.Child);
  1464.             this.helper.Call(XmlILMethods.StrLen);
  1465.             this.iterCurr.Storage = StorageDescriptor.Stack(typeof(int), false);
  1466.             return ndLen;
  1467.         }
  1468.        
  1469.         /// <summary>
  1470.         /// Generate code for QilNodeType.StrConcat.
  1471.         /// </summary>
  1472.         protected override QilNode VisitStrConcat(QilStrConcat ndStrConcat)
  1473.         {
  1474.             LocalBuilder locStringConcat;
  1475.             bool fasterConcat;
  1476.             QilNode delimiter;
  1477.             QilNode listStrings;
  1478.             Debug.Assert(!ndStrConcat.Values.XmlType.IsSingleton, "Optimizer should have folded StrConcat of a singleton value");
  1479.            
  1480.             // Get delimiter (assuming it's not the empty string)
  1481.             delimiter = ndStrConcat.Delimiter;
  1482.             if (delimiter.NodeType == QilNodeType.LiteralString && ((string)(QilLiteral)delimiter).Length == 0) {
  1483.                 delimiter = null;
  1484.             }
  1485.            
  1486.             listStrings = ndStrConcat.Values;
  1487.             if (listStrings.NodeType == QilNodeType.Sequence && listStrings.Count < 5) {
  1488.                 // Faster concat possible only if cardinality can be guaranteed at compile-time and there's no delimiter
  1489.                 fasterConcat = true;
  1490.                 foreach (QilNode ndStr in listStrings) {
  1491.                     if (!ndStr.XmlType.IsSingleton)
  1492.                         fasterConcat = false;
  1493.                 }
  1494.             }
  1495.             else {
  1496.                 // If more than 4 strings, array will need to be built
  1497.                 fasterConcat = false;
  1498.             }
  1499.            
  1500.             if (fasterConcat) {
  1501.                 foreach (QilNode ndStr in listStrings)
  1502.                     NestedVisitEnsureStack(ndStr);
  1503.                
  1504.                 this.helper.CallConcatStrings(listStrings.Count);
  1505.             }
  1506.             else {
  1507.                 // Create StringConcat helper internal class
  1508.                 locStringConcat = this.helper.DeclareLocal("$$$strcat", typeof(StringConcat));
  1509.                 this.helper.Emit(OpCodes.Ldloca, locStringConcat);
  1510.                 this.helper.Call(XmlILMethods.StrCatClear);
  1511.                
  1512.                 // Set delimiter, if it's not empty string
  1513.                 if (delimiter != null) {
  1514.                     this.helper.Emit(OpCodes.Ldloca, locStringConcat);
  1515.                     NestedVisitEnsureStack(delimiter);
  1516.                     this.helper.Call(XmlILMethods.StrCatDelim);
  1517.                 }
  1518.                
  1519.                 this.helper.Emit(OpCodes.Ldloca, locStringConcat);
  1520.                
  1521.                 if (listStrings.NodeType == QilNodeType.Sequence) {
  1522.                     foreach (QilNode ndStr in listStrings)
  1523.                         GenerateConcat(ndStr, locStringConcat);
  1524.                 }
  1525.                 else {
  1526.                     GenerateConcat(listStrings, locStringConcat);
  1527.                 }
  1528.                
  1529.                 // Push resulting string onto stack
  1530.                 this.helper.Call(XmlILMethods.StrCatResult);
  1531.             }
  1532.            
  1533.             this.iterCurr.Storage = StorageDescriptor.Stack(typeof(string), false);
  1534.            
  1535.             return ndStrConcat;
  1536.         }
  1537.        
  1538.         /// <summary>
  1539.         /// Generate code to concatenate string values returned by expression "ndStr" using the StringConcat helper class.
  1540.         /// </summary>
  1541.         private void GenerateConcat(QilNode ndStr, LocalBuilder locStringConcat)
  1542.         {
  1543.             Label lblOnEnd;
  1544.            
  1545.             // str = each string;
  1546.             lblOnEnd = this.helper.DefineLabel();
  1547.             StartNestedIterator(ndStr, lblOnEnd);
  1548.             Visit(ndStr);
  1549.            
  1550.             // strcat.Concat(str);
  1551.             this.iterCurr.EnsureStackNoCache();
  1552.             this.iterCurr.EnsureItemStorageType(ndStr.XmlType, typeof(string));
  1553.             this.helper.Call(XmlILMethods.StrCatCat);
  1554.             this.helper.Emit(OpCodes.Ldloca, locStringConcat);
  1555.            
  1556.             // Get next string
  1557.             // goto LabelNext;
  1558.             // LabelOnEnd:
  1559.             this.iterCurr.LoopToEnd(lblOnEnd);
  1560.            
  1561.             // End nested iterator
  1562.             EndNestedIterator(ndStr);
  1563.         }
  1564.        
  1565.         /// <summary>
  1566.         /// Generate code for QilNodeType.StrParseQName.
  1567.         /// </summary>
  1568.         protected override QilNode VisitStrParseQName(QilBinary ndParsedTagName)
  1569.         {
  1570.             VisitStrParseQName(ndParsedTagName, false);
  1571.             return ndParsedTagName;
  1572.         }
  1573.        
  1574.         /// <summary>
  1575.         /// Generate code for QilNodeType.StrParseQName.
  1576.         /// </summary>
  1577.         private void VisitStrParseQName(QilBinary ndParsedTagName, bool preservePrefix)
  1578.         {
  1579.             // If QName prefix should be preserved, then don't create an XmlQualifiedName, which discards the prefix
  1580.             if (!preservePrefix)
  1581.                 this.helper.LoadQueryRuntime();
  1582.            
  1583.             // Push (possibly computed) tag name onto the stack
  1584.             NestedVisitEnsureStack(ndParsedTagName.Left);
  1585.            
  1586.             // If type of second parameter is string,
  1587.             if (ndParsedTagName.Right.XmlType.TypeCode == XmlTypeCode.String) {
  1588.                 // Then push (possibly computed) namespace onto the stack
  1589.                 Debug.Assert(ndParsedTagName.Right.XmlType.IsSingleton);
  1590.                 NestedVisitEnsureStack(ndParsedTagName.Right);
  1591.                
  1592.                 if (!preservePrefix)
  1593.                     this.helper.CallParseTagName(GenerateNameType.TagNameAndNamespace);
  1594.             }
  1595.             else {
  1596.                 // Else push index of set of prefix mappings to use in resolving the prefix
  1597.                 if (ndParsedTagName.Right.NodeType == QilNodeType.Sequence)
  1598.                     this.helper.LoadInteger(this.helper.StaticData.DeclarePrefixMappings(ndParsedTagName.Right));
  1599.                 else
  1600.                     this.helper.LoadInteger(this.helper.StaticData.DeclarePrefixMappings(new QilNode[] {ndParsedTagName.Right}));
  1601.                
  1602.                 // If QName prefix should be preserved, then don't create an XmlQualifiedName, which discards the prefix
  1603.                 if (!preservePrefix)
  1604.                     this.helper.CallParseTagName(GenerateNameType.TagNameAndMappings);
  1605.             }
  1606.            
  1607.             this.iterCurr.Storage = StorageDescriptor.Stack(typeof(XmlQualifiedName), false);
  1608.         }
  1609.        
  1610.         /// <summary>
  1611.         /// Generate code for QilNodeType.Ne.
  1612.         /// </summary>
  1613.         protected override QilNode VisitNe(QilBinary ndNe)
  1614.         {
  1615.             Compare(ndNe);
  1616.             return ndNe;
  1617.         }
  1618.        
  1619.         /// <summary>
  1620.         /// Generate code for QilNodeType.Eq.
  1621.         /// </summary>
  1622.         protected override QilNode VisitEq(QilBinary ndEq)
  1623.         {
  1624.             Compare(ndEq);
  1625.             return ndEq;
  1626.         }
  1627.        
  1628.         /// <summary>
  1629.         /// Generate code for QilNodeType.Gt.
  1630.         /// </summary>
  1631.         protected override QilNode VisitGt(QilBinary ndGt)
  1632.         {
  1633.             Compare(ndGt);
  1634.             return ndGt;
  1635.         }
  1636.        
  1637.         /// <summary>
  1638.         /// Generate code for QilNodeType.Ne.
  1639.         /// </summary>
  1640.         protected override QilNode VisitGe(QilBinary ndGe)
  1641.         {
  1642.             Compare(ndGe);
  1643.             return ndGe;
  1644.         }
  1645.        
  1646.         /// <summary>
  1647.         /// Generate code for QilNodeType.Lt.
  1648.         /// </summary>
  1649.         protected override QilNode VisitLt(QilBinary ndLt)
  1650.         {
  1651.             Compare(ndLt);
  1652.             return ndLt;
  1653.         }
  1654.        
  1655.         /// <summary>
  1656.         /// Generate code for QilNodeType.Le.
  1657.         /// </summary>
  1658.         protected override QilNode VisitLe(QilBinary ndLe)
  1659.         {
  1660.             Compare(ndLe);
  1661.             return ndLe;
  1662.         }
  1663.        
  1664.         /// <summary>
  1665.         /// Generate code for comparison operations.
  1666.         /// </summary>
  1667.         private void Compare(QilBinary ndComp)
  1668.         {
  1669.             QilNodeType relOp = ndComp.NodeType;
  1670.             XmlTypeCode code;
  1671.             Debug.Assert(ndComp.Left.XmlType.IsAtomicValue && ndComp.Right.XmlType.IsAtomicValue, "Operands to compare must be atomic values.");
  1672.             Debug.Assert(ndComp.Left.XmlType.IsSingleton && ndComp.Right.XmlType.IsSingleton, "Operands to compare must be cardinality one.");
  1673.             Debug.Assert(ndComp.Left.XmlType == ndComp.Right.XmlType, "Operands to compare may not be heterogenous.");
  1674.            
  1675.             if (relOp == QilNodeType.Eq || relOp == QilNodeType.Ne) {
  1676.                 // Generate better code for certain special cases
  1677.                 if (TryZeroCompare(relOp, ndComp.Left, ndComp.Right))
  1678.                     return;
  1679.                
  1680.                 if (TryZeroCompare(relOp, ndComp.Right, ndComp.Left))
  1681.                     return;
  1682.                
  1683.                 if (TryNameCompare(relOp, ndComp.Left, ndComp.Right))
  1684.                     return;
  1685.                
  1686.                 if (TryNameCompare(relOp, ndComp.Right, ndComp.Left))
  1687.                     return;
  1688.             }
  1689.            
  1690.             // Push two operands onto the stack
  1691.             NestedVisitEnsureStack(ndComp.Left, ndComp.Right);
  1692.            
  1693.             // Perform comparison
  1694.             code = ndComp.Left.XmlType.TypeCode;
  1695.             switch (code) {
  1696.                 case XmlTypeCode.String:
  1697.                 case XmlTypeCode.Decimal:
  1698.                 case XmlTypeCode.QName:
  1699.                     if (relOp == QilNodeType.Eq || relOp == QilNodeType.Ne) {
  1700.                         this.helper.CallCompareEquals(code);
  1701.                        
  1702.                         // If relOp is Eq, then branch to true label or push "true" if Equals function returns true (non-zero)
  1703.                         // If relOp is Ne, then branch to true label or push "true" if Equals function returns false (zero)
  1704.                         ZeroCompare((relOp == QilNodeType.Eq) ? QilNodeType.Ne : QilNodeType.Eq, true);
  1705.                     }
  1706.                     else {
  1707.                         Debug.Assert(code != XmlTypeCode.QName, "QName values do not support the " + relOp + " operation");
  1708.                        
  1709.                         // Push -1, 0, or 1 onto the stack depending upon the result of the comparison
  1710.                         this.helper.CallCompare(code);
  1711.                        
  1712.                         // Compare result to 0 (e.g. Ge is >= 0)
  1713.                         this.helper.Emit(OpCodes.Ldc_I4_0);
  1714.                         ClrCompare(relOp, code);
  1715.                     }
  1716.                     break;
  1717.                 case XmlTypeCode.Integer:
  1718.                 case XmlTypeCode.Int:
  1719.                 case XmlTypeCode.Boolean:
  1720.                 case XmlTypeCode.Double:
  1721.                    
  1722.                     ClrCompare(relOp, code);
  1723.                     break;
  1724.                 default:
  1725.                    
  1726.                     Debug.Fail("Comparisons for datatype " + code + " are invalid.");
  1727.                     break;
  1728.             }
  1729.         }
  1730.        
  1731.         /// <summary>
  1732.         /// Generate code for QilNodeType.VisitIs.
  1733.         /// </summary>
  1734.         protected override QilNode VisitIs(QilBinary ndIs)
  1735.         {
  1736.             // Generate code to push arguments onto stack
  1737.             NestedVisitEnsureStack(ndIs.Left, ndIs.Right);
  1738.             this.helper.Call(XmlILMethods.NavSamePos);
  1739.            
  1740.             // navThis.IsSamePosition(navThat);
  1741.             ZeroCompare(QilNodeType.Ne, true);
  1742.             return ndIs;
  1743.         }
  1744.        
  1745.         /// <summary>
  1746.         /// Generate code for QilNodeType.VisitBefore.
  1747.         /// </summary>
  1748.         protected override QilNode VisitBefore(QilBinary ndBefore)
  1749.         {
  1750.             ComparePosition(ndBefore);
  1751.             return ndBefore;
  1752.         }
  1753.        
  1754.         /// <summary>
  1755.         /// Generate code for QilNodeType.VisitAfter.
  1756.         /// </summary>
  1757.         protected override QilNode VisitAfter(QilBinary ndAfter)
  1758.         {
  1759.             ComparePosition(ndAfter);
  1760.             return ndAfter;
  1761.         }
  1762.        
  1763.         /// <summary>
  1764.         /// Generate code for QilNodeType.VisitBefore and QilNodeType.VisitAfter.
  1765.         /// </summary>
  1766.         private void ComparePosition(QilBinary ndComp)
  1767.         {
  1768.             // Generate code to push arguments onto stack
  1769.             this.helper.LoadQueryRuntime();
  1770.             NestedVisitEnsureStack(ndComp.Left, ndComp.Right);
  1771.             this.helper.Call(XmlILMethods.CompPos);
  1772.            
  1773.             // XmlQueryRuntime.ComparePosition(navThis, navThat) < 0;
  1774.             this.helper.LoadInteger(0);
  1775.             ClrCompare(ndComp.NodeType == QilNodeType.Before ? QilNodeType.Lt : QilNodeType.Gt, XmlTypeCode.String);
  1776.         }
  1777.        
  1778.         /// <summary>
  1779.         /// Generate code for a QilNodeType.For.
  1780.         /// </summary>
  1781.         protected override QilNode VisitFor(QilIterator ndFor)
  1782.         {
  1783.             IteratorDescriptor iterInfo;
  1784.            
  1785.             // Reference saved location
  1786.             iterInfo = XmlILAnnotation.Write(ndFor).CachedIteratorDescriptor;
  1787.             this.iterCurr.Storage = iterInfo.Storage;
  1788.            
  1789.             // If the iterator is a reference to a global variable or parameter,
  1790.             if (this.iterCurr.Storage.Location == ItemLocation.Global) {
  1791.                 // Then compute global value and push it onto the stack
  1792.                 this.iterCurr.EnsureStack();
  1793.             }
  1794.            
  1795.             return ndFor;
  1796.         }
  1797.        
  1798.         /// <summary>
  1799.         /// Generate code for a QilNodeType.Let.
  1800.         /// </summary>
  1801.         protected override QilNode VisitLet(QilIterator ndLet)
  1802.         {
  1803.             // Same as For
  1804.             return VisitFor(ndLet);
  1805.         }
  1806.        
  1807.         /// <summary>
  1808.         /// Generate code for a QilNodeType.Parameter.
  1809.         /// </summary>
  1810.         protected override QilNode VisitParameter(QilParameter ndParameter)
  1811.         {
  1812.             // Same as For
  1813.             return VisitFor(ndParameter);
  1814.         }
  1815.        
  1816.         /// <summary>
  1817.         /// Generate code for a QilNodeType.Loop.
  1818.         /// </summary>
  1819.         protected override QilNode VisitLoop(QilLoop ndLoop)
  1820.         {
  1821.             bool hasOnEnd;
  1822.             Label lblOnEnd;
  1823.            
  1824.             StartWriterLoop(ndLoop, out hasOnEnd, out lblOnEnd);
  1825.            
  1826.             StartBinding(ndLoop.Variable);
  1827.            
  1828.             // Unnest loop body as part of the current iterator
  1829.             Visit(ndLoop.Body);
  1830.            
  1831.             EndBinding(ndLoop.Variable);
  1832.            
  1833.             EndWriterLoop(ndLoop, hasOnEnd, lblOnEnd);
  1834.            
  1835.             return ndLoop;
  1836.         }
  1837.        
  1838.         /// <summary>
  1839.         /// Generate code for a QilNodeType.Filter.
  1840.         /// </summary>
  1841.         protected override QilNode VisitFilter(QilLoop ndFilter)
  1842.         {
  1843.             // Handle any special-case patterns that are rooted at Filter
  1844.             if (HandleFilterPatterns(ndFilter))
  1845.                 return ndFilter;
  1846.            
  1847.             StartBinding(ndFilter.Variable);
  1848.            
  1849.             // Result of filter is the sequence bound to the iterator
  1850.             this.iterCurr.SetIterator(this.iterNested);
  1851.            
  1852.             // If filter is false, skip the current item
  1853.             StartNestedIterator(ndFilter.Body);
  1854.             this.iterCurr.SetBranching(BranchingContext.OnFalse, this.iterCurr.ParentIterator.GetLabelNext());
  1855.             Visit(ndFilter.Body);
  1856.             EndNestedIterator(ndFilter.Body);
  1857.            
  1858.             EndBinding(ndFilter.Variable);
  1859.            
  1860.             return ndFilter;
  1861.         }
  1862.        
  1863.         /// <summary>
  1864.         /// There are a number of path patterns that can be rooted at Filter nodes. Determine whether one of these patterns
  1865.         /// has been previously matched on "ndFilter". If so, generate code for the pattern and return true. Otherwise, just
  1866.         /// return false.
  1867.         /// </summary>
  1868.         private bool HandleFilterPatterns(QilLoop ndFilter)
  1869.         {
  1870.             OptimizerPatterns patt = OptimizerPatterns.Read(ndFilter);
  1871.             LocalBuilder locIter;
  1872.             XmlNodeKindFlags kinds;
  1873.             QilName name;
  1874.             QilNode input;
  1875.             QilNode step;
  1876.             bool isFilterElements;
  1877.            
  1878.             // Handle FilterElements and FilterContentKind patterns
  1879.             isFilterElements = patt.MatchesPattern(OptimizerPatternName.FilterElements);
  1880.             if (isFilterElements || patt.MatchesPattern(OptimizerPatternName.FilterContentKind)) {
  1881.                 if (isFilterElements) {
  1882.                     // FilterElements pattern, so Kind = Element and Name = Argument
  1883.                     kinds = XmlNodeKindFlags.Element;
  1884.                     name = (QilName)patt.GetArgument(OptimizerPatternArgument.ElementQName);
  1885.                 }
  1886.                 else {
  1887.                     // FilterKindTest pattern, so Kind = Argument and Name = null
  1888.                     kinds = ((XmlQueryType)patt.GetArgument(OptimizerPatternArgument.KindTestType)).NodeKinds;
  1889.                     name = null;
  1890.                 }
  1891.                
  1892.                 step = (QilNode)patt.GetArgument(OptimizerPatternArgument.StepNode);
  1893.                 input = (QilNode)patt.GetArgument(OptimizerPatternArgument.StepInput);
  1894.                 switch (step.NodeType) {
  1895.                     case QilNodeType.Content:
  1896.                         if (isFilterElements) {
  1897.                             // Iterator iter;
  1898.                             locIter = this.helper.DeclareLocal("$$$iterElemContent", typeof(ElementContentIterator));
  1899.                            
  1900.                             // iter.Create(navCtxt, locName, ns);
  1901.                             this.helper.Emit(OpCodes.Ldloca, locIter);
  1902.                             NestedVisitEnsureStack(input);
  1903.                             this.helper.CallGetAtomizedName(this.helper.StaticData.DeclareName(name.LocalName));
  1904.                             this.helper.CallGetAtomizedName(this.helper.StaticData.DeclareName(name.NamespaceUri));
  1905.                             this.helper.Call(XmlILMethods.ElemContentCreate);
  1906.                            
  1907.                             GenerateSimpleIterator(typeof(XPathNavigator), locIter, XmlILMethods.ElemContentNext);
  1908.                         }
  1909.                         else {
  1910.                             if (kinds == XmlNodeKindFlags.Content) {
  1911.                                 CreateSimpleIterator(input, "$$$iterContent", typeof(ContentIterator), XmlILMethods.ContentCreate, XmlILMethods.ContentNext);
  1912.                             }
  1913.                             else {
  1914.                                 // Iterator iter;
  1915.                                 locIter = this.helper.DeclareLocal("$$$iterContent", typeof(NodeKindContentIterator));
  1916.                                
  1917.                                 // iter.Create(navCtxt, nodeType);
  1918.                                 this.helper.Emit(OpCodes.Ldloca, locIter);
  1919.                                 NestedVisitEnsureStack(input);
  1920.                                 this.helper.LoadInteger((int)QilXmlToXPathNodeType(kinds));
  1921.                                 this.helper.Call(XmlILMethods.KindContentCreate);
  1922.                                
  1923.                                 GenerateSimpleIterator(typeof(XPathNavigator), locIter, XmlILMethods.KindContentNext);
  1924.                             }
  1925.                         }
  1926.                         return true;
  1927.                     case QilNodeType.Parent:
  1928.                        
  1929.                         CreateFilteredIterator(input, "$$$iterPar", typeof(ParentIterator), XmlILMethods.ParentCreate, XmlILMethods.ParentNext, kinds, name, TriState.Unknown, null);
  1930.                         return true;
  1931.                     case QilNodeType.Ancestor:
  1932.                     case QilNodeType.AncestorOrSelf:
  1933.                        
  1934.                         CreateFilteredIterator(input, "$$$iterAnc", typeof(AncestorIterator), XmlILMethods.AncCreate, XmlILMethods.AncNext, kinds, name, (step.NodeType == QilNodeType.Ancestor) ? TriState.False : TriState.True, null);
  1935.                         return true;
  1936.                     case QilNodeType.Descendant:
  1937.                     case QilNodeType.DescendantOrSelf:
  1938.                        
  1939.                         CreateFilteredIterator(input, "$$$iterDesc", typeof(DescendantIterator), XmlILMethods.DescCreate, XmlILMethods.DescNext, kinds, name, (step.NodeType == QilNodeType.Descendant) ? TriState.False : TriState.True, null);
  1940.                         return true;
  1941.                     case QilNodeType.Preceding:
  1942.                        
  1943.                         CreateFilteredIterator(input, "$$$iterPrec", typeof(PrecedingIterator), XmlILMethods.PrecCreate, XmlILMethods.PrecNext, kinds, name, TriState.Unknown, null);
  1944.                         return true;
  1945.                     case QilNodeType.FollowingSibling:
  1946.                        
  1947.                         CreateFilteredIterator(input, "$$$iterFollSib", typeof(FollowingSiblingIterator), XmlILMethods.FollSibCreate, XmlILMethods.FollSibNext, kinds, name, TriState.Unknown, null);
  1948.                         return true;
  1949.                     case QilNodeType.PrecedingSibling:
  1950.                        
  1951.                         CreateFilteredIterator(input, "$$$iterPreSib", typeof(PrecedingSiblingIterator), XmlILMethods.PreSibCreate, XmlILMethods.PreSibNext, kinds, name, TriState.Unknown, null);
  1952.                         return true;
  1953.                     case QilNodeType.NodeRange:
  1954.                        
  1955.                         CreateFilteredIterator(input, "$$$iterRange", typeof(NodeRangeIterator), XmlILMethods.NodeRangeCreate, XmlILMethods.NodeRangeNext, kinds, name, TriState.Unknown, ((QilBinary)step).Right);
  1956.                         return true;
  1957.                     case QilNodeType.XPathFollowing:
  1958.                        
  1959.                         CreateFilteredIterator(input, "$$$iterFoll", typeof(XPathFollowingIterator), XmlILMethods.XPFollCreate, XmlILMethods.XPFollNext, kinds, name, TriState.Unknown, null);
  1960.                         return true;
  1961.                     case QilNodeType.XPathPreceding:
  1962.                        
  1963.                         CreateFilteredIterator(input, "$$$iterPrec", typeof(XPathPrecedingIterator), XmlILMethods.XPPrecCreate, XmlILMethods.XPPrecNext, kinds, name, TriState.Unknown, null);
  1964.                         return true;
  1965.                     default:
  1966.                        
  1967.                         Debug.Assert(false, "Pattern " + step.NodeType + " should have been handled.");
  1968.                         break;
  1969.                 }
  1970.             }
  1971.             else if (patt.MatchesPattern(OptimizerPatternName.FilterAttributeKind)) {
  1972.                 // Handle FilterAttributeKind pattern
  1973.                 input = (QilNode)patt.GetArgument(OptimizerPatternArgument.StepInput);
  1974.                 CreateSimpleIterator(input, "$$$iterAttr", typeof(AttributeIterator), XmlILMethods.AttrCreate, XmlILMethods.AttrNext);
  1975.                 return true;
  1976.             }
  1977.             else if (patt.MatchesPattern(OptimizerPatternName.EqualityIndex)) {
  1978.                 // Handle EqualityIndex pattern
  1979.                 Label lblOnEnd = this.helper.DefineLabel();
  1980.                 Label lblLookup = this.helper.DefineLabel();
  1981.                 QilIterator nodes = (QilIterator)patt.GetArgument(OptimizerPatternArgument.IndexedNodes);
  1982.                 QilNode keys = (QilNode)patt.GetArgument(OptimizerPatternArgument.KeyExpression);
  1983.                
  1984.                 // XmlILIndex index;
  1985.                 // if (runtime.FindIndex(navCtxt, indexId, out index)) goto LabelLookup;
  1986.                 LocalBuilder locIndex = this.helper.DeclareLocal("$$$index", typeof(XmlILIndex));
  1987.                 this.helper.LoadQueryRuntime();
  1988.                 this.helper.Emit(OpCodes.Ldarg_1);
  1989.                 this.helper.LoadInteger(this.indexId);
  1990.                 this.helper.Emit(OpCodes.Ldloca, locIndex);
  1991.                 this.helper.Call(XmlILMethods.FindIndex);
  1992.                 this.helper.Emit(OpCodes.Brtrue, lblLookup);
  1993.                
  1994.                 // runtime.AddNewIndex(navCtxt, indexId, [build index]);
  1995.                 this.helper.LoadQueryRuntime();
  1996.                 this.helper.Emit(OpCodes.Ldarg_1);
  1997.                 this.helper.LoadInteger(this.indexId);
  1998.                 this.helper.Emit(OpCodes.Ldloc, locIndex);
  1999.                
  2000.                 // Generate code to iterate over the the nodes which are being indexed ($iterNodes in the pattern)
  2001.                 StartNestedIterator(nodes, lblOnEnd);
  2002.                 StartBinding(nodes);
  2003.                
  2004.                 // Generate code to iterate over the keys for each node ($bindingKeys in the pattern)
  2005.                 Visit(keys);
  2006.                
  2007.                 // index.Add(key, value);
  2008.                 this.iterCurr.EnsureStackNoCache();
  2009.                 VisitFor(nodes);
  2010.                 this.iterCurr.EnsureStackNoCache();
  2011.                 this.iterCurr.EnsureItemStorageType(nodes.XmlType, typeof(XPathNavigator));
  2012.                 this.helper.Call(XmlILMethods.IndexAdd);
  2013.                 this.helper.Emit(OpCodes.Ldloc, locIndex);
  2014.                
  2015.                 // LabelOnEnd:
  2016.                 this.iterCurr.LoopToEnd(lblOnEnd);
  2017.                 EndBinding(nodes);
  2018.                 EndNestedIterator(nodes);
  2019.                
  2020.                 // runtime.AddNewIndex(navCtxt, indexId, [build index]);
  2021.                 this.helper.Call(XmlILMethods.AddNewIndex);
  2022.                
  2023.                 // LabelLookup:
  2024.                 // results = index.Lookup(keyValue);
  2025.                 this.helper.MarkLabel(lblLookup);
  2026.                 this.helper.Emit(OpCodes.Ldloc, locIndex);
  2027.                 this.helper.Emit(OpCodes.Ldarg_2);
  2028.                 this.helper.Call(XmlILMethods.IndexLookup);
  2029.                 this.iterCurr.Storage = StorageDescriptor.Stack(typeof(XPathNavigator), true);
  2030.                
  2031.                 this.indexId++;
  2032.                
  2033.                 return true;
  2034.             }
  2035.            
  2036.             return false;
  2037.         }
  2038.        
  2039.         /// <summary>
  2040.         /// Generate code for a Let, For, or Parameter iterator. Bind iterated value to a variable.
  2041.         /// </summary>
  2042.         private void StartBinding(QilIterator ndIter)
  2043.         {
  2044.             OptimizerPatterns patt = OptimizerPatterns.Read(ndIter);
  2045.             Debug.Assert(ndIter != null);
  2046.            
  2047.             // DebugInfo: Sequence point just before generating code for the bound expression
  2048.             if (this.qil.IsDebug && ndIter.SourceLine != null)
  2049.                 this.helper.DebugSequencePoint(ndIter.SourceLine);
  2050.            
  2051.             // Treat cardinality one Let iterators as if they were For iterators (no nesting necessary)
  2052.             if (ndIter.NodeType == QilNodeType.For || ndIter.XmlType.IsSingleton) {
  2053.                 StartForBinding(ndIter, patt);
  2054.             }
  2055.             else {
  2056.                 Debug.Assert(ndIter.NodeType == QilNodeType.Let || ndIter.NodeType == QilNodeType.Parameter);
  2057.                 Debug.Assert(!patt.MatchesPattern(OptimizerPatternName.IsPositional));
  2058.                
  2059.                 // Bind Let values (nested iterator) to variable
  2060.                 StartLetBinding(ndIter);
  2061.             }
  2062.            
  2063.             // Attach IteratorDescriptor to the iterator
  2064.             XmlILAnnotation.Write(ndIter).CachedIteratorDescriptor = this.iterNested;
  2065.         }
  2066.        
  2067.         /// <summary>
  2068.         /// Bind values produced by the "ndFor" expression to a non-stack location that can later
  2069.         /// be referenced.
  2070.         /// </summary>
  2071.         private void StartForBinding(QilIterator ndFor, OptimizerPatterns patt)
  2072.         {
  2073.             LocalBuilder locPos = null;
  2074.             Debug.Assert(ndFor.XmlType.IsSingleton);
  2075.            
  2076.             // For expression iterator will be unnested as part of parent iterator
  2077.             if (this.iterCurr.HasLabelNext)
  2078.                 StartNestedIterator(ndFor.Binding, this.iterCurr.GetLabelNext());
  2079.             else
  2080.                 StartNestedIterator(ndFor.Binding);
  2081.            
  2082.             if (patt.MatchesPattern(OptimizerPatternName.IsPositional)) {
  2083.                 // Need to track loop index so initialize it to 0 before starting loop
  2084.                 locPos = this.helper.DeclareLocal("$$$pos", typeof(int));
  2085.                 this.helper.Emit(OpCodes.Ldc_I4_0);
  2086.                 this.helper.Emit(OpCodes.Stloc, locPos);
  2087.             }
  2088.            
  2089.             // Allow base internal class to dispatch based on QilExpression node type
  2090.             Visit(ndFor.Binding);
  2091.            
  2092.             // DebugInfo: Open variable scope
  2093.             // DebugInfo: Ensure that for variable is stored in a local and tag it with the user-defined name
  2094.             if (this.qil.IsDebug && ndFor.DebugName != null) {
  2095.                 this.helper.DebugStartScope();
  2096.                
  2097.                 // Ensure that values are stored in a local variable with a user-defined name
  2098.                 this.iterCurr.EnsureLocalNoCache("$$$for");
  2099.                 this.iterCurr.Storage.LocalLocation.SetLocalSymInfo(ndFor.DebugName);
  2100.             }
  2101.             else {
  2102.                 // Ensure that values are not stored on the stack
  2103.                 this.iterCurr.EnsureNoStackNoCache("$$$for");
  2104.             }
  2105.            
  2106.             if (patt.MatchesPattern(OptimizerPatternName.IsPositional)) {
  2107.                 // Increment position
  2108.                 this.helper.Emit(OpCodes.Ldloc, locPos);
  2109.                 this.helper.Emit(OpCodes.Ldc_I4_1);
  2110.                 this.helper.Emit(OpCodes.Add);
  2111.                 this.helper.Emit(OpCodes.Stloc, locPos);
  2112.                
  2113.                 if (patt.MatchesPattern(OptimizerPatternName.MaxPosition)) {
  2114.                     // Short-circuit rest of loop if max position has already been reached
  2115.                     this.helper.Emit(OpCodes.Ldloc, locPos);
  2116.                     this.helper.LoadInteger((int)patt.GetArgument(OptimizerPatternArgument.MaxPosition));
  2117.                     this.helper.Emit(OpCodes.Bgt, this.iterCurr.ParentIterator.GetLabelNext());
  2118.                 }
  2119.                
  2120.                 this.iterCurr.LocalPosition = locPos;
  2121.             }
  2122.            
  2123.             EndNestedIterator(ndFor.Binding);
  2124.             this.iterCurr.SetIterator(this.iterNested);
  2125.         }
  2126.        
  2127.         /// <summary>
  2128.         /// Bind values in the "ndLet" expression to a non-stack location that can later be referenced.
  2129.         /// </summary>
  2130.         public void StartLetBinding(QilIterator ndLet)
  2131.         {
  2132.             Debug.Assert(!ndLet.XmlType.IsSingleton);
  2133.            
  2134.             // Construct nested iterator
  2135.             StartNestedIterator(ndLet);
  2136.            
  2137.             // Allow base internal class to dispatch based on QilExpression node type
  2138.             NestedVisit(ndLet.Binding, GetItemStorageType(ndLet), !ndLet.XmlType.IsSingleton);
  2139.            
  2140.             // DebugInfo: Open variable scope
  2141.             // DebugInfo: Ensure that for variable is stored in a local and tag it with the user-defined name
  2142.             if (this.qil.IsDebug && ndLet.DebugName != null) {
  2143.                 this.helper.DebugStartScope();
  2144.                
  2145.                 // Ensure that cache is stored in a local variable with a user-defined name
  2146.                 this.iterCurr.EnsureLocal("$$$cache");
  2147.                 this.iterCurr.Storage.LocalLocation.SetLocalSymInfo(ndLet.DebugName);
  2148.             }
  2149.             else {
  2150.                 // Ensure that cache is not stored on the stack
  2151.                 this.iterCurr.EnsureNoStack("$$$cache");
  2152.             }
  2153.            
  2154.             EndNestedIterator(ndLet);
  2155.         }
  2156.        
  2157.         /// <summary>
  2158.         /// Mark iterator variables as out-of-scope.
  2159.         /// </summary>
  2160.         private void EndBinding(QilIterator ndIter)
  2161.         {
  2162.             Debug.Assert(ndIter != null);
  2163.            
  2164.             // Variables go out of scope here
  2165.             if (this.qil.IsDebug && ndIter.DebugName != null)
  2166.                 this.helper.DebugEndScope();
  2167.         }
  2168.        
  2169.         /// <summary>
  2170.         /// Generate code for QilNodeType.PositionOf.
  2171.         /// </summary>
  2172.         protected override QilNode VisitPositionOf(QilUnary ndPos)
  2173.         {
  2174.             QilIterator ndIter = ndPos.Child as QilIterator;
  2175.             LocalBuilder locPos;
  2176.             Debug.Assert(ndIter.NodeType == QilNodeType.For);
  2177.            
  2178.             locPos = XmlILAnnotation.Write(ndIter).CachedIteratorDescriptor.LocalPosition;
  2179.             Debug.Assert(locPos != null);
  2180.             this.iterCurr.Storage = StorageDescriptor.Local(locPos, typeof(int), false);
  2181.            
  2182.             return ndPos;
  2183.         }
  2184.        
  2185.         /// <summary>
  2186.         /// Generate code for QilNodeType.Sort.
  2187.         /// </summary>
  2188.         protected override QilNode VisitSort(QilLoop ndSort)
  2189.         {
  2190.             Type itemStorageType = GetItemStorageType(ndSort);
  2191.             LocalBuilder locCache;
  2192.             LocalBuilder locKeys;
  2193.             Label lblOnEndSort = this.helper.DefineLabel();
  2194.             Debug.Assert(ndSort.Variable.NodeType == QilNodeType.For);
  2195.            
  2196.             // XmlQuerySequence<T> cache;
  2197.             // cache = XmlQuerySequence.CreateOrReuse(cache);
  2198.             XmlILStorageMethods methods = XmlILMethods.StorageMethods[itemStorageType];
  2199.             locCache = this.helper.DeclareLocal("$$$cache", methods.SeqType);
  2200.             this.helper.Emit(OpCodes.Ldloc, locCache);
  2201.             this.helper.CallToken(methods.SeqReuse);
  2202.             this.helper.Emit(OpCodes.Stloc, locCache);
  2203.             this.helper.Emit(OpCodes.Ldloc, locCache);
  2204.            
  2205.             // XmlSortKeyAccumulator keys;
  2206.             // keys.Create(runtime);
  2207.             locKeys = this.helper.DeclareLocal("$$$keys", typeof(XmlSortKeyAccumulator));
  2208.             this.helper.Emit(OpCodes.Ldloca, locKeys);
  2209.             this.helper.Call(XmlILMethods.SortKeyCreate);
  2210.            
  2211.             // Construct nested iterator
  2212.             // foreach (item in sort-expr) {
  2213.             StartNestedIterator(ndSort.Variable, lblOnEndSort);
  2214.             StartBinding(ndSort.Variable);
  2215.             Debug.Assert(!this.iterNested.Storage.IsCached);
  2216.            
  2217.             // cache.Add(item);
  2218.             this.iterCurr.EnsureStackNoCache();
  2219.             this.iterCurr.EnsureItemStorageType(ndSort.Variable.XmlType, GetItemStorageType(ndSort.Variable));
  2220.             this.helper.Call(methods.SeqAdd);
  2221.            
  2222.             this.helper.Emit(OpCodes.Ldloca, locKeys);
  2223.            
  2224.             // Add keys to accumulator (there may be several keys)
  2225.             foreach (QilSortKey ndKey in ndSort.Body)
  2226.                 VisitSortKey(ndKey, locKeys);
  2227.            
  2228.             // keys.FinishSortKeys();
  2229.             this.helper.Call(XmlILMethods.SortKeyFinish);
  2230.            
  2231.             // }
  2232.             this.helper.Emit(OpCodes.Ldloc, locCache);
  2233.             this.iterCurr.LoopToEnd(lblOnEndSort);
  2234.            
  2235.             // Remove cache reference from stack
  2236.             this.helper.Emit(OpCodes.Pop);
  2237.            
  2238.             // cache.SortByKeys(keys.Keys);
  2239.             this.helper.Emit(OpCodes.Ldloc, locCache);
  2240.             this.helper.Emit(OpCodes.Ldloca, locKeys);
  2241.             this.helper.Call(XmlILMethods.SortKeyKeys);
  2242.             this.helper.Call(methods.SeqSortByKeys);
  2243.            
  2244.             // End nested iterator
  2245.             this.iterCurr.Storage = StorageDescriptor.Local(locCache, itemStorageType, true);
  2246.             EndBinding(ndSort.Variable);
  2247.             EndNestedIterator(ndSort.Variable);
  2248.             this.iterCurr.SetIterator(this.iterNested);
  2249.            
  2250.             return ndSort;
  2251.         }
  2252.        
  2253.         /// <summary>
  2254.         /// Generate code to add a (value, collation) sort key to the XmlSortKeyAccumulator.
  2255.         /// </summary>
  2256.         private void VisitSortKey(QilSortKey ndKey, LocalBuilder locKeys)
  2257.         {
  2258.             Label lblOnEndKey;
  2259.             Debug.Assert(ndKey.Key.XmlType.IsAtomicValue, "Sort key must be an atomic value.");
  2260.            
  2261.             // Push collation onto the stack
  2262.             this.helper.Emit(OpCodes.Ldloca, locKeys);
  2263.             if (ndKey.Collation.NodeType == QilNodeType.LiteralString) {
  2264.                 // collation = runtime.GetCollation(idx);
  2265.                 this.helper.CallGetCollation(this.helper.StaticData.DeclareCollation((string)(QilLiteral)ndKey.Collation));
  2266.             }
  2267.             else {
  2268.                 // collation = runtime.CreateCollation(str);
  2269.                 this.helper.LoadQueryRuntime();
  2270.                 NestedVisitEnsureStack(ndKey.Collation);
  2271.                 this.helper.Call(XmlILMethods.CreateCollation);
  2272.             }
  2273.            
  2274.             if (ndKey.XmlType.IsSingleton) {
  2275.                 NestedVisitEnsureStack(ndKey.Key);
  2276.                
  2277.                 // keys.AddSortKey(collation, value);
  2278.                 this.helper.AddSortKey(ndKey.Key.XmlType);
  2279.             }
  2280.             else {
  2281.                 lblOnEndKey = this.helper.DefineLabel();
  2282.                 StartNestedIterator(ndKey.Key, lblOnEndKey);
  2283.                 Visit(ndKey.Key);
  2284.                 this.iterCurr.EnsureStackNoCache();
  2285.                 this.iterCurr.EnsureItemStorageType(ndKey.Key.XmlType, GetItemStorageType(ndKey.Key));
  2286.                
  2287.                 // Non-empty sort key
  2288.                 // keys.AddSortKey(collation, value);
  2289.                 this.helper.AddSortKey(ndKey.Key.XmlType);
  2290.                
  2291.                 // goto LabelDone;
  2292.                 // LabelOnEnd:
  2293.                 Label lblDone = this.helper.DefineLabel();
  2294.                 this.helper.EmitUnconditionalBranch(OpCodes.Br_S, lblDone);
  2295.                 this.helper.MarkLabel(lblOnEndKey);
  2296.                
  2297.                 // Empty sequence key
  2298.                 // keys.AddSortKey(collation);
  2299.                 this.helper.AddSortKey(null);
  2300.                
  2301.                 this.helper.MarkLabel(lblDone);
  2302.                
  2303.                 EndNestedIterator(ndKey.Key);
  2304.             }
  2305.         }
  2306.        
  2307.         /// <summary>
  2308.         /// Generate code for for QilNodeType.DocOrderDistinct.
  2309.         /// </summary>
  2310.         protected override QilNode VisitDocOrderDistinct(QilUnary ndDod)
  2311.         {
  2312.             // DocOrderDistinct applied to a singleton is a no-op
  2313.             if (ndDod.XmlType.IsSingleton)
  2314.                 return Visit(ndDod.Child);
  2315.            
  2316.             // Handle any special-case patterns that are rooted at DocOrderDistinct
  2317.             if (HandleDodPatterns(ndDod))
  2318.                 return ndDod;
  2319.            
  2320.             // Sort results of child expression by document order and remove duplicate nodes
  2321.             // cache = runtime.DocOrderDistinct(cache);
  2322.             this.helper.LoadQueryRuntime();
  2323.             NestedVisitEnsureCache(ndDod.Child, typeof(XPathNavigator));
  2324.             this.iterCurr.EnsureStack();
  2325.             this.helper.Call(XmlILMethods.DocOrder);
  2326.             return ndDod;
  2327.         }
  2328.        
  2329.         /// <summary>
  2330.         /// There are a number of path patterns that can be rooted at DocOrderDistinct nodes. Determine whether one of these
  2331.         /// patterns has been previously matched on "ndDod". If so, generate code for the pattern and return true. Otherwise,
  2332.         /// just return false.
  2333.         /// </summary>
  2334.         private bool HandleDodPatterns(QilUnary ndDod)
  2335.         {
  2336.             OptimizerPatterns pattDod = OptimizerPatterns.Read(ndDod);
  2337.             XmlNodeKindFlags kinds;
  2338.             QilName name;
  2339.             QilNode input;
  2340.             QilNode step;
  2341.             bool isJoinAndDod;
  2342.            
  2343.             // Handle JoinAndDod and DodReverse patterns
  2344.             isJoinAndDod = pattDod.MatchesPattern(OptimizerPatternName.JoinAndDod);
  2345.             if (isJoinAndDod || pattDod.MatchesPattern(OptimizerPatternName.DodReverse)) {
  2346.                 OptimizerPatterns pattStep = OptimizerPatterns.Read((QilNode)pattDod.GetArgument(OptimizerPatternArgument.DodStep));
  2347.                
  2348.                 if (pattStep.MatchesPattern(OptimizerPatternName.FilterElements)) {
  2349.                     // FilterElements pattern, so Kind = Element and Name = Argument
  2350.                     kinds = XmlNodeKindFlags.Element;
  2351.                     name = (QilName)pattStep.GetArgument(OptimizerPatternArgument.ElementQName);
  2352.                 }
  2353.                 else if (pattStep.MatchesPattern(OptimizerPatternName.FilterContentKind)) {
  2354.                     // FilterKindTest pattern, so Kind = Argument and Name = null
  2355.                     kinds = ((XmlQueryType)pattStep.GetArgument(OptimizerPatternArgument.KindTestType)).NodeKinds;
  2356.                     name = null;
  2357.                 }
  2358.                 else {
  2359.                     Debug.Assert(pattStep.MatchesPattern(OptimizerPatternName.Axis), "Dod patterns should only match if step is FilterElements or FilterKindTest or Axis");
  2360.                     kinds = ((ndDod.XmlType.NodeKinds & XmlNodeKindFlags.Attribute) != 0) ? XmlNodeKindFlags.Any : XmlNodeKindFlags.Content;
  2361.                     name = null;
  2362.                 }
  2363.                
  2364.                 step = (QilNode)pattStep.GetArgument(OptimizerPatternArgument.StepNode);
  2365.                 if (isJoinAndDod) {
  2366.                     switch (step.NodeType) {
  2367.                         case QilNodeType.Content:
  2368.                             CreateContainerIterator(ndDod, "$$$iterContent", typeof(ContentMergeIterator), XmlILMethods.ContentMergeCreate, XmlILMethods.ContentMergeNext, kinds, name, TriState.Unknown);
  2369.                             return true;
  2370.                         case QilNodeType.Descendant:
  2371.                         case QilNodeType.DescendantOrSelf:
  2372.                            
  2373.                             CreateContainerIterator(ndDod, "$$$iterDesc", typeof(DescendantMergeIterator), XmlILMethods.DescMergeCreate, XmlILMethods.DescMergeNext, kinds, name, (step.NodeType == QilNodeType.Descendant) ? TriState.False : TriState.True);
  2374.                             return true;
  2375.                         case QilNodeType.XPathFollowing:
  2376.                            
  2377.                             CreateContainerIterator(ndDod, "$$$iterFoll", typeof(XPathFollowingMergeIterator), XmlILMethods.XPFollMergeCreate, XmlILMethods.XPFollMergeNext, kinds, name, TriState.Unknown);
  2378.                             return true;
  2379.                         case QilNodeType.FollowingSibling:
  2380.                            
  2381.                             CreateContainerIterator(ndDod, "$$$iterFollSib", typeof(FollowingSiblingMergeIterator), XmlILMethods.FollSibMergeCreate, XmlILMethods.FollSibMergeNext, kinds, name, TriState.Unknown);
  2382.                             return true;
  2383.                         case QilNodeType.XPathPreceding:
  2384.                            
  2385.                             CreateContainerIterator(ndDod, "$$$iterPrec", typeof(XPathPrecedingMergeIterator), XmlILMethods.XPPrecMergeCreate, XmlILMethods.XPPrecMergeNext, kinds, name, TriState.Unknown);
  2386.                             return true;
  2387.                         default:
  2388.                            
  2389.                             Debug.Assert(false, "Pattern " + step.NodeType + " should have been handled.");
  2390.                             break;
  2391.                     }
  2392.                 }
  2393.                 else {
  2394.                     input = (QilNode)pattStep.GetArgument(OptimizerPatternArgument.StepInput);
  2395.                     switch (step.NodeType) {
  2396.                         case QilNodeType.Ancestor:
  2397.                         case QilNodeType.AncestorOrSelf:
  2398.                             CreateFilteredIterator(input, "$$$iterAnc", typeof(AncestorDocOrderIterator), XmlILMethods.AncDOCreate, XmlILMethods.AncDONext, kinds, name, (step.NodeType == QilNodeType.Ancestor) ? TriState.False : TriState.True, null);
  2399.                             return true;
  2400.                         case QilNodeType.PrecedingSibling:
  2401.                            
  2402.                             CreateFilteredIterator(input, "$$$iterPreSib", typeof(PrecedingSiblingDocOrderIterator), XmlILMethods.PreSibDOCreate, XmlILMethods.PreSibDONext, kinds, name, TriState.Unknown, null);
  2403.                             return true;
  2404.                         case QilNodeType.XPathPreceding:
  2405.                            
  2406.                             CreateFilteredIterator(input, "$$$iterPrec", typeof(XPathPrecedingDocOrderIterator), XmlILMethods.XPPrecDOCreate, XmlILMethods.XPPrecDONext, kinds, name, TriState.Unknown, null);
  2407.                             return true;
  2408.                         default:
  2409.                            
  2410.                             Debug.Assert(false, "Pattern " + step.NodeType + " should have been handled.");
  2411.                             break;
  2412.                     }
  2413.                 }
  2414.             }
  2415.             else if (pattDod.MatchesPattern(OptimizerPatternName.DodMerge)) {
  2416.                 // DodSequenceMerge dodMerge;
  2417.                 LocalBuilder locMerge = this.helper.DeclareLocal("$$$dodMerge", typeof(DodSequenceMerge));
  2418.                 Label lblOnEnd = this.helper.DefineLabel();
  2419.                
  2420.                 // dodMerge.Create(runtime);
  2421.                 this.helper.Emit(OpCodes.Ldloca, locMerge);
  2422.                 this.helper.LoadQueryRuntime();
  2423.                 this.helper.Call(XmlILMethods.DodMergeCreate);
  2424.                 this.helper.Emit(OpCodes.Ldloca, locMerge);
  2425.                
  2426.                 StartNestedIterator(ndDod.Child, lblOnEnd);
  2427.                
  2428.                 // foreach (seq in expr) {
  2429.                 Visit(ndDod.Child);
  2430.                
  2431.                 // dodMerge.AddSequence(seq);
  2432.                 Debug.Assert(this.iterCurr.Storage.IsCached, "DodMerge pattern should only be matched when cached sequences are returned from loop");
  2433.                 this.iterCurr.EnsureStack();
  2434.                 this.helper.Call(XmlILMethods.DodMergeAdd);
  2435.                 this.helper.Emit(OpCodes.Ldloca, locMerge);
  2436.                
  2437.                 // }
  2438.                 this.iterCurr.LoopToEnd(lblOnEnd);
  2439.                
  2440.                 EndNestedIterator(ndDod.Child);
  2441.                
  2442.                 // mergedSequence = dodMerge.MergeSequences();
  2443.                 this.helper.Call(XmlILMethods.DodMergeSeq);
  2444.                
  2445.                 this.iterCurr.Storage = StorageDescriptor.Stack(typeof(XPathNavigator), true);
  2446.                
  2447.                 return true;
  2448.             }
  2449.            
  2450.             return false;
  2451.         }
  2452.        
  2453.         /// <summary>
  2454.         /// Generate code for for QilNodeType.Invoke.
  2455.         /// </summary>
  2456.         protected override QilNode VisitInvoke(QilInvoke ndInvoke)
  2457.         {
  2458.             QilFunction ndFunc = ndInvoke.Function;
  2459.             MethodInfo methInfo = XmlILAnnotation.Write(ndFunc).FunctionBinding;
  2460.             bool useWriter = (XmlILConstructInfo.Read(ndFunc).ConstructMethod == XmlILConstructMethod.Writer);
  2461.             Debug.Assert(!XmlILConstructInfo.Read(ndInvoke).PushToWriterFirst || useWriter);
  2462.            
  2463.             // Push XmlQueryRuntime onto the stack as the first parameter
  2464.             this.helper.LoadQueryRuntime();
  2465.            
  2466.             // Generate code to push each Invoke argument onto the stack
  2467.             for (int iArg = 0; iArg < ndInvoke.Arguments.Count; iArg++) {
  2468.                 QilNode ndActualArg = ndInvoke.Arguments[iArg];
  2469.                 QilNode ndFormalArg = ndInvoke.Function.Arguments[iArg];
  2470.                 NestedVisitEnsureStack(ndActualArg, GetItemStorageType(ndFormalArg), !ndFormalArg.XmlType.IsSingleton);
  2471.             }
  2472.            
  2473.             // Check whether this call should compiled using the .tailcall instruction
  2474.             if (OptimizerPatterns.Read(ndInvoke).MatchesPattern(OptimizerPatternName.TailCall))
  2475.                 this.helper.TailCall(methInfo);
  2476.             else
  2477.                 this.helper.Call(methInfo);
  2478.            
  2479.             // If function's results are not pushed to Writer,
  2480.             if (!useWriter) {
  2481.                 // Return value is on the stack; ensure it has the correct storage type
  2482.                 this.iterCurr.Storage = StorageDescriptor.Stack(GetItemStorageType(ndInvoke), !ndInvoke.XmlType.IsSingleton);
  2483.             }
  2484.             else {
  2485.                 this.iterCurr.Storage = StorageDescriptor.None();
  2486.             }
  2487.            
  2488.             return ndInvoke;
  2489.         }
  2490.        
  2491.         /// <summary>
  2492.         /// Generate code for for QilNodeType.Content.
  2493.         /// </summary>
  2494.         protected override QilNode VisitContent(QilUnary ndContent)
  2495.         {
  2496.             CreateSimpleIterator(ndContent.Child, "$$$iterAttrContent", typeof(AttributeContentIterator), XmlILMethods.AttrContentCreate, XmlILMethods.AttrContentNext);
  2497.             return ndContent;
  2498.         }
  2499.        
  2500.         /// <summary>
  2501.         /// Generate code for for QilNodeType.Attribute.
  2502.         /// </summary>
  2503.         protected override QilNode VisitAttribute(QilBinary ndAttr)
  2504.         {
  2505.             QilName ndName = ndAttr.Right as QilName;
  2506.             Debug.Assert(ndName != null, "Attribute node must have a literal QName as its second argument");
  2507.            
  2508.             // XPathNavigator navAttr;
  2509.             LocalBuilder locNav = this.helper.DeclareLocal("$$$navAttr", typeof(XPathNavigator));
  2510.            
  2511.             // navAttr = SyncToNavigator(navAttr, navCtxt);
  2512.             SyncToNavigator(locNav, ndAttr.Left);
  2513.            
  2514.             // if (!navAttr.MoveToAttribute(localName, namespaceUri)) goto LabelNextCtxt;
  2515.             this.helper.Emit(OpCodes.Ldloc, locNav);
  2516.             this.helper.CallGetAtomizedName(this.helper.StaticData.DeclareName(ndName.LocalName));
  2517.             this.helper.CallGetAtomizedName(this.helper.StaticData.DeclareName(ndName.NamespaceUri));
  2518.             this.helper.Call(XmlILMethods.NavMoveAttr);
  2519.             this.helper.Emit(OpCodes.Brfalse, this.iterCurr.GetLabelNext());
  2520.            
  2521.             this.iterCurr.Storage = StorageDescriptor.Local(locNav, typeof(XPathNavigator), false);
  2522.             return ndAttr;
  2523.         }
  2524.        
  2525.         /// <summary>
  2526.         /// Generate code for for QilNodeType.Parent.
  2527.         /// </summary>
  2528.         protected override QilNode VisitParent(QilUnary ndParent)
  2529.         {
  2530.             // XPathNavigator navParent;
  2531.             LocalBuilder locNav = this.helper.DeclareLocal("$$$navParent", typeof(XPathNavigator));
  2532.            
  2533.             // navParent = SyncToNavigator(navParent, navCtxt);
  2534.             SyncToNavigator(locNav, ndParent.Child);
  2535.            
  2536.             // if (!navParent.MoveToParent()) goto LabelNextCtxt;
  2537.             this.helper.Emit(OpCodes.Ldloc, locNav);
  2538.             this.helper.Call(XmlILMethods.NavMoveParent);
  2539.             this.helper.Emit(OpCodes.Brfalse, this.iterCurr.GetLabelNext());
  2540.            
  2541.             this.iterCurr.Storage = StorageDescriptor.Local(locNav, typeof(XPathNavigator), false);
  2542.             return ndParent;
  2543.         }
  2544.        
  2545.         /// <summary>
  2546.         /// Generate code for for QilNodeType.Root.
  2547.         /// </summary>
  2548.         protected override QilNode VisitRoot(QilUnary ndRoot)
  2549.         {
  2550.             // XPathNavigator navRoot;
  2551.             LocalBuilder locNav = this.helper.DeclareLocal("$$$navRoot", typeof(XPathNavigator));
  2552.            
  2553.             // navRoot = SyncToNavigator(navRoot, navCtxt);
  2554.             SyncToNavigator(locNav, ndRoot.Child);
  2555.            
  2556.             // navRoot.MoveToRoot();
  2557.             this.helper.Emit(OpCodes.Ldloc, locNav);
  2558.             this.helper.Call(XmlILMethods.NavMoveRoot);
  2559.            
  2560.             this.iterCurr.Storage = StorageDescriptor.Local(locNav, typeof(XPathNavigator), false);
  2561.             return ndRoot;
  2562.         }
  2563.        
  2564.         /// <summary>
  2565.         /// Generate code for QilNodeType.XmlContext.
  2566.         /// </summary>
  2567.         /// <remarks>
  2568.         /// Generates code to retrieve the default document using the XmlResolver.
  2569.         /// </remarks>
  2570.         protected override QilNode VisitXmlContext(QilNode ndCtxt)
  2571.         {
  2572.             // runtime.ExternalContext.DefaultDataSource
  2573.             this.helper.LoadQueryContext();
  2574.             this.helper.Call(XmlILMethods.GetDefaultDataSource);
  2575.             this.iterCurr.Storage = StorageDescriptor.Stack(typeof(XPathNavigator), false);
  2576.             return ndCtxt;
  2577.         }
  2578.        
  2579.         /// <summary>
  2580.         /// Find physical query plan for QilNodeType.Descendant.
  2581.         /// </summary>
  2582.         protected override QilNode VisitDescendant(QilUnary ndDesc)
  2583.         {
  2584.             CreateFilteredIterator(ndDesc.Child, "$$$iterDesc", typeof(DescendantIterator), XmlILMethods.DescCreate, XmlILMethods.DescNext, XmlNodeKindFlags.Any, null, TriState.False, null);
  2585.             return ndDesc;
  2586.         }
  2587.        
  2588.         /// <summary>
  2589.         /// Generate code for for QilNodeType.DescendantOrSelf.
  2590.         /// </summary>
  2591.         protected override QilNode VisitDescendantOrSelf(QilUnary ndDesc)
  2592.         {
  2593.             CreateFilteredIterator(ndDesc.Child, "$$$iterDesc", typeof(DescendantIterator), XmlILMethods.DescCreate, XmlILMethods.DescNext, XmlNodeKindFlags.Any, null, TriState.True, null);
  2594.             return ndDesc;
  2595.         }
  2596.        
  2597.         /// <summary>
  2598.         /// Find physical query plan for QilNodeType.Ancestor.
  2599.         /// </summary>
  2600.         protected override QilNode VisitAncestor(QilUnary ndAnc)
  2601.         {
  2602.             CreateFilteredIterator(ndAnc.Child, "$$$iterAnc", typeof(AncestorIterator), XmlILMethods.AncCreate, XmlILMethods.AncNext, XmlNodeKindFlags.Any, null, TriState.False, null);
  2603.             return ndAnc;
  2604.         }
  2605.        
  2606.         /// <summary>
  2607.         /// Find physical query plan for QilNodeType.AncestorOrSelf.
  2608.         /// </summary>
  2609.         protected override QilNode VisitAncestorOrSelf(QilUnary ndAnc)
  2610.         {
  2611.             CreateFilteredIterator(ndAnc.Child, "$$$iterAnc", typeof(AncestorIterator), XmlILMethods.AncCreate, XmlILMethods.AncNext, XmlNodeKindFlags.Any, null, TriState.True, null);
  2612.             return ndAnc;
  2613.         }
  2614.        
  2615.         /// <summary>
  2616.         /// Find physical query plan for QilNodeType.Preceding.
  2617.         /// </summary>
  2618.         protected override QilNode VisitPreceding(QilUnary ndPrec)
  2619.         {
  2620.             CreateFilteredIterator(ndPrec.Child, "$$$iterPrec", typeof(PrecedingIterator), XmlILMethods.PrecCreate, XmlILMethods.PrecNext, XmlNodeKindFlags.Any, null, TriState.Unknown, null);
  2621.             return ndPrec;
  2622.         }
  2623.        
  2624.         /// <summary>
  2625.         /// Find physical query plan for QilNodeType.FollowingSibling.
  2626.         /// </summary>
  2627.         protected override QilNode VisitFollowingSibling(QilUnary ndFollSib)
  2628.         {
  2629.             CreateFilteredIterator(ndFollSib.Child, "$$$iterFollSib", typeof(FollowingSiblingIterator), XmlILMethods.FollSibCreate, XmlILMethods.FollSibNext, XmlNodeKindFlags.Any, null, TriState.Unknown, null);
  2630.             return ndFollSib;
  2631.         }
  2632.        
  2633.         /// <summary>
  2634.         /// Find physical query plan for QilNodeType.PrecedingSibling.
  2635.         /// </summary>
  2636.         protected override QilNode VisitPrecedingSibling(QilUnary ndPreSib)
  2637.         {
  2638.             CreateFilteredIterator(ndPreSib.Child, "$$$iterPreSib", typeof(PrecedingSiblingIterator), XmlILMethods.PreSibCreate, XmlILMethods.PreSibNext, XmlNodeKindFlags.Any, null, TriState.Unknown, null);
  2639.             return ndPreSib;
  2640.         }
  2641.        
  2642.         /// <summary>
  2643.         /// Find physical query plan for QilNodeType.NodeRange.
  2644.         /// </summary>
  2645.         protected override QilNode VisitNodeRange(QilBinary ndRange)
  2646.         {
  2647.             CreateFilteredIterator(ndRange.Left, "$$$iterRange", typeof(NodeRangeIterator), XmlILMethods.NodeRangeCreate, XmlILMethods.NodeRangeNext, XmlNodeKindFlags.Any, null, TriState.Unknown, ndRange.Right);
  2648.             return ndRange;
  2649.         }
  2650.        
  2651.         /// <summary>
  2652.         /// Generate code for for QilNodeType.Deref.
  2653.         /// </summary>
  2654.         protected override QilNode VisitDeref(QilBinary ndDeref)
  2655.         {
  2656.             // IdIterator iterId;
  2657.             LocalBuilder locIter = this.helper.DeclareLocal("$$$iterId", typeof(IdIterator));
  2658.            
  2659.             // iterId.Create(navCtxt, value);
  2660.             this.helper.Emit(OpCodes.Ldloca, locIter);
  2661.             NestedVisitEnsureStack(ndDeref.Left);
  2662.             NestedVisitEnsureStack(ndDeref.Right);
  2663.             this.helper.Call(XmlILMethods.IdCreate);
  2664.            
  2665.             GenerateSimpleIterator(typeof(XPathNavigator), locIter, XmlILMethods.IdNext);
  2666.            
  2667.             return ndDeref;
  2668.         }
  2669.        
  2670.         /// <summary>
  2671.         /// Generate code for QilNodeType.ElementCtor.
  2672.         /// </summary>
  2673.         protected override QilNode VisitElementCtor(QilBinary ndElem)
  2674.         {
  2675.             XmlILConstructInfo info = XmlILConstructInfo.Read(ndElem);
  2676.             bool callChk;
  2677.             GenerateNameType nameType;
  2678.             Debug.Assert(XmlILConstructInfo.Read(ndElem).PushToWriterFirst, "Element contruction should always be pushed to writer.");
  2679.            
  2680.             // Runtime checks must be made in the following cases:
  2681.             // 1. Xml state is not known at compile-time, or is illegal
  2682.             // 2. Element's namespace must be declared
  2683.             // 3. Element's attributes might be duplicates of one another, or namespaces might follow attributes
  2684.             callChk = CheckWithinContent(info) || !info.IsNamespaceInScope || ElementCachesAttributes(info);
  2685.            
  2686.             // If it is not known whether element content was output, then make this check at run-time
  2687.             if (XmlILConstructInfo.Read(ndElem.Right).FinalStates == PossibleXmlStates.Any)
  2688.                 callChk = true;
  2689.            
  2690.             // If runtime state after EndElement is called is not known, then call XmlQueryOutput.WriteEndElementChk
  2691.             if (info.FinalStates == PossibleXmlStates.Any)
  2692.                 callChk = true;
  2693.            
  2694.             // If WriteStartElementChk will *not* be called, then code must be generated to ensure valid state transitions
  2695.             if (!callChk)
  2696.                 BeforeStartChecks(ndElem);
  2697.            
  2698.             // Generate call to WriteStartElement
  2699.             nameType = LoadNameAndType(XPathNodeType.Element, ndElem.Left, true, callChk);
  2700.             this.helper.CallWriteStartElement(nameType, callChk);
  2701.            
  2702.             // Recursively construct content
  2703.             NestedVisit(ndElem.Right);
  2704.            
  2705.             // If runtime state is guaranteed to be EnumAttrs, and an element is being constructed, call XmlQueryOutput.StartElementContent
  2706.             if (XmlILConstructInfo.Read(ndElem.Right).FinalStates == PossibleXmlStates.EnumAttrs && !callChk)
  2707.                 this.helper.CallStartElementContent();
  2708.            
  2709.             // Generate call to WriteEndElement
  2710.             nameType = LoadNameAndType(XPathNodeType.Element, ndElem.Left, false, callChk);
  2711.             this.helper.CallWriteEndElement(nameType, callChk);
  2712.            
  2713.             if (!callChk)
  2714.                 AfterEndChecks(ndElem);
  2715.            
  2716.             this.iterCurr.Storage = StorageDescriptor.None();
  2717.             return ndElem;
  2718.         }
  2719.        
  2720.         /// <summary>
  2721.         /// Generate code for QilNodeType.AttributeCtor.
  2722.         /// </summary>
  2723.         protected override QilNode VisitAttributeCtor(QilBinary ndAttr)
  2724.         {
  2725.             XmlILConstructInfo info = XmlILConstructInfo.Read(ndAttr);
  2726.             bool callChk;
  2727.             GenerateNameType nameType;
  2728.             Debug.Assert(XmlILConstructInfo.Read(ndAttr).PushToWriterFirst, "Attribute construction should always be pushed to writer.");
  2729.            
  2730.             // Runtime checks must be made in the following cases:
  2731.             // 1. Xml state is not known at compile-time, or is illegal
  2732.             // 2. Attribute's namespace must be declared
  2733.             callChk = CheckEnumAttrs(info) || !info.IsNamespaceInScope;
  2734.            
  2735.             // If WriteStartAttributeChk will *not* be called, then code must be generated to ensure well-formedness
  2736.             // and track namespace scope.
  2737.             if (!callChk)
  2738.                 BeforeStartChecks(ndAttr);
  2739.            
  2740.             // Generate call to WriteStartAttribute
  2741.             nameType = LoadNameAndType(XPathNodeType.Attribute, ndAttr.Left, true, callChk);
  2742.             this.helper.CallWriteStartAttribute(nameType, callChk);
  2743.            
  2744.             // Recursively construct content
  2745.             NestedVisit(ndAttr.Right);
  2746.            
  2747.             // Generate call to WriteEndAttribute
  2748.             this.helper.CallWriteEndAttribute(callChk);
  2749.            
  2750.             if (!callChk)
  2751.                 AfterEndChecks(ndAttr);
  2752.            
  2753.             this.iterCurr.Storage = StorageDescriptor.None();
  2754.             return ndAttr;
  2755.         }
  2756.        
  2757.         /// <summary>
  2758.         /// Generate code for QilNodeType.CommentCtor.
  2759.         /// </summary>
  2760.         protected override QilNode VisitCommentCtor(QilUnary ndComment)
  2761.         {
  2762.             Debug.Assert(XmlILConstructInfo.Read(ndComment).PushToWriterFirst, "Comment construction should always be pushed to writer.");
  2763.            
  2764.             // Always call XmlQueryOutput.WriteStartComment
  2765.             this.helper.CallWriteStartComment();
  2766.            
  2767.             // Recursively construct content
  2768.             NestedVisit(ndComment.Child);
  2769.            
  2770.             // Always call XmlQueryOutput.WriteEndComment
  2771.             this.helper.CallWriteEndComment();
  2772.            
  2773.             this.iterCurr.Storage = StorageDescriptor.None();
  2774.             return ndComment;
  2775.         }
  2776.        
  2777.         /// <summary>
  2778.         /// Generate code for QilNodeType.PICtor.
  2779.         /// </summary>
  2780.         protected override QilNode VisitPICtor(QilBinary ndPI)
  2781.         {
  2782.             Debug.Assert(XmlILConstructInfo.Read(ndPI).PushToWriterFirst, "PI construction should always be pushed to writer.");
  2783.            
  2784.             // Always call XmlQueryOutput.WriteStartPI
  2785.             this.helper.LoadQueryOutput();
  2786.             NestedVisitEnsureStack(ndPI.Left);
  2787.             this.helper.CallWriteStartPI();
  2788.            
  2789.             // Recursively construct content
  2790.             NestedVisit(ndPI.Right);
  2791.            
  2792.             // Always call XmlQueryOutput.WriteEndPI
  2793.             this.helper.CallWriteEndPI();
  2794.            
  2795.             this.iterCurr.Storage = StorageDescriptor.None();
  2796.             return ndPI;
  2797.         }
  2798.        
  2799.         /// <summary>
  2800.         /// Generate code for QilNodeType.TextCtor.
  2801.         /// </summary>
  2802.         protected override QilNode VisitTextCtor(QilUnary ndText)
  2803.         {
  2804.             return VisitTextCtor(ndText, false);
  2805.         }
  2806.        
  2807.         /// <summary>
  2808.         /// Generate code for QilNodeType.RawTextCtor.
  2809.         /// </summary>
  2810.         protected override QilNode VisitRawTextCtor(QilUnary ndText)
  2811.         {
  2812.             return VisitTextCtor(ndText, true);
  2813.         }
  2814.        
  2815.         /// <summary>
  2816.         /// Generate code for QilNodeType.TextCtor and QilNodeType.RawTextCtor.
  2817.         /// </summary>
  2818.         private QilNode VisitTextCtor(QilUnary ndText, bool disableOutputEscaping)
  2819.         {
  2820.             XmlILConstructInfo info = XmlILConstructInfo.Read(ndText);
  2821.             bool callChk;
  2822.             Debug.Assert(info.PushToWriterFirst, "Text construction should always be pushed to writer.");
  2823.            
  2824.             // Write out text in different contexts (within attribute, within element, within comment, etc.)
  2825.             callChk = (info.InitialStates != PossibleXmlStates.WithinAttr) && CheckWithinContent(info);
  2826.             switch (info.InitialStates) {
  2827.                 case PossibleXmlStates.WithinAttr:
  2828.                 case PossibleXmlStates.WithinComment:
  2829.                 case PossibleXmlStates.WithinPI:
  2830.                     callChk = false;
  2831.                     break;
  2832.                 default:
  2833.                    
  2834.                     callChk = CheckWithinContent(info);
  2835.                     break;
  2836.             }
  2837.            
  2838.             if (!callChk)
  2839.                 BeforeStartChecks(ndText);
  2840.            
  2841.             this.helper.LoadQueryOutput();
  2842.            
  2843.             // Push string value of text onto IL stack
  2844.             NestedVisitEnsureStack(ndText.Child);
  2845.            
  2846.             // Write out text in different contexts (within attribute, within element, within comment, etc.)
  2847.             switch (info.InitialStates) {
  2848.                 case PossibleXmlStates.WithinSequence:
  2849.                 case PossibleXmlStates.EnumAttrs:
  2850.                 case PossibleXmlStates.WithinContent:
  2851.                 case PossibleXmlStates.Any:
  2852.                     // Call XmlQueryOutput.WriteTextBlockChk, XmlQueryOutput.WriteTextBlockNoEntities, or XmlQueryOutput.WriteTextBlock
  2853.                     this.helper.CallWriteString(disableOutputEscaping, callChk);
  2854.                     break;
  2855.                 case PossibleXmlStates.WithinAttr:
  2856.                    
  2857.                     // Ignore hints when writing out attribute text
  2858.                     this.helper.CallWriteString(false, callChk);
  2859.                     break;
  2860.                 case PossibleXmlStates.WithinComment:
  2861.                    
  2862.                     // Call XmlQueryOutput.WriteCommentString
  2863.                     this.helper.Call(XmlILMethods.CommentText);
  2864.                     break;
  2865.                 case PossibleXmlStates.WithinPI:
  2866.                    
  2867.                     // Call XmlQueryOutput.WriteProcessingInstructionString
  2868.                     this.helper.Call(XmlILMethods.PIText);
  2869.                     break;
  2870.             }
  2871.            
  2872.             if (!callChk)
  2873.                 AfterEndChecks(ndText);
  2874.            
  2875.             this.iterCurr.Storage = StorageDescriptor.None();
  2876.             return ndText;
  2877.         }
  2878.        
  2879.         /// <summary>
  2880.         /// Generate code for QilNodeType.DocumentCtor.
  2881.         /// </summary>
  2882.         protected override QilNode VisitDocumentCtor(QilUnary ndDoc)
  2883.         {
  2884.             Debug.Assert(XmlILConstructInfo.Read(ndDoc).PushToWriterFirst, "Document root construction should always be pushed to writer.");
  2885.            
  2886.             // Generate call to XmlQueryOutput.WriteStartRootChk
  2887.             this.helper.CallWriteStartRoot();
  2888.            
  2889.             // Recursively construct content
  2890.             NestedVisit(ndDoc.Child);
  2891.            
  2892.             // Generate call to XmlQueryOutput.WriteEndRootChk
  2893.             this.helper.CallWriteEndRoot();
  2894.            
  2895.             this.iterCurr.Storage = StorageDescriptor.None();
  2896.            
  2897.             return ndDoc;
  2898.         }
  2899.        
  2900.         /// <summary>
  2901.         /// Generate code for QilNodeType.NamespaceDecl.
  2902.         /// </summary>
  2903.         protected override QilNode VisitNamespaceDecl(QilBinary ndNmsp)
  2904.         {
  2905.             XmlILConstructInfo info = XmlILConstructInfo.Read(ndNmsp);
  2906.             bool callChk;
  2907.             Debug.Assert(info.PushToWriterFirst, "Namespace construction should always be pushed to writer.");
  2908.            
  2909.             // Runtime checks must be made in the following cases:
  2910.             // 1. Xml state is not known at compile-time, or is illegal
  2911.             // 2. Namespaces might be added to element after attributes have already been added
  2912.             callChk = CheckEnumAttrs(info) || MightHaveNamespacesAfterAttributes(info);
  2913.            
  2914.             // If WriteNamespaceDeclarationChk will *not* be called, then code must be generated to ensure well-formedness
  2915.             // and track namespace scope.
  2916.             if (!callChk)
  2917.                 BeforeStartChecks(ndNmsp);
  2918.            
  2919.             this.helper.LoadQueryOutput();
  2920.            
  2921.             // Recursively construct prefix and ns
  2922.             NestedVisitEnsureStack(ndNmsp.Left);
  2923.             NestedVisitEnsureStack(ndNmsp.Right);
  2924.            
  2925.             // Generate call to WriteNamespaceDecl
  2926.             this.helper.CallWriteNamespaceDecl(callChk);
  2927.            
  2928.             if (!callChk)
  2929.                 AfterEndChecks(ndNmsp);
  2930.            
  2931.             this.iterCurr.Storage = StorageDescriptor.None();
  2932.             return ndNmsp;
  2933.         }
  2934.        
  2935.         /// <summary>
  2936.         /// Generate code for for QilNodeType.RtfCtor.
  2937.         /// </summary>
  2938.         protected override QilNode VisitRtfCtor(QilBinary ndRtf)
  2939.         {
  2940.             OptimizerPatterns patt = OptimizerPatterns.Read(ndRtf);
  2941.             string baseUri = (string)(QilLiteral)ndRtf.Right;
  2942.            
  2943.             if (patt.MatchesPattern(OptimizerPatternName.SingleTextRtf)) {
  2944.                 // Special-case Rtf containing a root node and a single text node child
  2945.                 this.helper.LoadQueryRuntime();
  2946.                 NestedVisitEnsureStack((QilNode)patt.GetArgument(OptimizerPatternArgument.RtfText));
  2947.                 this.helper.Emit(OpCodes.Ldstr, baseUri);
  2948.                 this.helper.Call(XmlILMethods.RtfConstr);
  2949.             }
  2950.             else {
  2951.                 // Start nested construction of an Rtf
  2952.                 this.helper.CallStartRtfConstruction(baseUri);
  2953.                
  2954.                 // Write content of Rtf to writer
  2955.                 NestedVisit(ndRtf.Left);
  2956.                
  2957.                 // Get the result Rtf
  2958.                 this.helper.CallEndRtfConstruction();
  2959.             }
  2960.            
  2961.             this.iterCurr.Storage = StorageDescriptor.Stack(typeof(XPathNavigator), false);
  2962.             return ndRtf;
  2963.         }
  2964.        
  2965.         /// <summary>
  2966.         /// Generate code for QilNodeType.NameOf.
  2967.         /// </summary>
  2968.         protected override QilNode VisitNameOf(QilUnary ndName)
  2969.         {
  2970.             return VisitNodeProperty(ndName);
  2971.         }
  2972.        
  2973.         /// <summary>
  2974.         /// Generate code for QilNodeType.LocalNameOf.
  2975.         /// </summary>
  2976.         protected override QilNode VisitLocalNameOf(QilUnary ndName)
  2977.         {
  2978.             return VisitNodeProperty(ndName);
  2979.         }
  2980.        
  2981.         /// <summary>
  2982.         /// Generate code for QilNodeType.NamespaceUriOf.
  2983.         /// </summary>
  2984.         protected override QilNode VisitNamespaceUriOf(QilUnary ndName)
  2985.         {
  2986.             return VisitNodeProperty(ndName);
  2987.         }
  2988.        
  2989.         /// <summary>
  2990.         /// Generate code for QilNodeType.PrefixOf.
  2991.         /// </summary>
  2992.         protected override QilNode VisitPrefixOf(QilUnary ndName)
  2993.         {
  2994.             return VisitNodeProperty(ndName);
  2995.         }
  2996.        
  2997.         /// <summary>
  2998.         /// Generate code to push the local name, namespace uri, or qname of the context navigator.
  2999.         /// </summary>
  3000.         private QilNode VisitNodeProperty(QilUnary ndProp)
  3001.         {
  3002.             // Generate code to push argument onto stack
  3003.             NestedVisitEnsureStack(ndProp.Child);
  3004.            
  3005.             switch (ndProp.NodeType) {
  3006.                 case QilNodeType.NameOf:
  3007.                     // push new XmlQualifiedName(navigator.LocalName, navigator.NamespaceURI);
  3008.                     this.helper.Emit(OpCodes.Dup);
  3009.                     this.helper.Call(XmlILMethods.NavLocalName);
  3010.                     this.helper.Call(XmlILMethods.NavNmsp);
  3011.                     this.helper.Construct(XmlILConstructors.QName);
  3012.                     this.iterCurr.Storage = StorageDescriptor.Stack(typeof(XmlQualifiedName), false);
  3013.                     break;
  3014.                 case QilNodeType.LocalNameOf:
  3015.                    
  3016.                     // push navigator.Name;
  3017.                     this.helper.Call(XmlILMethods.NavLocalName);
  3018.                     this.iterCurr.Storage = StorageDescriptor.Stack(typeof(string), false);
  3019.                     break;
  3020.                 case QilNodeType.NamespaceUriOf:
  3021.                    
  3022.                     // push navigator.NamespaceURI;
  3023.                     this.helper.Call(XmlILMethods.NavNmsp);
  3024.                     this.iterCurr.Storage = StorageDescriptor.Stack(typeof(string), false);
  3025.                     break;
  3026.                 case QilNodeType.PrefixOf:
  3027.                    
  3028.                     // push navigator.Prefix;
  3029.                     this.helper.Call(XmlILMethods.NavPrefix);
  3030.                     this.iterCurr.Storage = StorageDescriptor.Stack(typeof(string), false);
  3031.                     break;
  3032.                 default:
  3033.                    
  3034.                     Debug.Assert(false);
  3035.                     break;
  3036.             }
  3037.            
  3038.             return ndProp;
  3039.         }
  3040.        
  3041.         /// <summary>
  3042.         /// Find physical query plan for QilNodeType.TypeAssert.
  3043.         /// </summary>
  3044.         protected override QilNode VisitTypeAssert(QilTargetType ndTypeAssert)
  3045.         {
  3046.             if (!ndTypeAssert.Source.XmlType.IsSingleton && !this.iterCurr.HasLabelNext) {
  3047.                 // This case occurs when a non-singleton expression is treated as cardinality One.
  3048.                 // The trouble is that the expression will branch to an end label when it's done iterating, so
  3049.                 // an end label must be provided. But there is no next label in the current iteration context,
  3050.                 // so we've got to create a dummy label instead (IL requires it). This creates an infinite loop,
  3051.                 // but since it's known statically that the expression is cardinality One, this branch will never
  3052.                 // be taken.
  3053.                 Label lblDummy = this.helper.DefineLabel();
  3054.                 this.helper.MarkLabel(lblDummy);
  3055.                 NestedVisit(ndTypeAssert.Source, lblDummy);
  3056.             }
  3057.             else {
  3058.                 // Generate code for child expression
  3059.                 Visit(ndTypeAssert.Source);
  3060.             }
  3061.            
  3062.             this.iterCurr.EnsureItemStorageType(ndTypeAssert.Source.XmlType, GetItemStorageType(ndTypeAssert));
  3063.             return ndTypeAssert;
  3064.         }
  3065.        
  3066.         /// <summary>
  3067.         /// Generate code for QilNodeType.IsType.
  3068.         /// </summary>
  3069.         protected override QilNode VisitIsType(QilTargetType ndIsType)
  3070.         {
  3071.             XmlQueryType typDerived;
  3072.             XmlQueryType typBase;
  3073.             XmlTypeCode codeBase;
  3074.            
  3075.             typDerived = ndIsType.Source.XmlType;
  3076.             typBase = ndIsType.TargetType;
  3077.             Debug.Assert(!typDerived.NeverSubtypeOf(typBase), "Normalizer should have eliminated IsType where source can never be a subtype of destination type.");
  3078.            
  3079.             // Special Case: Test whether singleton item is a Node
  3080.             if (typDerived.IsSingleton && Ref.Equals(typBase, TypeFactory.Node)) {
  3081.                 NestedVisitEnsureStack(ndIsType.Source);
  3082.                 Debug.Assert(this.iterCurr.Storage.ItemStorageType == typeof(XPathItem), "If !IsNode, then storage type should be Item");
  3083.                
  3084.                 // if (item.IsNode op true) goto LabelBranch;
  3085.                 this.helper.Call(XmlILMethods.ItemIsNode);
  3086.                 ZeroCompare(QilNodeType.Ne, true);
  3087.                
  3088.                 return ndIsType;
  3089.             }
  3090.            
  3091.             // Special Case: Source value is a singleton Node, and we're testing whether it is an Element, Attribute, PI, etc.
  3092.             if (MatchesNodeKinds(ndIsType, typDerived, typBase))
  3093.                 return ndIsType;
  3094.            
  3095.             // Special Case: XmlTypeCode is sufficient to describe destination type
  3096.             if (Ref.Equals(typBase, TypeFactory.Double))
  3097.                 codeBase = XmlTypeCode.Double;
  3098.             else if (Ref.Equals(typBase, TypeFactory.String))
  3099.                 codeBase = XmlTypeCode.String;
  3100.             else if (Ref.Equals(typBase, TypeFactory.Boolean))
  3101.                 codeBase = XmlTypeCode.Boolean;
  3102.             else if (Ref.Equals(typBase, TypeFactory.Node))
  3103.                 codeBase = XmlTypeCode.Node;
  3104.             else
  3105.                 codeBase = XmlTypeCode.None;
  3106.            
  3107.             if (codeBase != XmlTypeCode.None) {
  3108.                 // if (runtime.MatchesXmlType(value, code) op true) goto LabelBranch;
  3109.                 this.helper.LoadQueryRuntime();
  3110.                 NestedVisitEnsureStack(ndIsType.Source, typeof(XPathItem), !typDerived.IsSingleton);
  3111.                 this.helper.LoadInteger((int)codeBase);
  3112.                 this.helper.Call(typDerived.IsSingleton ? XmlILMethods.ItemMatchesCode : XmlILMethods.SeqMatchesCode);
  3113.                 ZeroCompare(QilNodeType.Ne, true);
  3114.                
  3115.                 return ndIsType;
  3116.             }
  3117.            
  3118.             // if (runtime.MatchesXmlType(value, idxType) op true) goto LabelBranch;
  3119.             this.helper.LoadQueryRuntime();
  3120.             NestedVisitEnsureStack(ndIsType.Source, typeof(XPathItem), !typDerived.IsSingleton);
  3121.             this.helper.LoadInteger(this.helper.StaticData.DeclareXmlType(typBase));
  3122.             this.helper.Call(typDerived.IsSingleton ? XmlILMethods.ItemMatchesType : XmlILMethods.SeqMatchesType);
  3123.             ZeroCompare(QilNodeType.Ne, true);
  3124.            
  3125.             return ndIsType;
  3126.         }
  3127.        
  3128.         /// <summary>
  3129.         /// 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.
  3130.         /// Otherwise, return false, and a call to MatchesXmlType will be generated instead.
  3131.         /// </summary>
  3132.         private bool MatchesNodeKinds(QilTargetType ndIsType, XmlQueryType typDerived, XmlQueryType typBase)
  3133.         {
  3134.             XmlNodeKindFlags kinds;
  3135.             bool allowKinds = true;
  3136.             XPathNodeType kindsRuntime;
  3137.             int kindsUnion;
  3138.            
  3139.             // If not checking whether typDerived is some kind of singleton node, then fallback to MatchesXmlType
  3140.             if (!typBase.IsNode || !typBase.IsSingleton)
  3141.                 return false;
  3142.            
  3143.             // If typDerived is not statically guaranteed to be a singleton node (and not an rtf), then fallback to MatchesXmlType
  3144.             if (!typDerived.IsNode || !typDerived.IsSingleton || !typDerived.IsNotRtf)
  3145.                 return false;
  3146.            
  3147.             // Now we are guaranteed that typDerived is a node, and typBase is a node, so check node kinds
  3148.             // Ensure that typBase is only composed of kind-test prime types (no name-test, no schema-test, etc.)
  3149.             kinds = XmlNodeKindFlags.None;
  3150.             foreach (XmlQueryType typItem in typBase) {
  3151.                 if (Ref.Equals(typItem, TypeFactory.Element))
  3152.                     kinds |= XmlNodeKindFlags.Element;
  3153.                 else if (Ref.Equals(typItem, TypeFactory.Attribute))
  3154.                     kinds |= XmlNodeKindFlags.Attribute;
  3155.                 else if (Ref.Equals(typItem, TypeFactory.Text))
  3156.                     kinds |= XmlNodeKindFlags.Text;
  3157.                 else if (Ref.Equals(typItem, TypeFactory.Document))
  3158.                     kinds |= XmlNodeKindFlags.Document;
  3159.                 else if (Ref.Equals(typItem, TypeFactory.Comment))
  3160.                     kinds |= XmlNodeKindFlags.Comment;
  3161.                 else if (Ref.Equals(typItem, TypeFactory.PI))
  3162.                     kinds |= XmlNodeKindFlags.PI;
  3163.                 else if (Ref.Equals(typItem, TypeFactory.Namespace))
  3164.                     kinds |= XmlNodeKindFlags.Namespace;
  3165.                 else
  3166.                     return false;
  3167.             }
  3168.            
  3169.             Debug.Assert((typDerived.NodeKinds & kinds) != XmlNodeKindFlags.None, "Normalizer should have taken care of case where node kinds are disjoint.");
  3170.            
  3171.             kinds = typDerived.NodeKinds & kinds;
  3172.            
  3173.             // Attempt to allow or disallow exactly one kind
  3174.             if (!Bits.ExactlyOne((uint)kinds)) {
  3175.                 // Not possible to allow one kind, so try to disallow one kind
  3176.                 kinds = ~kinds & XmlNodeKindFlags.Any;
  3177.                 allowKinds = !allowKinds;
  3178.             }
  3179.            
  3180.             switch (kinds) {
  3181.                 case XmlNodeKindFlags.Element:
  3182.                     kindsRuntime = XPathNodeType.Element;
  3183.                     break;
  3184.                 case XmlNodeKindFlags.Attribute:
  3185.                     kindsRuntime = XPathNodeType.Attribute;
  3186.                     break;
  3187.                 case XmlNodeKindFlags.Namespace:
  3188.                     kindsRuntime = XPathNodeType.Namespace;
  3189.                     break;
  3190.                 case XmlNodeKindFlags.PI:
  3191.                     kindsRuntime = XPathNodeType.ProcessingInstruction;
  3192.                     break;
  3193.                 case XmlNodeKindFlags.Comment:
  3194.                     kindsRuntime = XPathNodeType.Comment;
  3195.                     break;
  3196.                 case XmlNodeKindFlags.Document:
  3197.                     kindsRuntime = XPathNodeType.Root;
  3198.                     break;
  3199.                 default:
  3200.                    
  3201.                     // Union of several types (when testing for Text, we need to test for Whitespace as well)
  3202.                    
  3203.                     // if (((1 << navigator.NodeType) & nodesDisallow) op 0) goto LabelBranch;
  3204.                     this.helper.Emit(OpCodes.Ldc_I4_1);
  3205.                     kindsRuntime = XPathNodeType.All;
  3206.                     break;
  3207.             }
  3208.            
  3209.             // Push navigator.NodeType onto the stack
  3210.             NestedVisitEnsureStack(ndIsType.Source);
  3211.             this.helper.Call(XmlILMethods.NavType);
  3212.            
  3213.             if (kindsRuntime == XPathNodeType.All) {
  3214.                 // if (((1 << navigator.NodeType) & kindsUnion) op 0) goto LabelBranch;
  3215.                 this.helper.Emit(OpCodes.Shl);
  3216.                
  3217.                 kindsUnion = 0;
  3218.                 if ((kinds & XmlNodeKindFlags.Document) != 0)
  3219.                     kindsUnion |= (1 << (int)XPathNodeType.Root);
  3220.                 if ((kinds & XmlNodeKindFlags.Element) != 0)
  3221.                     kindsUnion |= (1 << (int)XPathNodeType.Element);
  3222.                 if ((kinds & XmlNodeKindFlags.Attribute) != 0)
  3223.                     kindsUnion |= (1 << (int)XPathNodeType.Attribute);
  3224.                 if ((kinds & XmlNodeKindFlags.Text) != 0)
  3225.                     kindsUnion |= (1 << (int)(int)XPathNodeType.Text) | (1 << (int)(int)XPathNodeType.SignificantWhitespace) | (1 << (int)(int)XPathNodeType.Whitespace);
  3226.                 if ((kinds & XmlNodeKindFlags.Comment) != 0)
  3227.                     kindsUnion |= (1 << (int)XPathNodeType.Comment);
  3228.                 if ((kinds & XmlNodeKindFlags.PI) != 0)
  3229.                     kindsUnion |= (1 << (int)XPathNodeType.ProcessingInstruction);
  3230.                 if ((kinds & XmlNodeKindFlags.Namespace) != 0)
  3231.                     kindsUnion |= (1 << (int)XPathNodeType.Namespace);
  3232.                
  3233.                 this.helper.LoadInteger(kindsUnion);
  3234.                 this.helper.Emit(OpCodes.And);
  3235.                 ZeroCompare(allowKinds ? QilNodeType.Ne : QilNodeType.Eq, false);
  3236.             }
  3237.             else {
  3238.                 // if (navigator.NodeType op runtimeItem) goto LabelBranch;
  3239.                 this.helper.LoadInteger((int)kindsRuntime);
  3240.                 ClrCompare(allowKinds ? QilNodeType.Eq : QilNodeType.Ne, XmlTypeCode.Int);
  3241.             }
  3242.            
  3243.             return true;
  3244.         }
  3245.        
  3246.         /// <summary>
  3247.         /// Generate code for QilNodeType.IsEmpty.
  3248.         /// </summary>
  3249.         /// <remarks>
  3250.         /// BranchingContext.OnFalse context: is-empty(expr)
  3251.         /// ==> foreach (item in expr)
  3252.         /// goto LabelBranch;
  3253.         ///
  3254.         /// BranchingContext.OnTrue context: is-empty(expr)
  3255.         /// ==> foreach (item in expr)
  3256.         /// break;
  3257.         /// ...
  3258.         /// LabelOnEnd: (called if foreach is empty)
  3259.         /// goto LabelBranch;
  3260.         ///
  3261.         /// BranchingContext.None context: is-empty(expr)
  3262.         /// ==> foreach (item in expr)
  3263.         /// break;
  3264.         /// push true();
  3265.         /// ...
  3266.         /// LabelOnEnd: (called if foreach is empty)
  3267.         /// push false();
  3268.         /// </remarks>
  3269.         protected override QilNode VisitIsEmpty(QilUnary ndIsEmpty)
  3270.         {
  3271.             Label lblTrue;
  3272.            
  3273.             // If the child expression returns a cached result,
  3274.             if (CachesResult(ndIsEmpty.Child)) {
  3275.                 // Then get the count directly from the cache
  3276.                 NestedVisitEnsureStack(ndIsEmpty.Child);
  3277.                 this.helper.CallCacheCount(this.iterNested.Storage.ItemStorageType);
  3278.                
  3279.                 switch (this.iterCurr.CurrentBranchingContext) {
  3280.                     case BranchingContext.OnFalse:
  3281.                         // Take false path if count != 0
  3282.                         this.helper.TestAndBranch(0, this.iterCurr.LabelBranch, OpCodes.Bne_Un);
  3283.                         break;
  3284.                     case BranchingContext.OnTrue:
  3285.                        
  3286.                         // Take true path if count == 0
  3287.                         this.helper.TestAndBranch(0, this.iterCurr.LabelBranch, OpCodes.Beq);
  3288.                         break;
  3289.                     default:
  3290.                        
  3291.                         Debug.Assert(this.iterCurr.CurrentBranchingContext == BranchingContext.None);
  3292.                        
  3293.                         // if (count == 0) goto LabelTrue;
  3294.                         lblTrue = this.helper.DefineLabel();
  3295.                         this.helper.Emit(OpCodes.Brfalse_S, lblTrue);
  3296.                        
  3297.                         // Convert branch targets into push of true/false
  3298.                         this.helper.ConvBranchToBool(lblTrue, true);
  3299.                         break;
  3300.                 }
  3301.             }
  3302.             else {
  3303.                 Label lblOnEnd = this.helper.DefineLabel();
  3304.                 IteratorDescriptor iterParent = this.iterCurr;
  3305.                
  3306.                 // Forward any LabelOnEnd jumps to LabelBranch if BranchingContext.OnTrue
  3307.                 if (iterParent.CurrentBranchingContext == BranchingContext.OnTrue)
  3308.                     StartNestedIterator(ndIsEmpty.Child, this.iterCurr.LabelBranch);
  3309.                 else
  3310.                     StartNestedIterator(ndIsEmpty.Child, lblOnEnd);
  3311.                
  3312.                 Visit(ndIsEmpty.Child);
  3313.                
  3314.                 // Pop value of IsEmpty expression from the stack if necessary
  3315.                 this.iterCurr.EnsureNoCache();
  3316.                 this.iterCurr.DiscardStack();
  3317.                
  3318.                 switch (iterParent.CurrentBranchingContext) {
  3319.                     case BranchingContext.OnFalse:
  3320.                         // Reverse polarity of iterator
  3321.                         this.helper.EmitUnconditionalBranch(OpCodes.Br, iterParent.LabelBranch);
  3322.                         this.helper.MarkLabel(lblOnEnd);
  3323.                         break;
  3324.                     case BranchingContext.OnTrue:
  3325.                        
  3326.                         // Nothing to do
  3327.                         break;
  3328.                     case BranchingContext.None:
  3329.                        
  3330.                         // Convert branch targets into push of true/false
  3331.                         this.helper.ConvBranchToBool(lblOnEnd, true);
  3332.                         break;
  3333.                 }
  3334.                
  3335.                 // End nested iterator
  3336.                 EndNestedIterator(ndIsEmpty.Child);
  3337.             }
  3338.            
  3339.             if (this.iterCurr.IsBranching)
  3340.                 this.iterCurr.Storage = StorageDescriptor.None();
  3341.             else
  3342.                 this.iterCurr.Storage = StorageDescriptor.Stack(typeof(bool), false);
  3343.            
  3344.             return ndIsEmpty;
  3345.         }
  3346.        
  3347.         /// <summary>
  3348.         /// Generate code for QilNodeType.XPathNodeValue.
  3349.         /// </summary>
  3350.         protected override QilNode VisitXPathNodeValue(QilUnary ndVal)
  3351.         {
  3352.             Label lblOnEnd;
  3353.             Label lblDone;
  3354.             Debug.Assert(ndVal.Child.XmlType.IsNode, "XPathNodeValue node may only be applied to a sequence of Nodes.");
  3355.            
  3356.             // If the expression is a singleton,
  3357.             if (ndVal.Child.XmlType.IsSingleton) {
  3358.                 // Then generate code to push expresion result onto the stack
  3359.                 NestedVisitEnsureStack(ndVal.Child, typeof(XPathNavigator), false);
  3360.                
  3361.                 // navigator.Value;
  3362.                 this.helper.Call(XmlILMethods.Value);
  3363.             }
  3364.             else {
  3365.                 lblOnEnd = this.helper.DefineLabel();
  3366.                
  3367.                 // Construct nested iterator and iterate over results
  3368.                 StartNestedIterator(ndVal.Child, lblOnEnd);
  3369.                 Visit(ndVal.Child);
  3370.                 this.iterCurr.EnsureStackNoCache();
  3371.                
  3372.                 // navigator.Value;
  3373.                 this.helper.Call(XmlILMethods.Value);
  3374.                
  3375.                 // Handle empty sequence by pushing empty string onto the stack
  3376.                 lblDone = this.helper.DefineLabel();
  3377.                 this.helper.EmitUnconditionalBranch(OpCodes.Br, lblDone);
  3378.                 this.helper.MarkLabel(lblOnEnd);
  3379.                 this.helper.Emit(OpCodes.Ldstr, "");
  3380.                 this.helper.MarkLabel(lblDone);
  3381.                
  3382.                 // End nested iterator
  3383.                 EndNestedIterator(ndVal.Child);
  3384.             }
  3385.            
  3386.             this.iterCurr.Storage = StorageDescriptor.Stack(typeof(string), false);
  3387.            
  3388.             return ndVal;
  3389.         }
  3390.        
  3391.         /// <summary>
  3392.         /// Find physical query plan for QilNodeType.XPathFollowing.
  3393.         /// </summary>
  3394.         protected override QilNode VisitXPathFollowing(QilUnary ndFoll)
  3395.         {
  3396.             CreateFilteredIterator(ndFoll.Child, "$$$iterFoll", typeof(XPathFollowingIterator), XmlILMethods.XPFollCreate, XmlILMethods.XPFollNext, XmlNodeKindFlags.Any, null, TriState.Unknown, null);
  3397.             return ndFoll;
  3398.         }
  3399.        
  3400.         /// <summary>
  3401.         /// Find physical query plan for QilNodeType.XPathPreceding.
  3402.         /// </summary>
  3403.         protected override QilNode VisitXPathPreceding(QilUnary ndPrec)
  3404.         {
  3405.             CreateFilteredIterator(ndPrec.Child, "$$$iterPrec", typeof(XPathPrecedingIterator), XmlILMethods.XPPrecCreate, XmlILMethods.XPPrecNext, XmlNodeKindFlags.Any, null, TriState.Unknown, null);
  3406.             return ndPrec;
  3407.         }
  3408.        
  3409.         /// <summary>
  3410.         /// Find physical query plan for QilNodeType.XPathNamespace.
  3411.         /// </summary>
  3412.         protected override QilNode VisitXPathNamespace(QilUnary ndNmsp)
  3413.         {
  3414.             CreateSimpleIterator(ndNmsp.Child, "$$$iterNmsp", typeof(NamespaceIterator), XmlILMethods.NmspCreate, XmlILMethods.NmspNext);
  3415.             return ndNmsp;
  3416.         }
  3417.        
  3418.         /// <summary>
  3419.         /// Generate code for QilNodeType.XsltGenerateId.
  3420.         /// </summary>
  3421.         protected override QilNode VisitXsltGenerateId(QilUnary ndGenId)
  3422.         {
  3423.             Label lblOnEnd;
  3424.             Label lblDone;
  3425.            
  3426.             this.helper.LoadQueryRuntime();
  3427.            
  3428.             // If the expression is a singleton,
  3429.             if (ndGenId.Child.XmlType.IsSingleton) {
  3430.                 // Then generate code to push expresion result onto the stack
  3431.                 NestedVisitEnsureStack(ndGenId.Child, typeof(XPathNavigator), false);
  3432.                
  3433.                 // runtime.GenerateId(value);
  3434.                 this.helper.Call(XmlILMethods.GenId);
  3435.             }
  3436.             else {
  3437.                 lblOnEnd = this.helper.DefineLabel();
  3438.                
  3439.                 // Construct nested iterator and iterate over results
  3440.                 StartNestedIterator(ndGenId.Child, lblOnEnd);
  3441.                 Visit(ndGenId.Child);
  3442.                 this.iterCurr.EnsureStackNoCache();
  3443.                 this.iterCurr.EnsureItemStorageType(ndGenId.Child.XmlType, typeof(XPathNavigator));
  3444.                
  3445.                 // runtime.GenerateId(value);
  3446.                 this.helper.Call(XmlILMethods.GenId);
  3447.                
  3448.                 // Handle empty sequence by pushing empty string onto the stack
  3449.                 lblDone = this.helper.DefineLabel();
  3450.                 this.helper.EmitUnconditionalBranch(OpCodes.Br, lblDone);
  3451.                 this.helper.MarkLabel(lblOnEnd);
  3452.                 this.helper.Emit(OpCodes.Pop);
  3453.                 this.helper.Emit(OpCodes.Ldstr, "");
  3454.                 this.helper.MarkLabel(lblDone);
  3455.                
  3456.                 // End nested iterator
  3457.                 EndNestedIterator(ndGenId.Child);
  3458.             }
  3459.            
  3460.             this.iterCurr.Storage = StorageDescriptor.Stack(typeof(string), false);
  3461.            
  3462.             return ndGenId;
  3463.         }
  3464.        
  3465.         /// <summary>
  3466.         /// Generate code for for QilNodeType.XsltInvokeLateBound.
  3467.         /// </summary>
  3468.         protected override QilNode VisitXsltInvokeLateBound(QilInvokeLateBound ndInvoke)
  3469.         {
  3470.             LocalBuilder locArgs = this.helper.DeclareLocal("$$$args", typeof(IList<XPathItem>[]));
  3471.             QilName ndName = (QilName)ndInvoke.Name;
  3472.             Debug.Assert(XmlILConstructInfo.Read(ndInvoke).ConstructMethod != XmlILConstructMethod.Writer);
  3473.            
  3474.             // runtime.ExternalContext.InvokeXsltLateBoundFunction(name, ns, args);
  3475.             this.helper.LoadQueryContext();
  3476.             this.helper.Emit(OpCodes.Ldstr, ndName.LocalName);
  3477.             this.helper.Emit(OpCodes.Ldstr, ndName.NamespaceUri);
  3478.            
  3479.             // args = new IList<XPathItem>[argCount];
  3480.             this.helper.LoadInteger(ndInvoke.Arguments.Count);
  3481.             this.helper.Emit(OpCodes.Newarr, typeof(IList<XPathItem>));
  3482.             this.helper.Emit(OpCodes.Stloc, locArgs);
  3483.            
  3484.             for (int iArg = 0; iArg < ndInvoke.Arguments.Count; iArg++) {
  3485.                 QilNode ndArg = ndInvoke.Arguments[iArg];
  3486.                
  3487.                 // args[0] = arg0;
  3488.                 // ...
  3489.                 // args[N] = argN;
  3490.                 this.helper.Emit(OpCodes.Ldloc, locArgs);
  3491.                 this.helper.LoadInteger(iArg);
  3492.                 this.helper.Emit(OpCodes.Ldelema, typeof(IList<XPathItem>));
  3493.                
  3494.                 NestedVisitEnsureCache(ndArg, typeof(XPathItem));
  3495.                 this.iterCurr.EnsureStack();
  3496.                
  3497.                 this.helper.Emit(OpCodes.Stobj, typeof(IList<XPathItem>));
  3498.             }
  3499.            
  3500.             this.helper.Emit(OpCodes.Ldloc, locArgs);
  3501.            
  3502.             this.helper.Call(XmlILMethods.InvokeXsltLate);
  3503.            
  3504.             // Returned item sequence is on the stack
  3505.             this.iterCurr.Storage = StorageDescriptor.Stack(typeof(XPathItem), true);
  3506.            
  3507.             return ndInvoke;
  3508.         }
  3509.        
  3510.         /// <summary>
  3511.         /// Generate code for for QilNodeType.XsltInvokeEarlyBound.
  3512.         /// </summary>
  3513.         protected override QilNode VisitXsltInvokeEarlyBound(QilInvokeEarlyBound ndInvoke)
  3514.         {
  3515.             QilName ndName = ndInvoke.Name;
  3516.             XmlExtensionFunction extFunc;
  3517.             Type clrTypeRetSrc;
  3518.             Type clrTypeRetDst;
  3519.            
  3520.             // Retrieve metadata from the extension function
  3521.             extFunc = new XmlExtensionFunction(ndName.LocalName, ndName.NamespaceUri, ndInvoke.ClrMethod);
  3522.             clrTypeRetSrc = extFunc.ClrReturnType;
  3523.             clrTypeRetDst = GetStorageType(ndInvoke);
  3524.            
  3525.             // Prepare to call runtime.ChangeTypeXsltResult
  3526.             if (clrTypeRetSrc != clrTypeRetDst && !ndInvoke.XmlType.IsEmpty) {
  3527.                 this.helper.LoadQueryRuntime();
  3528.                 this.helper.LoadInteger(this.helper.StaticData.DeclareXmlType(ndInvoke.XmlType));
  3529.             }
  3530.            
  3531.             // If this is not a static method, then get the instance object
  3532.             if (!extFunc.Method.IsStatic) {
  3533.                 // Special-case the XsltLibrary object
  3534.                 if (ndName.NamespaceUri.Length == 0)
  3535.                     this.helper.LoadXsltLibrary();
  3536.                 else
  3537.                     this.helper.CallGetEarlyBoundObject(this.helper.StaticData.DeclareEarlyBound(ndName.NamespaceUri, extFunc.Method), extFunc.Method.DeclaringType);
  3538.             }
  3539.            
  3540.             // Generate code to push each Invoke argument onto the stack
  3541.             for (int iArg = 0; iArg < ndInvoke.Arguments.Count; iArg++) {
  3542.                 QilNode ndActualArg;
  3543.                 XmlQueryType xmlTypeFormalArg;
  3544.                 Type clrTypeActualArg;
  3545.                 Type clrTypeFormalArg;
  3546.                
  3547.                 ndActualArg = ndInvoke.Arguments[iArg];
  3548.                
  3549.                 // Infer Xml type and Clr type of formal argument
  3550.                 xmlTypeFormalArg = extFunc.GetXmlArgumentType(iArg);
  3551.                 clrTypeFormalArg = extFunc.GetClrArgumentType(iArg);
  3552.                
  3553.                 Debug.Assert(ndActualArg.XmlType.IsSubtypeOf(xmlTypeFormalArg), "Xml type of actual arg must be a subtype of the Xml type of the formal arg");
  3554.                
  3555.                 // Use different conversion rules for internal Xslt libraries. If the actual argument is
  3556.                 // stored using Clr type T, then library must use type T, XPathItem, IList<T>, or IList<XPathItem>.
  3557.                 // If the actual argument is stored using Clr type IList<T>, then library must use type
  3558.                 // IList<T> or IList<XPathItem>. This is to ensure that there will not be unnecessary
  3559.                 // conversions that take place when calling into an internal library.
  3560.                 if (ndName.NamespaceUri.Length == 0) {
  3561.                     Type itemType = GetItemStorageType(ndActualArg);
  3562.                    
  3563.                     if (clrTypeFormalArg == XmlILMethods.StorageMethods[itemType].IListType) {
  3564.                         // Formal type is IList<T>
  3565.                         NestedVisitEnsureStack(ndActualArg, itemType, true);
  3566.                     }
  3567.                     else if (clrTypeFormalArg == XmlILMethods.StorageMethods[typeof(XPathItem)].IListType) {
  3568.                         // Formal type is IList<XPathItem>
  3569.                         NestedVisitEnsureStack(ndActualArg, typeof(XPathItem), true);
  3570.                     }
  3571.                     else if ((ndActualArg.XmlType.IsSingleton && clrTypeFormalArg == itemType) || ndActualArg.XmlType.TypeCode == XmlTypeCode.None) {
  3572.                         // Formal type is T
  3573.                         NestedVisitEnsureStack(ndActualArg, itemType, false);
  3574.                     }
  3575.                     else if (ndActualArg.XmlType.IsSingleton && clrTypeFormalArg == typeof(XPathItem)) {
  3576.                         // Formal type is XPathItem
  3577.                         NestedVisitEnsureStack(ndActualArg, typeof(XPathItem), false);
  3578.                     }
  3579.                     else
  3580.                         Debug.Fail("Internal Xslt library may not use parameters of type " + clrTypeFormalArg);
  3581.                 }
  3582.                 else {
  3583.                     // There is an implicit upcast to the Xml type of the formal argument. This can change the Clr storage type.
  3584.                     clrTypeActualArg = GetStorageType(xmlTypeFormalArg);
  3585.                    
  3586.                     // If the formal Clr type is typeof(object) or if it is not a supertype of the actual Clr type, then call ChangeTypeXsltArgument
  3587.                     if (xmlTypeFormalArg.TypeCode == XmlTypeCode.Item || !clrTypeFormalArg.IsAssignableFrom(clrTypeActualArg)) {
  3588.                         // (clrTypeFormalArg) runtime.ChangeTypeXsltArgument(xmlTypeFormalArg, (object) value, clrTypeFormalArg);
  3589.                         this.helper.LoadQueryRuntime();
  3590.                         this.helper.LoadInteger(this.helper.StaticData.DeclareXmlType(xmlTypeFormalArg));
  3591.                         NestedVisitEnsureStack(ndActualArg, GetItemStorageType(xmlTypeFormalArg), !xmlTypeFormalArg.IsSingleton);
  3592.                         this.helper.TreatAs(clrTypeActualArg, typeof(object));
  3593.                         this.helper.LoadType(clrTypeFormalArg);
  3594.                         this.helper.Call(XmlILMethods.ChangeTypeXsltArg);
  3595.                         this.helper.TreatAs(typeof(object), clrTypeFormalArg);
  3596.                     }
  3597.                     else {
  3598.                         NestedVisitEnsureStack(ndActualArg, GetItemStorageType(xmlTypeFormalArg), !xmlTypeFormalArg.IsSingleton);
  3599.                     }
  3600.                 }
  3601.             }
  3602.            
  3603.             // Invoke the target method
  3604.             this.helper.Call(extFunc.Method);
  3605.            
  3606.             // Return value is on the stack; convert it to canonical ILGen storage type
  3607.             if (ndInvoke.XmlType.IsEmpty) {
  3608.                 this.helper.Emit(OpCodes.Ldsfld, XmlILMethods.StorageMethods[typeof(XPathItem)].SeqEmpty);
  3609.             }
  3610.             else if (clrTypeRetSrc != clrTypeRetDst) {
  3611.                 // (T) runtime.ChangeTypeXsltResult(idxType, (object) value);
  3612.                 this.helper.TreatAs(clrTypeRetSrc, typeof(object));
  3613.                 this.helper.Call(XmlILMethods.ChangeTypeXsltResult);
  3614.                 this.helper.TreatAs(typeof(object), clrTypeRetDst);
  3615.             }
  3616.             else if (ndName.NamespaceUri.Length != 0 && !clrTypeRetSrc.IsValueType) {
  3617.                 // Check for null if a user-defined extension function returns a reference type
  3618.                 Label lblSkip = this.helper.DefineLabel();
  3619.                 this.helper.Emit(OpCodes.Dup);
  3620.                 this.helper.Emit(OpCodes.Brtrue, lblSkip);
  3621.                 this.helper.LoadQueryRuntime();
  3622.                 this.helper.Emit(OpCodes.Ldstr, Res.GetString(Res.Xslt_ItemNull));
  3623.                 this.helper.Call(XmlILMethods.ThrowException);
  3624.                 this.helper.MarkLabel(lblSkip);
  3625.             }
  3626.            
  3627.             this.iterCurr.Storage = StorageDescriptor.Stack(GetItemStorageType(ndInvoke), !ndInvoke.XmlType.IsSingleton);
  3628.            
  3629.             return ndInvoke;
  3630.         }
  3631.        
  3632.         /// <summary>
  3633.         /// Generate code for QilNodeType.XsltCopy.
  3634.         /// </summary>
  3635.         protected override QilNode VisitXsltCopy(QilBinary ndCopy)
  3636.         {
  3637.             Label lblSkipContent = this.helper.DefineLabel();
  3638.             Debug.Assert(XmlILConstructInfo.Read(ndCopy).PushToWriterFirst);
  3639.            
  3640.             // if (!xwrtChk.StartCopyChk(navCopy)) goto LabelSkipContent;
  3641.             this.helper.LoadQueryOutput();
  3642.            
  3643.             NestedVisitEnsureStack(ndCopy.Left);
  3644.             Debug.Assert(ndCopy.Left.XmlType.IsNode);
  3645.            
  3646.             this.helper.Call(XmlILMethods.StartCopy);
  3647.             this.helper.Emit(OpCodes.Brfalse, lblSkipContent);
  3648.            
  3649.             // Recursively construct content
  3650.             NestedVisit(ndCopy.Right);
  3651.            
  3652.             // xwrtChk.EndCopyChk(navCopy);
  3653.             this.helper.LoadQueryOutput();
  3654.            
  3655.             NestedVisitEnsureStack(ndCopy.Left);
  3656.             Debug.Assert(ndCopy.Left.XmlType.IsNode);
  3657.            
  3658.             this.helper.Call(XmlILMethods.EndCopy);
  3659.            
  3660.             // LabelSkipContent:
  3661.             this.helper.MarkLabel(lblSkipContent);
  3662.            
  3663.             this.iterCurr.Storage = StorageDescriptor.None();
  3664.             return ndCopy;
  3665.         }
  3666.        
  3667.         /// <summary>
  3668.         /// Generate code for QilNodeType.XsltCopyOf.
  3669.         /// </summary>
  3670.         protected override QilNode VisitXsltCopyOf(QilUnary ndCopyOf)
  3671.         {
  3672.             Debug.Assert(XmlILConstructInfo.Read(ndCopyOf).PushToWriterFirst, "XsltCopyOf should always be pushed to writer.");
  3673.            
  3674.             this.helper.LoadQueryOutput();
  3675.            
  3676.             // XmlQueryOutput.XsltCopyOf(navigator);
  3677.             NestedVisitEnsureStack(ndCopyOf.Child);
  3678.             this.helper.Call(XmlILMethods.CopyOf);
  3679.            
  3680.             this.iterCurr.Storage = StorageDescriptor.None();
  3681.             return ndCopyOf;
  3682.         }
  3683.        
  3684.         /// <summary>
  3685.         /// Generate code for QilNodeType.XsltConvert.
  3686.         /// </summary>
  3687.         protected override QilNode VisitXsltConvert(QilTargetType ndConv)
  3688.         {
  3689.             XmlQueryType typSrc;
  3690.             XmlQueryType typDst;
  3691.             MethodInfo meth;
  3692.            
  3693.             typSrc = ndConv.Source.XmlType;
  3694.             typDst = ndConv.TargetType;
  3695.            
  3696.             if (GetXsltConvertMethod(typSrc, typDst, out meth)) {
  3697.                 NestedVisitEnsureStack(ndConv.Source);
  3698.             }
  3699.             else {
  3700.                 // If a conversion could not be found, then convert the source expression to item or item* and try again
  3701.                 NestedVisitEnsureStack(ndConv.Source, typeof(XPathItem), !typSrc.IsSingleton);
  3702.                 if (!GetXsltConvertMethod(typSrc.IsSingleton ? TypeFactory.Item : TypeFactory.ItemS, typDst, out meth))
  3703.                     Debug.Fail("Conversion from " + ndConv.Source.XmlType + " to " + ndConv.TargetType + " is not supported.");
  3704.             }
  3705.            
  3706.             // XsltConvert.XXXToYYY(value);
  3707.             if (meth != null)
  3708.                 this.helper.Call(meth);
  3709.            
  3710.             this.iterCurr.Storage = StorageDescriptor.Stack(GetItemStorageType(typDst), !typDst.IsSingleton);
  3711.             return ndConv;
  3712.         }
  3713.        
  3714.         /// <summary>
  3715.         /// Get the XsltConvert method that converts from "typSrc" to "typDst". Return false if no
  3716.         /// such method exists. This conversion matrix should match the one in XsltConvert.ExternalValueToExternalValue.
  3717.         /// </summary>
  3718.         private bool GetXsltConvertMethod(XmlQueryType typSrc, XmlQueryType typDst, out MethodInfo meth)
  3719.         {
  3720.             meth = null;
  3721.            
  3722.             // Note, Ref.Equals is OK to use here, since we will always fall back to Item or Item* in the
  3723.             // case where the source or destination types do not match the static types exposed on the
  3724.             // XmlQueryTypeFactory. This is bad for perf if it accidentally occurs, but the results
  3725.             // should still be correct.
  3726.            
  3727.             // => xs:boolean
  3728.             if (Ref.Equals(typDst, TypeFactory.BooleanX)) {
  3729.                 if (Ref.Equals(typSrc, TypeFactory.Item))
  3730.                     meth = XmlILMethods.ItemToBool;
  3731.                 else if (Ref.Equals(typSrc, TypeFactory.ItemS))
  3732.                     meth = XmlILMethods.ItemsToBool;
  3733.             }
  3734.             // => xs:dateTime
  3735.             else if (Ref.Equals(typDst, TypeFactory.DateTimeX)) {
  3736.                 if (Ref.Equals(typSrc, TypeFactory.StringX))
  3737.                     meth = XmlILMethods.StrToDT;
  3738.             }
  3739.             // => xs:decimal
  3740.             else if (Ref.Equals(typDst, TypeFactory.DecimalX)) {
  3741.                 if (Ref.Equals(typSrc, TypeFactory.DoubleX))
  3742.                     meth = XmlILMethods.DblToDec;
  3743.             }
  3744.             // => xs:double
  3745.             else if (Ref.Equals(typDst, TypeFactory.DoubleX)) {
  3746.                 if (Ref.Equals(typSrc, TypeFactory.DecimalX))
  3747.                     meth = XmlILMethods.DecToDbl;
  3748.                 else if (Ref.Equals(typSrc, TypeFactory.IntX))
  3749.                     meth = XmlILMethods.IntToDbl;
  3750.                 else if (Ref.Equals(typSrc, TypeFactory.Item))
  3751.                     meth = XmlILMethods.ItemToDbl;
  3752.                 else if (Ref.Equals(typSrc, TypeFactory.ItemS))
  3753.                     meth = XmlILMethods.ItemsToDbl;
  3754.                 else if (Ref.Equals(typSrc, TypeFactory.LongX))
  3755.                     meth = XmlILMethods.LngToDbl;
  3756.                 else if (Ref.Equals(typSrc, TypeFactory.StringX))
  3757.                     meth = XmlILMethods.StrToDbl;
  3758.             }
  3759.             // => xs:int
  3760.             else if (Ref.Equals(typDst, TypeFactory.IntX)) {
  3761.                 if (Ref.Equals(typSrc, TypeFactory.DoubleX))
  3762.                     meth = XmlILMethods.DblToInt;
  3763.             }
  3764.             // => xs:long
  3765.             else if (Ref.Equals(typDst, TypeFactory.LongX)) {
  3766.                 if (Ref.Equals(typSrc, TypeFactory.DoubleX))
  3767.                     meth = XmlILMethods.DblToLng;
  3768.             }
  3769.             // => node
  3770.             else if (Ref.Equals(typDst, TypeFactory.NodeNotRtf)) {
  3771.                 if (Ref.Equals(typSrc, TypeFactory.Item))
  3772.                     meth = XmlILMethods.ItemToNode;
  3773.                 else if (Ref.Equals(typSrc, TypeFactory.ItemS))
  3774.                     meth = XmlILMethods.ItemsToNode;
  3775.             }
  3776.             // => node*
  3777.             else if (Ref.Equals(typDst, TypeFactory.NodeDodS) || Ref.Equals(typDst, TypeFactory.NodeNotRtfS)) {
  3778.                 if (Ref.Equals(typSrc, TypeFactory.Item))
  3779.                     meth = XmlILMethods.ItemToNodes;
  3780.                 else if (Ref.Equals(typSrc, TypeFactory.ItemS))
  3781.                     meth = XmlILMethods.ItemsToNodes;
  3782.             }
  3783.             // => xs:string
  3784.             else if (Ref.Equals(typDst, TypeFactory.StringX)) {
  3785.                 if (Ref.Equals(typSrc, TypeFactory.DateTimeX))
  3786.                     meth = XmlILMethods.DTToStr;
  3787.                 else if (Ref.Equals(typSrc, TypeFactory.DoubleX))
  3788.                     meth = XmlILMethods.DblToStr;
  3789.                 else if (Ref.Equals(typSrc, TypeFactory.Item))
  3790.                     meth = XmlILMethods.ItemToStr;
  3791.                 else if (Ref.Equals(typSrc, TypeFactory.ItemS))
  3792.                     meth = XmlILMethods.ItemsToStr;
  3793.             }
  3794.            
  3795.             return meth != null ? true : false;
  3796.         }
  3797.        
  3798.        
  3799.         //-----------------------------------------------
  3800.         // Helper methods
  3801.         //-----------------------------------------------
  3802.        
  3803.         /// <summary>
  3804.         /// Ensure that the "locNav" navigator is positioned to the context node "ndCtxt".
  3805.         /// </summary>
  3806.         private void SyncToNavigator(LocalBuilder locNav, QilNode ndCtxt)
  3807.         {
  3808.             this.helper.Emit(OpCodes.Ldloc, locNav);
  3809.             NestedVisitEnsureStack(ndCtxt);
  3810.             this.helper.CallSyncToNavigator();
  3811.             this.helper.Emit(OpCodes.Stloc, locNav);
  3812.         }
  3813.        
  3814.         /// <summary>
  3815.         /// Generate boiler-plate code to create a simple Xml iterator.
  3816.         /// </summary>
  3817.         /// <remarks>
  3818.         /// Iterator iter;
  3819.         /// iter.Create(navCtxt);
  3820.         /// LabelNext:
  3821.         /// if (!iter.MoveNext())
  3822.         /// goto LabelNextCtxt;
  3823.         /// </remarks>
  3824.         private void CreateSimpleIterator(QilNode ndCtxt, string iterName, Type iterType, MethodInfo methCreate, MethodInfo methNext)
  3825.         {
  3826.             // Iterator iter;
  3827.             LocalBuilder locIter = this.helper.DeclareLocal(iterName, iterType);
  3828.            
  3829.             // iter.Create(navCtxt);
  3830.             this.helper.Emit(OpCodes.Ldloca, locIter);
  3831.             NestedVisitEnsureStack(ndCtxt);
  3832.             this.helper.Call(methCreate);
  3833.            
  3834.             GenerateSimpleIterator(typeof(XPathNavigator), locIter, methNext);
  3835.         }
  3836.        
  3837.         /// <summary>
  3838.         /// Generate boiler-plate code to create an Xml iterator that uses an XmlNavigatorFilter to filter items.
  3839.         /// </summary>
  3840.         /// <remarks>
  3841.         /// Iterator iter;
  3842.         /// iter.Create(navCtxt, filter [, orSelf] [, navEnd]);
  3843.         /// LabelNext:
  3844.         /// if (!iter.MoveNext())
  3845.         /// goto LabelNextCtxt;
  3846.         /// </remarks>
  3847.         private void CreateFilteredIterator(QilNode ndCtxt, string iterName, Type iterType, MethodInfo methCreate, MethodInfo methNext, XmlNodeKindFlags kinds, QilName ndName, TriState orSelf, QilNode ndEnd)
  3848.         {
  3849.             // Iterator iter;
  3850.             LocalBuilder locIter = this.helper.DeclareLocal(iterName, iterType);
  3851.            
  3852.             // iter.Create(navCtxt, filter [, orSelf], [, navEnd]);
  3853.             this.helper.Emit(OpCodes.Ldloca, locIter);
  3854.             NestedVisitEnsureStack(ndCtxt);
  3855.             LoadSelectFilter(kinds, ndName);
  3856.             if (orSelf != TriState.Unknown)
  3857.                 this.helper.LoadBoolean(orSelf == TriState.True);
  3858.             if (ndEnd != null)
  3859.                 NestedVisitEnsureStack(ndEnd);
  3860.             this.helper.Call(methCreate);
  3861.            
  3862.             GenerateSimpleIterator(typeof(XPathNavigator), locIter, methNext);
  3863.         }
  3864.        
  3865.         /// <summary>
  3866.         /// Generate boiler-plate code to create an Xml iterator that controls a nested iterator.
  3867.         /// </summary>
  3868.         /// <remarks>
  3869.         /// Iterator iter;
  3870.         /// iter.Create(filter [, orSelf]);
  3871.         /// ...nested iterator...
  3872.         /// navInput = nestedNested;
  3873.         /// goto LabelCall;
  3874.         /// LabelNext:
  3875.         /// navInput = null;
  3876.         /// LabelCall:
  3877.         /// switch (iter.MoveNext(navInput)) {
  3878.         /// case IteratorState.NoMoreNodes: goto LabelNextCtxt;
  3879.         /// case IteratorState.NextInputNode: goto LabelNextNested;
  3880.         /// }
  3881.         /// </remarks>
  3882.         private void CreateContainerIterator(QilUnary ndDod, string iterName, Type iterType, MethodInfo methCreate, MethodInfo methNext, XmlNodeKindFlags kinds, QilName ndName, TriState orSelf)
  3883.         {
  3884.             // Iterator iter;
  3885.             LocalBuilder locIter = this.helper.DeclareLocal(iterName, iterType);
  3886.             Label lblOnEndNested;
  3887.             QilLoop ndLoop = (QilLoop)ndDod.Child;
  3888.             Debug.Assert(ndDod.NodeType == QilNodeType.DocOrderDistinct && ndLoop != null);
  3889.            
  3890.             // iter.Create(filter [, orSelf]);
  3891.             this.helper.Emit(OpCodes.Ldloca, locIter);
  3892.             LoadSelectFilter(kinds, ndName);
  3893.             if (orSelf != TriState.Unknown)
  3894.                 this.helper.LoadBoolean(orSelf == TriState.True);
  3895.             this.helper.Call(methCreate);
  3896.            
  3897.             // Generate nested iterator (branch to lblOnEndNested when iteration is complete)
  3898.             lblOnEndNested = this.helper.DefineLabel();
  3899.             StartNestedIterator(ndLoop, lblOnEndNested);
  3900.             StartBinding(ndLoop.Variable);
  3901.             EndBinding(ndLoop.Variable);
  3902.             EndNestedIterator(ndLoop.Variable);
  3903.             this.iterCurr.Storage = this.iterNested.Storage;
  3904.            
  3905.             GenerateContainerIterator(ndDod, locIter, lblOnEndNested, methNext, typeof(XPathNavigator));
  3906.         }
  3907.        
  3908.         /// <summary>
  3909.         /// Generate boiler-plate code that calls MoveNext on a simple Xml iterator. Iterator should have already been
  3910.         /// created by calling code.
  3911.         /// </summary>
  3912.         /// <remarks>
  3913.         /// ...
  3914.         /// LabelNext:
  3915.         /// if (!iter.MoveNext())
  3916.         /// goto LabelNextCtxt;
  3917.         /// </remarks>
  3918.         private void GenerateSimpleIterator(Type itemStorageType, LocalBuilder locIter, MethodInfo methNext)
  3919.         {
  3920.             Label lblNext;
  3921.            
  3922.             // LabelNext:
  3923.             lblNext = this.helper.DefineLabel();
  3924.             this.helper.MarkLabel(lblNext);
  3925.            
  3926.             // if (!iter.MoveNext()) goto LabelNextCtxt;
  3927.             this.helper.Emit(OpCodes.Ldloca, locIter);
  3928.             this.helper.Call(methNext);
  3929.             this.helper.Emit(OpCodes.Brfalse, this.iterCurr.GetLabelNext());
  3930.            
  3931.             this.iterCurr.SetIterator(lblNext, StorageDescriptor.Current(locIter, itemStorageType));
  3932.         }
  3933.        
  3934.         /// <summary>
  3935.         /// Generate boiler-plate code that calls MoveNext on an Xml iterator that controls a nested iterator. Iterator should
  3936.         /// have already been created by calling code.
  3937.         /// </summary>
  3938.         /// <remarks>
  3939.         /// ...
  3940.         /// goto LabelCall;
  3941.         /// LabelNext:
  3942.         /// navCtxt = null;
  3943.         /// LabelCall:
  3944.         /// switch (iter.MoveNext(navCtxt)) {
  3945.         /// case IteratorState.NoMoreNodes: goto LabelNextCtxt;
  3946.         /// case IteratorState.NextInputNode: goto LabelNextNested;
  3947.         /// }
  3948.         /// </remarks>
  3949.         private void GenerateContainerIterator(QilNode nd, LocalBuilder locIter, Label lblOnEndNested, MethodInfo methNext, Type itemStorageType)
  3950.         {
  3951.             Label lblCall;
  3952.            
  3953.             // Define labels that will be used
  3954.             lblCall = this.helper.DefineLabel();
  3955.            
  3956.             // iter.MoveNext(input);
  3957.             // goto LabelCall;
  3958.             this.iterCurr.EnsureNoStackNoCache(nd.XmlType.IsNode ? "$$$navInput" : "$$$itemInput");
  3959.             this.helper.Emit(OpCodes.Ldloca, locIter);
  3960.             this.iterCurr.PushValue();
  3961.             this.helper.EmitUnconditionalBranch(OpCodes.Br, lblCall);
  3962.            
  3963.             // LabelNext:
  3964.             // iterSet.MoveNext(null);
  3965.             this.helper.MarkLabel(lblOnEndNested);
  3966.             this.helper.Emit(OpCodes.Ldloca, locIter);
  3967.             this.helper.Emit(OpCodes.Ldnull);
  3968.            
  3969.             // LabelCall:
  3970.             // result = iter.MoveNext(input);
  3971.             this.helper.MarkLabel(lblCall);
  3972.             this.helper.Call(methNext);
  3973.            
  3974.             // If this iterator always returns a single node, then NoMoreNodes will never be returned
  3975.             if (nd.XmlType.IsSingleton) {
  3976.                 // if (result == IteratorResult.NeedInputNode) goto LabelNextInput;
  3977.                 this.helper.LoadInteger((int)IteratorResult.NeedInputNode);
  3978.                 this.helper.Emit(OpCodes.Beq, this.iterNested.GetLabelNext());
  3979.                
  3980.                 this.iterCurr.Storage = StorageDescriptor.Current(locIter, itemStorageType);
  3981.             }
  3982.             else {
  3983.                 // switch (iter.MoveNext(input)) {
  3984.                 // case IteratorResult.NoMoreNodes: goto LabelNextCtxt;
  3985.                 // case IteratorResult.NeedInputNode: goto LabelNextInput;
  3986.                 // }
  3987.                 this.helper.Emit(OpCodes.Switch, new Label[] {this.iterCurr.GetLabelNext(), this.iterNested.GetLabelNext()});
  3988.                
  3989.                 this.iterCurr.SetIterator(lblOnEndNested, StorageDescriptor.Current(locIter, itemStorageType));
  3990.             }
  3991.         }
  3992.        
  3993.         /// <summary>
  3994.         /// Load XmlQueryOutput, load a name (computed or literal) and load an index to an Xml schema type.
  3995.         /// Return an enumeration that specifies what kind of name was loaded.
  3996.         /// </summary>
  3997.         private GenerateNameType LoadNameAndType(XPathNodeType nodeType, QilNode ndName, bool isStart, bool callChk)
  3998.         {
  3999.             QilName ndLiteralName;
  4000.             string prefix;
  4001.             string localName;
  4002.             string ns;
  4003.             GenerateNameType nameType;
  4004.             Debug.Assert(ndName.XmlType.TypeCode == XmlTypeCode.QName, "Element or attribute name must have QName type.");
  4005.            
  4006.             this.helper.LoadQueryOutput();
  4007.            
  4008.             // 0. Default is to pop names off stack
  4009.             nameType = GenerateNameType.StackName;
  4010.            
  4011.             // 1. Literal names
  4012.             if (ndName.NodeType == QilNodeType.LiteralQName) {
  4013.                 // If checks need to be made on End construction, then always pop names from stack
  4014.                 if (isStart || !callChk) {
  4015.                     ndLiteralName = ndName as QilName;
  4016.                     prefix = ndLiteralName.Prefix;
  4017.                     localName = ndLiteralName.LocalName;
  4018.                     ns = ndLiteralName.NamespaceUri;
  4019.                    
  4020.                     // Check local name, namespace parts in debug code
  4021.                     Debug.Assert(ValidateNames.ValidateName(prefix, localName, ns, nodeType, ValidateNames.Flags.AllExceptPrefixMapping));
  4022.                    
  4023.                     // If the namespace is empty,
  4024.                     if (ndLiteralName.NamespaceUri.Length == 0) {
  4025.                         // Then always call method on XmlQueryOutput
  4026.                         this.helper.Emit(OpCodes.Ldstr, ndLiteralName.LocalName);
  4027.                         return GenerateNameType.LiteralLocalName;
  4028.                     }
  4029.                    
  4030.                     // If prefix is not valid for the node type,
  4031.                     if (!ValidateNames.ValidateName(prefix, localName, ns, nodeType, ValidateNames.Flags.CheckPrefixMapping)) {
  4032.                         // Then construct a new prefix at run-time
  4033.                         if (isStart) {
  4034.                             this.helper.Emit(OpCodes.Ldstr, localName);
  4035.                             this.helper.Emit(OpCodes.Ldstr, ns);
  4036.                             this.helper.Construct(XmlILConstructors.QName);
  4037.                            
  4038.                             nameType = GenerateNameType.QName;
  4039.                         }
  4040.                     }
  4041.                     else {
  4042.                         // Push string parts
  4043.                         this.helper.Emit(OpCodes.Ldstr, prefix);
  4044.                         this.helper.Emit(OpCodes.Ldstr, localName);
  4045.                         this.helper.Emit(OpCodes.Ldstr, ns);
  4046.                        
  4047.                         nameType = GenerateNameType.LiteralName;
  4048.                     }
  4049.                 }
  4050.             }
  4051.             else {
  4052.                 if (isStart) {
  4053.                     // 2. Copied names
  4054.                     if (ndName.NodeType == QilNodeType.NameOf) {
  4055.                         // Preserve prefix of source node, so just push navigator onto stack
  4056.                         NestedVisitEnsureStack((ndName as QilUnary).Child);
  4057.                         nameType = GenerateNameType.CopiedName;
  4058.                     }
  4059.                     // 3. Parsed tag names (foo:bar)
  4060.                     else if (ndName.NodeType == QilNodeType.StrParseQName) {
  4061.                         // Preserve prefix from parsed tag name
  4062.                         VisitStrParseQName(ndName as QilBinary, true);
  4063.                        
  4064.                         // Type of name depends upon data-type of name argument
  4065.                         if ((ndName as QilBinary).Right.XmlType.TypeCode == XmlTypeCode.String)
  4066.                             nameType = GenerateNameType.TagNameAndNamespace;
  4067.                         else
  4068.                             nameType = GenerateNameType.TagNameAndMappings;
  4069.                     }
  4070.                     // 4. Other computed qnames
  4071.                     else {
  4072.                         // Push XmlQualifiedName onto the stack
  4073.                         NestedVisitEnsureStack(ndName);
  4074.                         nameType = GenerateNameType.QName;
  4075.                     }
  4076.                 }
  4077.             }
  4078.            
  4079.             return nameType;
  4080.         }
  4081.        
  4082.         /// <summary>
  4083.         /// If the first argument is a constant value that evaluates to zero, then a more optimal instruction sequence
  4084.         /// can be generated that does not have to push the zero onto the stack. Instead, a Brfalse or Brtrue instruction
  4085.         /// can be used.
  4086.         /// </summary>
  4087.         private bool TryZeroCompare(QilNodeType relOp, QilNode ndFirst, QilNode ndSecond)
  4088.         {
  4089.             Debug.Assert(relOp == QilNodeType.Eq || relOp == QilNodeType.Ne);
  4090.            
  4091.             switch (ndFirst.NodeType) {
  4092.                 case QilNodeType.LiteralInt64:
  4093.                     if ((int)(QilLiteral)ndFirst != 0)
  4094.                         return false;
  4095.                     break;
  4096.                 case QilNodeType.LiteralInt32:
  4097.                    
  4098.                     if ((int)(QilLiteral)ndFirst != 0)
  4099.                         return false;
  4100.                     break;
  4101.                 case QilNodeType.False:
  4102.                    
  4103.                     break;
  4104.                 case QilNodeType.True:
  4105.                    
  4106.                     // Inverse of QilNodeType.False
  4107.                     relOp = (relOp == QilNodeType.Eq) ? QilNodeType.Ne : QilNodeType.Eq;
  4108.                     break;
  4109.                 default:
  4110.                    
  4111.                     return false;
  4112.             }
  4113.            
  4114.             // Generate code to push second argument on stack
  4115.             NestedVisitEnsureStack(ndSecond);
  4116.            
  4117.             // Generate comparison code -- op == 0 or op != 0
  4118.             ZeroCompare(relOp, ndSecond.XmlType.TypeCode == XmlTypeCode.Boolean);
  4119.            
  4120.             return true;
  4121.         }
  4122.        
  4123.         /// <summary>
  4124.         /// If the comparison involves a qname, then perform comparison using atoms and return true.
  4125.         /// Otherwise, return false (caller will perform comparison).
  4126.         /// </summary>
  4127.         private bool TryNameCompare(QilNodeType relOp, QilNode ndFirst, QilNode ndSecond)
  4128.         {
  4129.             Debug.Assert(relOp == QilNodeType.Eq || relOp == QilNodeType.Ne);
  4130.            
  4131.             if (ndFirst.NodeType == QilNodeType.NameOf) {
  4132.                 switch (ndSecond.NodeType) {
  4133.                     case QilNodeType.NameOf:
  4134.                     case QilNodeType.LiteralQName:
  4135.                        
  4136.                         {
  4137.                             this.helper.LoadQueryRuntime();
  4138.                            
  4139.                             // Push left navigator onto the stack
  4140.                             NestedVisitEnsureStack((ndFirst as QilUnary).Child);
  4141.                            
  4142.                             // Push the local name and namespace uri of the right argument onto the stack
  4143.                             if (ndSecond.NodeType == QilNodeType.LiteralQName) {
  4144.                                 QilName ndName = ndSecond as QilName;
  4145.                                 this.helper.LoadInteger(this.helper.StaticData.DeclareName(ndName.LocalName));
  4146.                                 this.helper.LoadInteger(this.helper.StaticData.DeclareName(ndName.NamespaceUri));
  4147.                                
  4148.                                 // push runtime.IsQNameEqual(navigator, localName, namespaceUri)
  4149.                                 this.helper.Call(XmlILMethods.QNameEqualLit);
  4150.                             }
  4151.                             else {
  4152.                                 // Generate code to locate the navigator argument of NameOf operator
  4153.                                 Debug.Assert(ndSecond.NodeType == QilNodeType.NameOf);
  4154.                                 NestedVisitEnsureStack(ndSecond);
  4155.                                
  4156.                                 // push runtime.IsQNameEqual(nav1, nav2)
  4157.                                 this.helper.Call(XmlILMethods.QNameEqualNav);
  4158.                             }
  4159.                            
  4160.                             // Branch based on boolean result or push boolean value
  4161.                             ZeroCompare((relOp == QilNodeType.Eq) ? QilNodeType.Ne : QilNodeType.Eq, true);
  4162.                             return true;
  4163.                         }
  4164.                         break;
  4165.                 }
  4166.             }
  4167.            
  4168.             // Caller must perform comparison
  4169.             return false;
  4170.         }
  4171.        
  4172.         /// <summary>
  4173.         /// For QilExpression types that map directly to CLR primitive types, the built-in CLR comparison operators can
  4174.         /// be used to perform the specified relational operation.
  4175.         /// </summary>
  4176.         private void ClrCompare(QilNodeType relOp, XmlTypeCode code)
  4177.         {
  4178.             OpCode opcode;
  4179.             Label lblTrue;
  4180.            
  4181.             switch (this.iterCurr.CurrentBranchingContext) {
  4182.                 case BranchingContext.OnFalse:
  4183.                     // Reverse the comparison operator
  4184.                     // Use Bxx_Un OpCodes to handle NaN case for double and single types
  4185.                     if (code == XmlTypeCode.Double || code == XmlTypeCode.Float) {
  4186.                         switch (relOp) {
  4187.                             case QilNodeType.Gt:
  4188.                                 opcode = OpCodes.Ble_Un;
  4189.                                 break;
  4190.                             case QilNodeType.Ge:
  4191.                                 opcode = OpCodes.Blt_Un;
  4192.                                 break;
  4193.                             case QilNodeType.Lt:
  4194.                                 opcode = OpCodes.Bge_Un;
  4195.                                 break;
  4196.                             case QilNodeType.Le:
  4197.                                 opcode = OpCodes.Bgt_Un;
  4198.                                 break;
  4199.                             case QilNodeType.Eq:
  4200.                                 opcode = OpCodes.Bne_Un;
  4201.                                 break;
  4202.                             case QilNodeType.Ne:
  4203.                                 opcode = OpCodes.Beq;
  4204.                                 break;
  4205.                             default:
  4206.                                 Debug.Assert(false);
  4207.                                 opcode = OpCodes.Nop;
  4208.                                 break;
  4209.                         }
  4210.                     }
  4211.                     else {
  4212.                         switch (relOp) {
  4213.                             case QilNodeType.Gt:
  4214.                                 opcode = OpCodes.Ble;
  4215.                                 break;
  4216.                             case QilNodeType.Ge:
  4217.                                 opcode = OpCodes.Blt;
  4218.                                 break;
  4219.                             case QilNodeType.Lt:
  4220.                                 opcode = OpCodes.Bge;
  4221.                                 break;
  4222.                             case QilNodeType.Le:
  4223.                                 opcode = OpCodes.Bgt;
  4224.                                 break;
  4225.                             case QilNodeType.Eq:
  4226.                                 opcode = OpCodes.Bne_Un;
  4227.                                 break;
  4228.                             case QilNodeType.Ne:
  4229.                                 opcode = OpCodes.Beq;
  4230.                                 break;
  4231.                             default:
  4232.                                 Debug.Assert(false);
  4233.                                 opcode = OpCodes.Nop;
  4234.                                 break;
  4235.                         }
  4236.                     }
  4237.                     this.helper.Emit(opcode, this.iterCurr.LabelBranch);
  4238.                     this.iterCurr.Storage = StorageDescriptor.None();
  4239.                     break;
  4240.                 case BranchingContext.OnTrue:
  4241.                    
  4242.                     switch (relOp) {
  4243.                         case QilNodeType.Gt:
  4244.                             opcode = OpCodes.Bgt;
  4245.                             break;
  4246.                         case QilNodeType.Ge:
  4247.                             opcode = OpCodes.Bge;
  4248.                             break;
  4249.                         case QilNodeType.Lt:
  4250.                             opcode = OpCodes.Blt;
  4251.                             break;
  4252.                         case QilNodeType.Le:
  4253.                             opcode = OpCodes.Ble;
  4254.                             break;
  4255.                         case QilNodeType.Eq:
  4256.                             opcode = OpCodes.Beq;
  4257.                             break;
  4258.                         case QilNodeType.Ne:
  4259.                             opcode = OpCodes.Bne_Un;
  4260.                             break;
  4261.                         default:
  4262.                             Debug.Assert(false);
  4263.                             opcode = OpCodes.Nop;
  4264.                             break;
  4265.                     }
  4266.                     this.helper.Emit(opcode, this.iterCurr.LabelBranch);
  4267.                     this.iterCurr.Storage = StorageDescriptor.None();
  4268.                     break;
  4269.                 default:
  4270.                    
  4271.                     Debug.Assert(this.iterCurr.CurrentBranchingContext == BranchingContext.None);
  4272.                     switch (relOp) {
  4273.                         case QilNodeType.Gt:
  4274.                             this.helper.Emit(OpCodes.Cgt);
  4275.                             break;
  4276.                         case QilNodeType.Lt:
  4277.                             this.helper.Emit(OpCodes.Clt);
  4278.                             break;
  4279.                         case QilNodeType.Eq:
  4280.                             this.helper.Emit(OpCodes.Ceq);
  4281.                             break;
  4282.                         default:
  4283.                             switch (relOp) {
  4284.                                 case QilNodeType.Ge:
  4285.                                     opcode = OpCodes.Bge_S;
  4286.                                     break;
  4287.                                 case QilNodeType.Le:
  4288.                                     opcode = OpCodes.Ble_S;
  4289.                                     break;
  4290.                                 case QilNodeType.Ne:
  4291.                                     opcode = OpCodes.Bne_Un_S;
  4292.                                     break;
  4293.                                 default:
  4294.                                     Debug.Assert(false);
  4295.                                     opcode = OpCodes.Nop;
  4296.                                     break;
  4297.                             }
  4298.                            
  4299.                             // Push "true" if comparison succeeds, "false" otherwise
  4300.                             lblTrue = this.helper.DefineLabel();
  4301.                             this.helper.Emit(opcode, lblTrue);
  4302.                             this.helper.ConvBranchToBool(lblTrue, true);
  4303.                             break;
  4304.                     }
  4305.                     this.iterCurr.Storage = StorageDescriptor.Stack(typeof(bool), false);
  4306.                     break;
  4307.             }
  4308.         }
  4309.        
  4310.         /// <summary>
  4311.         /// Generate code to compare the top stack value to 0 by using the Brfalse or Brtrue instructions,
  4312.         /// which avoid pushing zero onto the stack. Both of these instructions test for null/zero/false.
  4313.         /// </summary>
  4314.         private void ZeroCompare(QilNodeType relOp, bool isBoolVal)
  4315.         {
  4316.             Label lblTrue;
  4317.             Debug.Assert(relOp == QilNodeType.Eq || relOp == QilNodeType.Ne);
  4318.            
  4319.             // Test to determine if top stack value is zero (if relOp is Eq) or is not zero (if relOp is Ne)
  4320.             switch (this.iterCurr.CurrentBranchingContext) {
  4321.                 case BranchingContext.OnTrue:
  4322.                     // If relOp is Eq, jump to true label if top value is zero (Brfalse)
  4323.                     // If relOp is Ne, jump to true label if top value is non-zero (Brtrue)
  4324.                     this.helper.Emit((relOp == QilNodeType.Eq) ? OpCodes.Brfalse : OpCodes.Brtrue, this.iterCurr.LabelBranch);
  4325.                     this.iterCurr.Storage = StorageDescriptor.None();
  4326.                     break;
  4327.                 case BranchingContext.OnFalse:
  4328.                    
  4329.                     // If relOp is Eq, jump to false label if top value is non-zero (Brtrue)
  4330.                     // If relOp is Ne, jump to false label if top value is zero (Brfalse)
  4331.                     this.helper.Emit((relOp == QilNodeType.Eq) ? OpCodes.Brtrue : OpCodes.Brfalse, this.iterCurr.LabelBranch);
  4332.                     this.iterCurr.Storage = StorageDescriptor.None();
  4333.                     break;
  4334.                 default:
  4335.                    
  4336.                     Debug.Assert(this.iterCurr.CurrentBranchingContext == BranchingContext.None);
  4337.                    
  4338.                     // Since (boolval != 0) = boolval, value on top of the stack is already correct
  4339.                     if (!isBoolVal || relOp == QilNodeType.Eq) {
  4340.                         // If relOp is Eq, push "true" if top value is zero, "false" otherwise
  4341.                         // If relOp is Ne, push "true" if top value is non-zero, "false" otherwise
  4342.                         lblTrue = this.helper.DefineLabel();
  4343.                         this.helper.Emit((relOp == QilNodeType.Eq) ? OpCodes.Brfalse : OpCodes.Brtrue, lblTrue);
  4344.                         this.helper.ConvBranchToBool(lblTrue, true);
  4345.                     }
  4346.                    
  4347.                     this.iterCurr.Storage = StorageDescriptor.Stack(typeof(bool), false);
  4348.                     break;
  4349.             }
  4350.         }
  4351.        
  4352.         /// <summary>
  4353.         /// Construction within a loop is starting. If transition from non-Any to Any state occurs, then ensure
  4354.         /// that runtime state will be set.
  4355.         /// </summary>
  4356.         private void StartWriterLoop(QilNode nd, out bool hasOnEnd, out Label lblOnEnd)
  4357.         {
  4358.             XmlILConstructInfo info = XmlILConstructInfo.Read(nd);
  4359.            
  4360.             // By default, do not create a new iteration label
  4361.             hasOnEnd = false;
  4362.             lblOnEnd = new Label();
  4363.            
  4364.             // If loop is not involved in Xml construction, or if loop returns exactly one value, then do nothing
  4365.             if (!info.PushToWriterLast || nd.XmlType.IsSingleton)
  4366.                 return;
  4367.            
  4368.             if (!this.iterCurr.HasLabelNext) {
  4369.                 // Iterate until all items are constructed
  4370.                 hasOnEnd = true;
  4371.                 lblOnEnd = this.helper.DefineLabel();
  4372.                 this.iterCurr.SetIterator(lblOnEnd, StorageDescriptor.None());
  4373.             }
  4374.         }
  4375.        
  4376.         /// <summary>
  4377.         /// Construction within a loop is ending. If transition from non-Any to Any state occurs, then ensure that
  4378.         /// runtime state will be set.
  4379.         /// </summary>
  4380.         private void EndWriterLoop(QilNode nd, bool hasOnEnd, Label lblOnEnd)
  4381.         {
  4382.             XmlILConstructInfo info = XmlILConstructInfo.Read(nd);
  4383.            
  4384.             // If loop is not involved in Xml construction, then do nothing
  4385.             if (!info.PushToWriterLast)
  4386.                 return;
  4387.            
  4388.             // Since results of construction were pushed to writer, there are no values to return
  4389.             this.iterCurr.Storage = StorageDescriptor.None();
  4390.            
  4391.             // If loop returns exactly one value, then do nothing further
  4392.             if (nd.XmlType.IsSingleton)
  4393.                 return;
  4394.            
  4395.             if (hasOnEnd) {
  4396.                 // Loop over all items in the list, sending each to the output writer
  4397.                 this.iterCurr.LoopToEnd(lblOnEnd);
  4398.             }
  4399.         }
  4400.        
  4401.         /// <summary>
  4402.         /// Returns true if the specified node's owner element might have local namespaces added to it
  4403.         /// after attributes have already been added.
  4404.         /// </summary>
  4405.         private bool MightHaveNamespacesAfterAttributes(XmlILConstructInfo info)
  4406.         {
  4407.             // Get parent element
  4408.             if (info != null)
  4409.                 info = info.ParentElementInfo;
  4410.            
  4411.             // If a parent element has not been statically identified, then assume that the runtime
  4412.             // element will have namespaces added after attributes.
  4413.             if (info == null)
  4414.                 return true;
  4415.            
  4416.             return info.MightHaveNamespacesAfterAttributes;
  4417.         }
  4418.        
  4419.         /// <summary>
  4420.         /// Returns true if the specified element should cache attributes.
  4421.         /// </summary>
  4422.         private bool ElementCachesAttributes(XmlILConstructInfo info)
  4423.         {
  4424.             // Attributes will be cached if namespaces might be constructed after the attributes
  4425.             return info.MightHaveDuplicateAttributes || info.MightHaveNamespacesAfterAttributes;
  4426.         }
  4427.        
  4428.         /// <summary>
  4429.         /// This method is called before calling any WriteEnd??? method. It generates code to perform runtime
  4430.         /// construction checks separately. This should only be called if the XmlQueryOutput::StartElementChk
  4431.         /// method will *not* be called.
  4432.         /// </summary>
  4433.         private void BeforeStartChecks(QilNode ndCtor)
  4434.         {
  4435.             switch (XmlILConstructInfo.Read(ndCtor).InitialStates) {
  4436.                 case PossibleXmlStates.WithinSequence:
  4437.                     // If runtime state is guaranteed to be WithinSequence, then call XmlQueryOutput.StartTree
  4438.                     this.helper.CallStartTree(QilConstructorToNodeType(ndCtor.NodeType));
  4439.                     break;
  4440.                 case PossibleXmlStates.EnumAttrs:
  4441.                    
  4442.                     switch (ndCtor.NodeType) {
  4443.                         case QilNodeType.ElementCtor:
  4444.                         case QilNodeType.TextCtor:
  4445.                         case QilNodeType.RawTextCtor:
  4446.                         case QilNodeType.PICtor:
  4447.                         case QilNodeType.CommentCtor:
  4448.                             // If runtime state is guaranteed to be EnumAttrs, and content is being constructed, call
  4449.                             // XmlQueryOutput.StartElementContent
  4450.                             this.helper.CallStartElementContent();
  4451.                             break;
  4452.                     }
  4453.                     break;
  4454.             }
  4455.         }
  4456.        
  4457.         /// <summary>
  4458.         /// This method is called after calling any WriteEnd??? method. It generates code to perform runtime
  4459.         /// construction checks separately. This should only be called if the XmlQueryOutput::EndElementChk
  4460.         /// method will *not* be called.
  4461.         /// </summary>
  4462.         private void AfterEndChecks(QilNode ndCtor)
  4463.         {
  4464.             if (XmlILConstructInfo.Read(ndCtor).FinalStates == PossibleXmlStates.WithinSequence) {
  4465.                 // If final runtime state is guaranteed to be WithinSequence, then call XmlQueryOutput.StartTree
  4466.                 this.helper.CallEndTree();
  4467.             }
  4468.         }
  4469.        
  4470.         /// <summary>
  4471.         /// Return true if a runtime check needs to be made in order to transition into the WithinContent state.
  4472.         /// </summary>
  4473.         private bool CheckWithinContent(XmlILConstructInfo info)
  4474.         {
  4475.             switch (info.InitialStates) {
  4476.                 case PossibleXmlStates.WithinSequence:
  4477.                 case PossibleXmlStates.EnumAttrs:
  4478.                 case PossibleXmlStates.WithinContent:
  4479.                     // Transition to WithinContent can be ensured at compile-time
  4480.                     return false;
  4481.             }
  4482.            
  4483.             return true;
  4484.         }
  4485.        
  4486.         /// <summary>
  4487.         /// Return true if a runtime check needs to be made in order to transition into the EnumAttrs state.
  4488.         /// </summary>
  4489.         private bool CheckEnumAttrs(XmlILConstructInfo info)
  4490.         {
  4491.             switch (info.InitialStates) {
  4492.                 case PossibleXmlStates.WithinSequence:
  4493.                 case PossibleXmlStates.EnumAttrs:
  4494.                     // Transition to EnumAttrs can be ensured at compile-time
  4495.                     return false;
  4496.             }
  4497.            
  4498.             return true;
  4499.         }
  4500.        
  4501.         /// <summary>
  4502.         /// Map the XmlNodeKindFlags enumeration into the XPathNodeType enumeration.
  4503.         /// </summary>
  4504.         private XPathNodeType QilXmlToXPathNodeType(XmlNodeKindFlags xmlTypes)
  4505.         {
  4506.             switch (xmlTypes) {
  4507.                 case XmlNodeKindFlags.Element:
  4508.                     return XPathNodeType.Element;
  4509.                 case XmlNodeKindFlags.Attribute:
  4510.                     return XPathNodeType.Attribute;
  4511.                 case XmlNodeKindFlags.Text:
  4512.                     return XPathNodeType.Text;
  4513.                 case XmlNodeKindFlags.Comment:
  4514.                     return XPathNodeType.Comment;
  4515.             }
  4516.             Debug.Assert(xmlTypes == XmlNodeKindFlags.PI);
  4517.             return XPathNodeType.ProcessingInstruction;
  4518.         }
  4519.        
  4520.         /// <summary>
  4521.         /// Map a QilExpression constructor type into the XPathNodeType enumeration.
  4522.         /// </summary>
  4523.         private XPathNodeType QilConstructorToNodeType(QilNodeType typ)
  4524.         {
  4525.             switch (typ) {
  4526.                 case QilNodeType.DocumentCtor:
  4527.                     return XPathNodeType.Root;
  4528.                 case QilNodeType.ElementCtor:
  4529.                     return XPathNodeType.Element;
  4530.                 case QilNodeType.TextCtor:
  4531.                     return XPathNodeType.Text;
  4532.                 case QilNodeType.RawTextCtor:
  4533.                     return XPathNodeType.Text;
  4534.                 case QilNodeType.PICtor:
  4535.                     return XPathNodeType.ProcessingInstruction;
  4536.                 case QilNodeType.CommentCtor:
  4537.                     return XPathNodeType.Comment;
  4538.                 case QilNodeType.AttributeCtor:
  4539.                     return XPathNodeType.Attribute;
  4540.                 case QilNodeType.NamespaceDecl:
  4541.                     return XPathNodeType.Namespace;
  4542.             }
  4543.            
  4544.             Debug.Assert(false, "Cannot map QilNodeType " + typ + " to an XPathNodeType");
  4545.             return XPathNodeType.All;
  4546.         }
  4547.        
  4548.         /// <summary>
  4549.         /// Load an XmlNavigatorFilter that matches only the specified name and types onto the stack.
  4550.         /// </summary>
  4551.         private void LoadSelectFilter(XmlNodeKindFlags xmlTypes, QilName ndName)
  4552.         {
  4553.             if (ndName != null) {
  4554.                 // Push NameFilter
  4555.                 Debug.Assert(xmlTypes == XmlNodeKindFlags.Element);
  4556.                 this.helper.CallGetNameFilter(this.helper.StaticData.DeclareNameFilter(ndName.LocalName, ndName.NamespaceUri));
  4557.             }
  4558.             else {
  4559.                 // Either type cannot be a union, or else it must be >= union of all Content types
  4560.                 bool isXmlTypeUnion = IsNodeTypeUnion(xmlTypes);
  4561.                 Debug.Assert(!isXmlTypeUnion || (xmlTypes & XmlNodeKindFlags.Content) == XmlNodeKindFlags.Content);
  4562.                
  4563.                 if (isXmlTypeUnion) {
  4564.                     if ((xmlTypes & XmlNodeKindFlags.Attribute) != 0) {
  4565.                         // Union includes attributes, so allow all node kinds
  4566.                         this.helper.CallGetTypeFilter(XPathNodeType.All);
  4567.                     }
  4568.                     else {
  4569.                         // Filter attributes
  4570.                         this.helper.CallGetTypeFilter(XPathNodeType.Attribute);
  4571.                     }
  4572.                 }
  4573.                 else {
  4574.                     // Filter nodes of all but one type
  4575.                     this.helper.CallGetTypeFilter(QilXmlToXPathNodeType(xmlTypes));
  4576.                 }
  4577.             }
  4578.         }
  4579.        
  4580.         /// <summary>
  4581.         /// Return true if more than one node type is set.
  4582.         /// </summary>
  4583.         private static bool IsNodeTypeUnion(XmlNodeKindFlags xmlTypes)
  4584.         {
  4585.             return ((int)xmlTypes & ((int)xmlTypes - 1)) != 0;
  4586.         }
  4587.        
  4588.         /// <summary>
  4589.         /// Start construction of a new nested iterator. If this.iterCurr == null, then the new iterator
  4590.         /// is a top-level, or root iterator. Otherwise, the new iterator will be nested within the
  4591.         /// current iterator.
  4592.         /// </summary>
  4593.         private void StartNestedIterator(QilNode nd)
  4594.         {
  4595.             IteratorDescriptor iterParent = this.iterCurr;
  4596.            
  4597.             // Create a new, nested iterator
  4598.             if (iterParent == null) {
  4599.                 // Create a "root" iterator info that has no parernt
  4600.                 this.iterCurr = new IteratorDescriptor(this.helper);
  4601.             }
  4602.             else {
  4603.                 // Create a nested iterator
  4604.                 this.iterCurr = new IteratorDescriptor(iterParent);
  4605.             }
  4606.            
  4607.             this.iterNested = null;
  4608.         }
  4609.        
  4610.         /// <summary>
  4611.         /// Calls StartNestedIterator(nd) and also sets up the nested iterator to branch to "lblOnEnd" when iteration
  4612.         /// is complete.
  4613.         /// </summary>
  4614.         private void StartNestedIterator(QilNode nd, Label lblOnEnd)
  4615.         {
  4616.             StartNestedIterator(nd);
  4617.             this.iterCurr.SetIterator(lblOnEnd, StorageDescriptor.None());
  4618.         }
  4619.        
  4620.         /// <summary>
  4621.         /// End construction of the current iterator.
  4622.         /// </summary>
  4623.         private void EndNestedIterator(QilNode nd)
  4624.         {
  4625.             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 + ".");
  4626.            
  4627.             // If the nested iterator was constructed in branching mode,
  4628.             if (this.iterCurr.IsBranching) {
  4629.                 // Then if branching hasn't already taken place, do so now
  4630.                 if (this.iterCurr.Storage.Location != ItemLocation.None) {
  4631.                     this.iterCurr.EnsureItemStorageType(nd.XmlType, typeof(bool));
  4632.                     this.iterCurr.EnsureStackNoCache();
  4633.                    
  4634.                     if (this.iterCurr.CurrentBranchingContext == BranchingContext.OnTrue)
  4635.                         this.helper.Emit(OpCodes.Brtrue, this.iterCurr.LabelBranch);
  4636.                     else
  4637.                         this.helper.Emit(OpCodes.Brfalse, this.iterCurr.LabelBranch);
  4638.                    
  4639.                     this.iterCurr.Storage = StorageDescriptor.None();
  4640.                 }
  4641.             }
  4642.            
  4643.             // Save current iterator as nested iterator
  4644.             this.iterNested = this.iterCurr;
  4645.            
  4646.             // Update current iterator to be parent iterator
  4647.             this.iterCurr = this.iterCurr.ParentIterator;
  4648.         }
  4649.        
  4650.         /// <summary>
  4651.         /// Recursively generate code to iterate over the results of the "nd" expression. If "nd" is pushed
  4652.         /// to the writer, then there are no results. If "nd" is a singleton expression and isCached is false,
  4653.         /// then generate code to construct the singleton. Otherwise, cache the sequence in an XmlQuerySequence
  4654.         /// object. Ensure that all items are converted to the specified "itemStorageType".
  4655.         /// </summary>
  4656.         private void NestedVisit(QilNode nd, Type itemStorageType, bool isCached)
  4657.         {
  4658.             if (XmlILConstructInfo.Read(nd).PushToWriterLast) {
  4659.                 // Push results to output, so nothing is left to store
  4660.                 StartNestedIterator(nd);
  4661.                 Visit(nd);
  4662.                 EndNestedIterator(nd);
  4663.                 this.iterCurr.Storage = StorageDescriptor.None();
  4664.             }
  4665.             else if (!isCached && nd.XmlType.IsSingleton) {
  4666.                 // Storage of result will be a non-cached singleton
  4667.                 StartNestedIterator(nd);
  4668.                 Visit(nd);
  4669.                 this.iterCurr.EnsureNoCache();
  4670.                 this.iterCurr.EnsureItemStorageType(nd.XmlType, itemStorageType);
  4671.                 EndNestedIterator(nd);
  4672.                 this.iterCurr.Storage = this.iterNested.Storage;
  4673.             }
  4674.             else {
  4675.                 NestedVisitEnsureCache(nd, itemStorageType);
  4676.             }
  4677.         }
  4678.        
  4679.         /// <summary>
  4680.         /// Calls NestedVisit(QilNode, Type, bool), storing result in the default storage type for "nd".
  4681.         /// </summary>
  4682.         private void NestedVisit(QilNode nd)
  4683.         {
  4684.             NestedVisit(nd, GetItemStorageType(nd), !nd.XmlType.IsSingleton);
  4685.         }
  4686.        
  4687.         /// <summary>
  4688.         /// Recursively generate code to iterate over the results of the "nd" expression. When the expression
  4689.         /// has been fully iterated, it will jump to "lblOnEnd".
  4690.         /// </summary>
  4691.         private void NestedVisit(QilNode nd, Label lblOnEnd)
  4692.         {
  4693.             Debug.Assert(!XmlILConstructInfo.Read(nd).PushToWriterLast);
  4694.             StartNestedIterator(nd, lblOnEnd);
  4695.             Visit(nd);
  4696.             this.iterCurr.EnsureNoCache();
  4697.             this.iterCurr.EnsureItemStorageType(nd.XmlType, GetItemStorageType(nd));
  4698.             EndNestedIterator(nd);
  4699.             this.iterCurr.Storage = this.iterNested.Storage;
  4700.         }
  4701.        
  4702.         /// <summary>
  4703.         /// Call NestedVisit(QilNode) and ensure that result is pushed onto the IL stack.
  4704.         /// </summary>
  4705.         private void NestedVisitEnsureStack(QilNode nd)
  4706.         {
  4707.             Debug.Assert(!XmlILConstructInfo.Read(nd).PushToWriterLast);
  4708.             NestedVisit(nd);
  4709.             this.iterCurr.EnsureStack();
  4710.         }
  4711.        
  4712.         /// <summary>
  4713.         /// Generate code for both QilExpression nodes and ensure that each result is pushed onto the IL stack.
  4714.         /// </summary>
  4715.         private void NestedVisitEnsureStack(QilNode ndLeft, QilNode ndRight)
  4716.         {
  4717.             NestedVisitEnsureStack(ndLeft);
  4718.             NestedVisitEnsureStack(ndRight);
  4719.         }
  4720.        
  4721.         /// <summary>
  4722.         /// Call NestedVisit(QilNode, Type, bool) and ensure that result is pushed onto the IL stack.
  4723.         /// </summary>
  4724.         private void NestedVisitEnsureStack(QilNode nd, Type itemStorageType, bool isCached)
  4725.         {
  4726.             Debug.Assert(!XmlILConstructInfo.Read(nd).PushToWriterLast);
  4727.             NestedVisit(nd, itemStorageType, isCached);
  4728.             this.iterCurr.EnsureStack();
  4729.         }
  4730.        
  4731.         /// <summary>
  4732.         /// Call NestedVisit(QilNode) and ensure that result is stored in local variable "loc".
  4733.         /// </summary>
  4734.         private void NestedVisitEnsureLocal(QilNode nd, LocalBuilder loc)
  4735.         {
  4736.             Debug.Assert(!XmlILConstructInfo.Read(nd).PushToWriterLast);
  4737.             NestedVisit(nd);
  4738.             this.iterCurr.EnsureLocal(loc);
  4739.         }
  4740.        
  4741.         /// <summary>
  4742.         /// Start a nested iterator in a branching context and recursively generate code for the specified QilExpression node.
  4743.         /// </summary>
  4744.         private void NestedVisitWithBranch(QilNode nd, BranchingContext brctxt, Label lblBranch)
  4745.         {
  4746.             Debug.Assert(nd.XmlType.IsSingleton && !XmlILConstructInfo.Read(nd).PushToWriterLast);
  4747.             StartNestedIterator(nd);
  4748.             this.iterCurr.SetBranching(brctxt, lblBranch);
  4749.             Visit(nd);
  4750.             EndNestedIterator(nd);
  4751.             this.iterCurr.Storage = StorageDescriptor.None();
  4752.         }
  4753.        
  4754.         /// <summary>
  4755.         /// Generate code for the QilExpression node and ensure that results are fully cached as an XmlQuerySequence. All results
  4756.         /// should be converted to "itemStorageType" before being added to the cache.
  4757.         /// </summary>
  4758.         private void NestedVisitEnsureCache(QilNode nd, Type itemStorageType)
  4759.         {
  4760.             Debug.Assert(!XmlILConstructInfo.Read(nd).PushToWriterLast);
  4761.             bool cachesResult = CachesResult(nd);
  4762.             LocalBuilder locCache;
  4763.             Label lblOnEnd = this.helper.DefineLabel();
  4764.             Type cacheType;
  4765.             XmlILStorageMethods methods;
  4766.            
  4767.             // If bound expression will already be cached correctly, then don't create an XmlQuerySequence
  4768.             if (cachesResult) {
  4769.                 StartNestedIterator(nd);
  4770.                 Visit(nd);
  4771.                 EndNestedIterator(nd);
  4772.                 this.iterCurr.Storage = this.iterNested.Storage;
  4773.                 Debug.Assert(this.iterCurr.Storage.IsCached, "Expression result should be cached. CachesResult() might have a bug in it.");
  4774.                
  4775.                 // If type of items in the cache matches "itemStorageType", then done
  4776.                 if (this.iterCurr.Storage.ItemStorageType == itemStorageType)
  4777.                     return;
  4778.                
  4779.                 // If the cache has navigators in it, or if converting to a cache of navigators, then EnsureItemStorageType
  4780.                 // can directly convert without needing to create a new cache.
  4781.                 if (this.iterCurr.Storage.ItemStorageType == typeof(XPathNavigator) || itemStorageType == typeof(XPathNavigator)) {
  4782.                     this.iterCurr.EnsureItemStorageType(nd.XmlType, itemStorageType);
  4783.                     return;
  4784.                 }
  4785.                
  4786.                 this.iterCurr.EnsureNoStack("$$$cacheResult");
  4787.             }
  4788.            
  4789.             // Always store navigators in XmlQueryNodeSequence (which implements IList<XPathItem>)
  4790.             cacheType = (GetItemStorageType(nd) == typeof(XPathNavigator)) ? typeof(XPathNavigator) : itemStorageType;
  4791.            
  4792.             // XmlQuerySequence<T> cache;
  4793.             methods = XmlILMethods.StorageMethods[cacheType];
  4794.             locCache = this.helper.DeclareLocal("$$$cache", methods.SeqType);
  4795.             this.helper.Emit(OpCodes.Ldloc, locCache);
  4796.            
  4797.             // Special case non-navigator singletons to use overload of CreateOrReuse
  4798.             if (nd.XmlType.IsSingleton) {
  4799.                 // cache = XmlQuerySequence.CreateOrReuse(cache, item);
  4800.                 NestedVisitEnsureStack(nd, cacheType, false);
  4801.                 this.helper.CallToken(methods.SeqReuseSgl);
  4802.                 this.helper.Emit(OpCodes.Stloc, locCache);
  4803.             }
  4804.             else {
  4805.                 // XmlQuerySequence<T> cache;
  4806.                 // cache = XmlQuerySequence.CreateOrReuse(cache);
  4807.                 this.helper.CallToken(methods.SeqReuse);
  4808.                 this.helper.Emit(OpCodes.Stloc, locCache);
  4809.                 this.helper.Emit(OpCodes.Ldloc, locCache);
  4810.                
  4811.                 StartNestedIterator(nd, lblOnEnd);
  4812.                
  4813.                 if (cachesResult)
  4814.                     this.iterCurr.Storage = this.iterCurr.ParentIterator.Storage;
  4815.                 else
  4816.                     Visit(nd);
  4817.                
  4818.                 // cache.Add(item);
  4819.                 this.iterCurr.EnsureItemStorageType(nd.XmlType, cacheType);
  4820.                 this.iterCurr.EnsureStackNoCache();
  4821.                 this.helper.Call(methods.SeqAdd);
  4822.                 this.helper.Emit(OpCodes.Ldloc, locCache);
  4823.                
  4824.                 // }
  4825.                 this.iterCurr.LoopToEnd(lblOnEnd);
  4826.                
  4827.                 EndNestedIterator(nd);
  4828.                
  4829.                 // Remove cache reference from stack
  4830.                 this.helper.Emit(OpCodes.Pop);
  4831.             }
  4832.            
  4833.             this.iterCurr.Storage = StorageDescriptor.Local(locCache, itemStorageType, true);
  4834.         }
  4835.        
  4836.         /// <summary>
  4837.         /// Returns true if the specified QilExpression node type is *guaranteed* to cache its results in an XmlQuerySequence,
  4838.         /// where items in the cache are stored using the default storage type.
  4839.         /// </summary>
  4840.         private bool CachesResult(QilNode nd)
  4841.         {
  4842.             OptimizerPatterns patt;
  4843.            
  4844.             switch (nd.NodeType) {
  4845.                 case QilNodeType.Let:
  4846.                 case QilNodeType.Parameter:
  4847.                 case QilNodeType.Invoke:
  4848.                 case QilNodeType.XsltInvokeLateBound:
  4849.                 case QilNodeType.XsltInvokeEarlyBound:
  4850.                     return !nd.XmlType.IsSingleton;
  4851.                 case QilNodeType.Filter:
  4852.                    
  4853.                     // EqualityIndex pattern caches results
  4854.                     patt = OptimizerPatterns.Read(nd);
  4855.                     return patt.MatchesPattern(OptimizerPatternName.EqualityIndex);
  4856.                 case QilNodeType.DocOrderDistinct:
  4857.                    
  4858.                     if (nd.XmlType.IsSingleton)
  4859.                         return false;
  4860.                    
  4861.                     // JoinAndDod and DodReverse patterns don't cache results
  4862.                     patt = OptimizerPatterns.Read(nd);
  4863.                     return !patt.MatchesPattern(OptimizerPatternName.JoinAndDod) && !patt.MatchesPattern(OptimizerPatternName.DodReverse);
  4864.             }
  4865.            
  4866.             return false;
  4867.         }
  4868.        
  4869.         /// <summary>
  4870.         /// Shortcut call to XmlILTypeHelper.GetStorageType.
  4871.         /// </summary>
  4872.         private Type GetStorageType(QilNode nd)
  4873.         {
  4874.             return XmlILTypeHelper.GetStorageType(nd.XmlType);
  4875.         }
  4876.        
  4877.         /// <summary>
  4878.         /// Shortcut call to XmlILTypeHelper.GetStorageType.
  4879.         /// </summary>
  4880.         private Type GetStorageType(XmlQueryType typ)
  4881.         {
  4882.             return XmlILTypeHelper.GetStorageType(typ);
  4883.         }
  4884.        
  4885.         /// <summary>
  4886.         /// Shortcut call to XmlILTypeHelper.GetStorageType, using an expression's prime type.
  4887.         /// </summary>
  4888.         private Type GetItemStorageType(QilNode nd)
  4889.         {
  4890.             return XmlILTypeHelper.GetStorageType(nd.XmlType.Prime);
  4891.         }
  4892.        
  4893.         /// <summary>
  4894.         /// Shortcut call to XmlILTypeHelper.GetStorageType, using the prime type.
  4895.         /// </summary>
  4896.         private Type GetItemStorageType(XmlQueryType typ)
  4897.         {
  4898.             return XmlILTypeHelper.GetStorageType(typ.Prime);
  4899.         }
  4900.     }
  4901. }

Developer Fusion