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

  1. //------------------------------------------------------------------------------
  2. // <copyright file="XmlILConstructAnalyzer.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.Schema;
  18. using System.Xml.XPath;
  19. using System.Diagnostics;
  20. using System.Collections;
  21. using System.Collections.Generic;
  22. using System.Collections.Specialized;
  23. using System.Xml.Xsl.Qil;
  24. namespace System.Xml.Xsl.IlGen
  25. {
  26.    
  27.    
  28.     /// <summary>
  29.     /// Until run-time, the exact xml state cannot always be determined. However, the construction analyzer
  30.     /// keeps track of the set of possible xml states at each node in order to reduce run-time state management.
  31.     /// </summary>
  32.     internal enum PossibleXmlStates
  33.     {
  34.         None = 0,
  35.         WithinSequence,
  36.         EnumAttrs,
  37.         WithinContent,
  38.         WithinAttr,
  39.         WithinComment,
  40.         WithinPI,
  41.         Any
  42.     }
  43.    
  44.    
  45.     /// <summary>
  46.     /// 1. Some expressions are lazily materialized by creating an iterator over the results (ex. LiteralString, Content).
  47.     /// 2. Some expressions are incrementally constructed by a Writer (ex. ElementCtor, XsltCopy).
  48.     /// 3. Some expressions can be iterated or written (ex. List).
  49.     /// </summary>
  50.     internal enum XmlILConstructMethod
  51.     {
  52.         Iterator,
  53.         // Construct iterator over expression's results
  54.         Writer,
  55.         // Construct expression through calls to Writer
  56.         WriterThenIterator,
  57.         // Construct expression through calls to caching Writer; then construct iterator over cached results
  58.         IteratorThenWriter
  59.         // Iterate over expression's results and send each item to Writer
  60.     }
  61.    
  62.    
  63.     /// <summary>
  64.     /// Every node is annotated with information about how it will be constructed by ILGen.
  65.     /// </summary>
  66.     internal class XmlILConstructInfo : IQilAnnotation
  67.     {
  68.         private QilNodeType nodeType;
  69.         private PossibleXmlStates xstatesInitial, xstatesFinal, xstatesBeginLoop, xstatesEndLoop;
  70.         private bool isNmspInScope, mightHaveNmsp, mightHaveAttrs, mightHaveDupAttrs, mightHaveNmspAfterAttrs;
  71.         private XmlILConstructMethod constrMeth;
  72.         private XmlILConstructInfo parentInfo;
  73.         private ArrayList callersInfo;
  74.         private bool isReadOnly;
  75.        
  76.         private static XmlILConstructInfo Default;
  77.        
  78.         /// <summary>
  79.         /// Get ConstructInfo annotation for the specified node. Lazily create if necessary.
  80.         /// </summary>
  81.         public static XmlILConstructInfo Read(QilNode nd)
  82.         {
  83.             XmlILAnnotation ann = nd.Annotation as XmlILAnnotation;
  84.             XmlILConstructInfo constrInfo = (ann != null) ? ann.ConstructInfo : null;
  85.            
  86.             if (constrInfo == null) {
  87.                 if (Default == null) {
  88.                     constrInfo = new XmlILConstructInfo(QilNodeType.Unknown);
  89.                     constrInfo.isReadOnly = true;
  90.                    
  91.                     Default = constrInfo;
  92.                 }
  93.                 else {
  94.                     constrInfo = Default;
  95.                 }
  96.             }
  97.            
  98.             return constrInfo;
  99.         }
  100.        
  101.         /// <summary>
  102.         /// Create and initialize XmlILConstructInfo annotation for the specified node.
  103.         /// </summary>
  104.         public static XmlILConstructInfo Write(QilNode nd)
  105.         {
  106.             XmlILAnnotation ann = XmlILAnnotation.Write(nd);
  107.             XmlILConstructInfo constrInfo = ann.ConstructInfo;
  108.            
  109.             if (constrInfo == null || constrInfo.isReadOnly) {
  110.                 constrInfo = new XmlILConstructInfo(nd.NodeType);
  111.                 ann.ConstructInfo = constrInfo;
  112.             }
  113.            
  114.             return constrInfo;
  115.         }
  116.        
  117.         /// <summary>
  118.         /// Default to worst possible construction information.
  119.         /// </summary>
  120.         private XmlILConstructInfo(QilNodeType nodeType)
  121.         {
  122.             this.nodeType = nodeType;
  123.             this.xstatesInitial = this.xstatesFinal = PossibleXmlStates.Any;
  124.             this.xstatesBeginLoop = this.xstatesEndLoop = PossibleXmlStates.None;
  125.             this.isNmspInScope = false;
  126.             this.mightHaveNmsp = true;
  127.             this.mightHaveAttrs = true;
  128.             this.mightHaveDupAttrs = true;
  129.             this.mightHaveNmspAfterAttrs = true;
  130.             this.constrMeth = XmlILConstructMethod.Iterator;
  131.             this.parentInfo = null;
  132.         }
  133.        
  134.         /// <summary>
  135.         /// Xml states that are possible as construction of the annotated expression begins.
  136.         /// </summary>
  137.         public PossibleXmlStates InitialStates {
  138.             get { return this.xstatesInitial; }
  139.             set {
  140.                 Debug.Assert(!this.isReadOnly, "This XmlILConstructInfo instance is read-only.");
  141.                 this.xstatesInitial = value;
  142.             }
  143.         }
  144.        
  145.         /// <summary>
  146.         /// Xml states that are possible as construction of the annotated expression ends.
  147.         /// </summary>
  148.         public PossibleXmlStates FinalStates {
  149.             get { return this.xstatesFinal; }
  150.             set {
  151.                 Debug.Assert(!this.isReadOnly, "This XmlILConstructInfo instance is read-only.");
  152.                 this.xstatesFinal = value;
  153.             }
  154.         }
  155.        
  156.         /// <summary>
  157.         /// Xml states that are possible as looping begins. This is None if the annotated expression does not loop.
  158.         /// </summary>
  159.         public PossibleXmlStates BeginLoopStates {
  160.             //get { return this.xstatesBeginLoop; }
  161.             set {
  162.                 Debug.Assert(!this.isReadOnly, "This XmlILConstructInfo instance is read-only.");
  163.                 this.xstatesBeginLoop = value;
  164.             }
  165.         }
  166.        
  167.         /// <summary>
  168.         /// Xml states that are possible as looping ends. This is None if the annotated expression does not loop.
  169.         /// </summary>
  170.         public PossibleXmlStates EndLoopStates {
  171.             //get { return this.xstatesEndLoop; }
  172.             set {
  173.                 Debug.Assert(!this.isReadOnly, "This XmlILConstructInfo instance is read-only.");
  174.                 this.xstatesEndLoop = value;
  175.             }
  176.         }
  177.        
  178.         /// <summary>
  179.         /// Return the method that will be used to construct the annotated node.
  180.         /// </summary>
  181.         public XmlILConstructMethod ConstructMethod {
  182.             get { return this.constrMeth; }
  183.             set {
  184.                 Debug.Assert(!this.isReadOnly, "This XmlILConstructInfo instance is read-only.");
  185.                 this.constrMeth = value;
  186.             }
  187.         }
  188.        
  189.         /// <summary>
  190.         /// Returns true if construction method is Writer or WriterThenIterator.
  191.         /// </summary>
  192.         public bool PushToWriterFirst {
  193.             get { return this.constrMeth == XmlILConstructMethod.Writer || this.constrMeth == XmlILConstructMethod.WriterThenIterator; }
  194.             set {
  195.                 Debug.Assert(!this.isReadOnly, "This XmlILConstructInfo instance is read-only.");
  196.                 Debug.Assert(value);
  197.                
  198.                 switch (this.constrMeth) {
  199.                     case XmlILConstructMethod.Iterator:
  200.                         this.constrMeth = XmlILConstructMethod.WriterThenIterator;
  201.                         break;
  202.                     case XmlILConstructMethod.IteratorThenWriter:
  203.                        
  204.                         this.constrMeth = XmlILConstructMethod.Writer;
  205.                         break;
  206.                 }
  207.             }
  208.         }
  209.        
  210.         /// <summary>
  211.         /// Returns true if construction method is Writer or IteratorThenWriter.
  212.         /// </summary>
  213.         public bool PushToWriterLast {
  214.             get { return this.constrMeth == XmlILConstructMethod.Writer || this.constrMeth == XmlILConstructMethod.IteratorThenWriter; }
  215.             set {
  216.                 Debug.Assert(!this.isReadOnly, "This XmlILConstructInfo instance is read-only.");
  217.                 Debug.Assert(value);
  218.                
  219.                 switch (this.constrMeth) {
  220.                     case XmlILConstructMethod.Iterator:
  221.                         this.constrMeth = XmlILConstructMethod.IteratorThenWriter;
  222.                         break;
  223.                     case XmlILConstructMethod.WriterThenIterator:
  224.                        
  225.                         this.constrMeth = XmlILConstructMethod.Writer;
  226.                         break;
  227.                 }
  228.             }
  229.         }
  230.        
  231.         /// <summary>
  232.         /// Returns true if construction method is IteratorThenWriter or Iterator.
  233.         /// </summary>
  234.         public bool PullFromIteratorFirst {
  235.             get { return this.constrMeth == XmlILConstructMethod.IteratorThenWriter || this.constrMeth == XmlILConstructMethod.Iterator; }
  236.             set {
  237.                 Debug.Assert(!this.isReadOnly, "This XmlILConstructInfo instance is read-only.");
  238.                 Debug.Assert(value);
  239.                
  240.                 switch (this.constrMeth) {
  241.                     case XmlILConstructMethod.Writer:
  242.                         this.constrMeth = XmlILConstructMethod.IteratorThenWriter;
  243.                         break;
  244.                     case XmlILConstructMethod.WriterThenIterator:
  245.                        
  246.                         this.constrMeth = XmlILConstructMethod.Iterator;
  247.                         break;
  248.                 }
  249.             }
  250.         }
  251.        
  252.         /// <summary>
  253.         /// If the annotated expression will be constructed as the content of another constructor, and this can be
  254.         /// guaranteed at compile-time, then this property will be the non-null XmlILConstructInfo of that constructor.
  255.         /// </summary>
  256.         public XmlILConstructInfo ParentInfo {
  257.             //get { return this.parentInfo; }
  258.             set {
  259.                 Debug.Assert(!this.isReadOnly, "This XmlILConstructInfo instance is read-only.");
  260.                 this.parentInfo = value;
  261.             }
  262.         }
  263.        
  264.         /// <summary>
  265.         /// If the annotated expression will be constructed as the content of an ElementCtor, and this can be
  266.         /// guaranteed at compile-time, then this property will be the non-null XmlILConstructInfo of that constructor.
  267.         /// </summary>
  268.         public XmlILConstructInfo ParentElementInfo {
  269.             get {
  270.                 if (this.parentInfo != null && this.parentInfo.nodeType == QilNodeType.ElementCtor)
  271.                     return this.parentInfo;
  272.                
  273.                 return null;
  274.             }
  275.         }
  276.        
  277.         /// <summary>
  278.         /// This annotation is only applicable to NamespaceDecl nodes and to ElementCtor and AttributeCtor nodes with
  279.         /// literal names. If the namespace is already guaranteed to be constructed, then this property will be true.
  280.         /// </summary>
  281.         public bool IsNamespaceInScope {
  282.             get { return this.isNmspInScope; }
  283.             set {
  284.                 Debug.Assert(!this.isReadOnly, "This XmlILConstructInfo instance is read-only.");
  285.                 this.isNmspInScope = value;
  286.             }
  287.         }
  288.        
  289.         /// <summary>
  290.         /// This annotation is only applicable to ElementCtor nodes. If the element might have local namespaces
  291.         /// added to it at runtime, then this property will be true.
  292.         /// </summary>
  293.         public bool MightHaveNamespaces {
  294.             get { return this.mightHaveNmsp; }
  295.             set {
  296.                 Debug.Assert(!this.isReadOnly, "This XmlILConstructInfo instance is read-only.");
  297.                 this.mightHaveNmsp = value;
  298.             }
  299.         }
  300.        
  301.         /// <summary>
  302.         /// This annotation is only applicable to ElementCtor nodes. If the element might have namespaces added to it after
  303.         /// attributes have already been added, then this property will be true.
  304.         /// </summary>
  305.         public bool MightHaveNamespacesAfterAttributes {
  306.             get { return this.mightHaveNmspAfterAttrs; }
  307.             set {
  308.                 Debug.Assert(!this.isReadOnly, "This XmlILConstructInfo instance is read-only.");
  309.                 this.mightHaveNmspAfterAttrs = value;
  310.             }
  311.         }
  312.        
  313.         /// <summary>
  314.         /// This annotation is only applicable to ElementCtor nodes. If the element might have attributes added to it at
  315.         /// runtime, then this property will be true.
  316.         /// </summary>
  317.         public bool MightHaveAttributes {
  318.             get { return this.mightHaveAttrs; }
  319.             set {
  320.                 Debug.Assert(!this.isReadOnly, "This XmlILConstructInfo instance is read-only.");
  321.                 this.mightHaveAttrs = value;
  322.             }
  323.         }
  324.        
  325.         /// <summary>
  326.         /// This annotation is only applicable to ElementCtor nodes. If the element might have multiple attributes added to
  327.         /// it with the same name, then this property will be true.
  328.         /// </summary>
  329.         public bool MightHaveDuplicateAttributes {
  330.             get { return this.mightHaveDupAttrs; }
  331.             set {
  332.                 Debug.Assert(!this.isReadOnly, "This XmlILConstructInfo instance is read-only.");
  333.                 this.mightHaveDupAttrs = value;
  334.             }
  335.         }
  336.        
  337.         /// <summary>
  338.         /// This annotation is only applicable to Function nodes. It contains a list of XmlILConstructInfo annontations
  339.         /// for all QilInvoke nodes which call the annotated function.
  340.         /// </summary>
  341.         public ArrayList CallersInfo {
  342.             get {
  343.                 if (this.callersInfo == null)
  344.                     this.callersInfo = new ArrayList();
  345.                
  346.                 return this.callersInfo;
  347.             }
  348.         }
  349.        
  350.         /// <summary>
  351.         /// Return name of this annotation.
  352.         /// </summary>
  353.         public virtual string Name {
  354.             get { return "ConstructInfo"; }
  355.         }
  356.        
  357.         /// <summary>
  358.         /// Return string representation of this annotation.
  359.         /// </summary>
  360.         public override string ToString()
  361.         {
  362.             string s = "";
  363.            
  364.             if (this.constrMeth != XmlILConstructMethod.Iterator) {
  365.                 s += this.constrMeth.ToString();
  366.                
  367.                 s += ", " + this.xstatesInitial;
  368.                
  369.                 if (this.xstatesBeginLoop != PossibleXmlStates.None) {
  370.                     s += " => " + this.xstatesBeginLoop.ToString() + " => " + this.xstatesEndLoop.ToString();
  371.                 }
  372.                
  373.                 s += " => " + this.xstatesFinal;
  374.                
  375.                 if (!MightHaveAttributes)
  376.                     s += ", NoAttrs";
  377.                
  378.                 if (!MightHaveDuplicateAttributes)
  379.                     s += ", NoDupAttrs";
  380.                
  381.                 if (!MightHaveNamespaces)
  382.                     s += ", NoNmsp";
  383.                
  384.                 if (!MightHaveNamespacesAfterAttributes)
  385.                     s += ", NoNmspAfterAttrs";
  386.             }
  387.            
  388.             return s;
  389.         }
  390.     }
  391.    
  392.    
  393.     /// <summary>
  394.     /// Scans the content of an constructor and tries to minimize the number of well-formed checks that will have
  395.     /// to be made at runtime when constructing content.
  396.     /// </summary>
  397.     internal class XmlILStateAnalyzer
  398.     {
  399.         protected XmlILConstructInfo parentInfo;
  400.         protected QilFactory fac;
  401.         protected PossibleXmlStates xstates;
  402.         protected bool withinElem;
  403.        
  404.         /// <summary>
  405.         /// Constructor.
  406.         /// </summary>
  407.         public XmlILStateAnalyzer(QilFactory fac)
  408.         {
  409.             this.fac = fac;
  410.         }
  411.        
  412.         /// <summary>
  413.         /// Perform analysis on the specified constructor and its content. Return the ndContent that was passed in,
  414.         /// or a replacement.
  415.         /// </summary>
  416.         public virtual QilNode Analyze(QilNode ndConstr, QilNode ndContent)
  417.         {
  418.             if (ndConstr == null) {
  419.                 // Root expression is analyzed
  420.                 this.parentInfo = null;
  421.                 this.xstates = PossibleXmlStates.WithinSequence;
  422.                 this.withinElem = false;
  423.                
  424.                 Debug.Assert(ndContent != null);
  425.                 ndContent = AnalyzeContent(ndContent);
  426.             }
  427.             else {
  428.                 this.parentInfo = XmlILConstructInfo.Write(ndConstr);
  429.                
  430.                 if (ndConstr.NodeType == QilNodeType.Function) {
  431.                     // Results of function should be pushed to writer
  432.                     this.parentInfo.ConstructMethod = XmlILConstructMethod.Writer;
  433.                    
  434.                     // Start with PossibleXmlStates.None and then add additional possible starting states
  435.                     PossibleXmlStates xstates = PossibleXmlStates.None;
  436.                     foreach (XmlILConstructInfo infoCaller in this.parentInfo.CallersInfo) {
  437.                         if (xstates == PossibleXmlStates.None) {
  438.                             xstates = infoCaller.InitialStates;
  439.                         }
  440.                         else if (xstates != infoCaller.InitialStates) {
  441.                             xstates = PossibleXmlStates.Any;
  442.                         }
  443.                        
  444.                         // Function's results are pushed to Writer, so make sure that Invoke nodes' construct methods match
  445.                         infoCaller.PushToWriterFirst = true;
  446.                     }
  447.                     this.parentInfo.InitialStates = xstates;
  448.                 }
  449.                 else {
  450.                     // Build a standalone tree, with this constructor as its root
  451.                     if (ndConstr.NodeType != QilNodeType.Choice)
  452.                         this.parentInfo.InitialStates = this.parentInfo.FinalStates = PossibleXmlStates.WithinSequence;
  453.                    
  454.                     // Don't stream Rtf; fully cache the Rtf and copy it into any containing tree in order to simplify XmlILVisitor.VisitRtfCtor
  455.                     if (ndConstr.NodeType != QilNodeType.RtfCtor)
  456.                         this.parentInfo.ConstructMethod = XmlILConstructMethod.WriterThenIterator;
  457.                 }
  458.                
  459.                 // Set withinElem = true if analyzing element content
  460.                 this.withinElem = (ndConstr.NodeType == QilNodeType.ElementCtor);
  461.                
  462.                 switch (ndConstr.NodeType) {
  463.                     case QilNodeType.DocumentCtor:
  464.                         this.xstates = PossibleXmlStates.WithinContent;
  465.                         break;
  466.                     case QilNodeType.ElementCtor:
  467.                         this.xstates = PossibleXmlStates.EnumAttrs;
  468.                         break;
  469.                     case QilNodeType.AttributeCtor:
  470.                         this.xstates = PossibleXmlStates.WithinAttr;
  471.                         break;
  472.                     case QilNodeType.NamespaceDecl:
  473.                         Debug.Assert(ndContent == null);
  474.                         break;
  475.                     case QilNodeType.TextCtor:
  476.                         Debug.Assert(ndContent == null);
  477.                         break;
  478.                     case QilNodeType.RawTextCtor:
  479.                         Debug.Assert(ndContent == null);
  480.                         break;
  481.                     case QilNodeType.CommentCtor:
  482.                         this.xstates = PossibleXmlStates.WithinComment;
  483.                         break;
  484.                     case QilNodeType.PICtor:
  485.                         this.xstates = PossibleXmlStates.WithinPI;
  486.                         break;
  487.                     case QilNodeType.XsltCopy:
  488.                         this.xstates = PossibleXmlStates.Any;
  489.                         break;
  490.                     case QilNodeType.XsltCopyOf:
  491.                         Debug.Assert(ndContent == null);
  492.                         break;
  493.                     case QilNodeType.Function:
  494.                         this.xstates = this.parentInfo.InitialStates;
  495.                         break;
  496.                     case QilNodeType.RtfCtor:
  497.                         this.xstates = PossibleXmlStates.WithinContent;
  498.                         break;
  499.                     case QilNodeType.Choice:
  500.                         this.xstates = PossibleXmlStates.Any;
  501.                         break;
  502.                     default:
  503.                         Debug.Assert(false, ndConstr.NodeType + " is not handled by XmlILStateAnalyzer.");
  504.                         break;
  505.                 }
  506.                
  507.                 if (ndContent != null)
  508.                     ndContent = AnalyzeContent(ndContent);
  509.                
  510.                 if (ndConstr.NodeType == QilNodeType.Choice)
  511.                     AnalyzeChoice(ndConstr as QilChoice, this.parentInfo);
  512.                
  513.                 // Since Function will never be another node's content, set its final states here
  514.                 if (ndConstr.NodeType == QilNodeType.Function)
  515.                     this.parentInfo.FinalStates = this.xstates;
  516.             }
  517.            
  518.             return ndContent;
  519.         }
  520.        
  521.         /// <summary>
  522.         /// Recursively analyze content. Return "nd" or a replacement for it.
  523.         /// </summary>
  524.         protected virtual QilNode AnalyzeContent(QilNode nd)
  525.         {
  526.             XmlILConstructInfo info;
  527.             QilNode ndChild;
  528.            
  529.             // Handle special node-types that are replaced
  530.             switch (nd.NodeType) {
  531.                 case QilNodeType.For:
  532.                 case QilNodeType.Let:
  533.                 case QilNodeType.Parameter:
  534.                     // Iterator references are shared and cannot be annotated directly with ConstructInfo,
  535.                     // so wrap them with Nop node.
  536.                     nd = this.fac.Nop(nd);
  537.                     break;
  538.             }
  539.            
  540.             // Get node's ConstructInfo annotation
  541.             info = XmlILConstructInfo.Write(nd);
  542.            
  543.             // Set node's guaranteed parent constructor
  544.             info.ParentInfo = this.parentInfo;
  545.            
  546.             // Construct all content using the Writer
  547.             info.PushToWriterLast = true;
  548.            
  549.             // Set states that are possible before expression is constructed
  550.             info.InitialStates = this.xstates;
  551.            
  552.             switch (nd.NodeType) {
  553.                 case QilNodeType.Loop:
  554.                     AnalyzeLoop(nd as QilLoop, info);
  555.                     break;
  556.                 case QilNodeType.Sequence:
  557.                     AnalyzeSequence(nd as QilList, info);
  558.                     break;
  559.                 case QilNodeType.Conditional:
  560.                     AnalyzeConditional(nd as QilTernary, info);
  561.                     break;
  562.                 case QilNodeType.Choice:
  563.                     AnalyzeChoice(nd as QilChoice, info);
  564.                     break;
  565.                 case QilNodeType.Error:
  566.                 case QilNodeType.Warning:
  567.                    
  568.                     // Ensure that construct method is Writer
  569.                     info.ConstructMethod = XmlILConstructMethod.Writer;
  570.                     break;
  571.                 case QilNodeType.Nop:
  572.                    
  573.                     ndChild = (nd as QilUnary).Child;
  574.                     switch (ndChild.NodeType) {
  575.                         case QilNodeType.For:
  576.                         case QilNodeType.Let:
  577.                         case QilNodeType.Parameter:
  578.                             // Copy iterator items as content
  579.                             AnalyzeCopy(nd, info);
  580.                             break;
  581.                         default:
  582.                            
  583.                             // Ensure that construct method is Writer and recursively analyze content
  584.                             info.ConstructMethod = XmlILConstructMethod.Writer;
  585.                             AnalyzeContent(ndChild);
  586.                             break;
  587.                     }
  588.                     break;
  589.                 default:
  590.                    
  591.                     AnalyzeCopy(nd, info);
  592.                     break;
  593.             }
  594.            
  595.             // Set states that are possible after expression is constructed
  596.             info.FinalStates = this.xstates;
  597.            
  598.             return nd;
  599.         }
  600.        
  601.         /// <summary>
  602.         /// Analyze loop.
  603.         /// </summary>
  604.         protected virtual void AnalyzeLoop(QilLoop ndLoop, XmlILConstructInfo info)
  605.         {
  606.             XmlQueryType typ = ndLoop.XmlType;
  607.            
  608.             // Ensure that construct method is Writer
  609.             info.ConstructMethod = XmlILConstructMethod.Writer;
  610.            
  611.             if (!typ.IsSingleton)
  612.                 StartLoop(typ, info);
  613.            
  614.             // Body constructs content
  615.             ndLoop.Body = AnalyzeContent(ndLoop.Body);
  616.            
  617.             if (!typ.IsSingleton)
  618.                 EndLoop(typ, info);
  619.         }
  620.        
  621.         /// <summary>
  622.         /// Analyze list.
  623.         /// </summary>
  624.         protected virtual void AnalyzeSequence(QilList ndSeq, XmlILConstructInfo info)
  625.         {
  626.             // Ensure that construct method is Writer
  627.             info.ConstructMethod = XmlILConstructMethod.Writer;
  628.            
  629.             // Analyze each item in the list
  630.             for (int idx = 0; idx < ndSeq.Count; idx++)
  631.                 ndSeq[idx] = AnalyzeContent(ndSeq[idx]);
  632.         }
  633.        
  634.         /// <summary>
  635.         /// Analyze conditional.
  636.         /// </summary>
  637.         protected virtual void AnalyzeConditional(QilTernary ndCond, XmlILConstructInfo info)
  638.         {
  639.             PossibleXmlStates xstatesTrue;
  640.            
  641.             // Ensure that construct method is Writer
  642.             info.ConstructMethod = XmlILConstructMethod.Writer;
  643.            
  644.             // Visit true branch; save resulting states
  645.             ndCond.Center = AnalyzeContent(ndCond.Center);
  646.             xstatesTrue = this.xstates;
  647.            
  648.             // Restore starting states and visit false branch
  649.             this.xstates = info.InitialStates;
  650.             ndCond.Right = AnalyzeContent(ndCond.Right);
  651.            
  652.             // Conditional ending states consist of combination of true and false branch states
  653.             if (xstatesTrue != this.xstates)
  654.                 this.xstates = PossibleXmlStates.Any;
  655.         }
  656.        
  657.         /// <summary>
  658.         /// Analyze choice.
  659.         /// </summary>
  660.         protected virtual void AnalyzeChoice(QilChoice ndChoice, XmlILConstructInfo info)
  661.         {
  662.             PossibleXmlStates xstatesChoice;
  663.             int idx;
  664.            
  665.             // Visit default branch; save resulting states
  666.             idx = ndChoice.Branches.Count - 1;
  667.             ndChoice.Branches[idx] = AnalyzeContent(ndChoice.Branches[idx]);
  668.             xstatesChoice = this.xstates;
  669.            
  670.             // Visit all other branches
  671.             while (--idx >= 0) {
  672.                 // Restore starting states and visit the next branch
  673.                 this.xstates = info.InitialStates;
  674.                 ndChoice.Branches[idx] = AnalyzeContent(ndChoice.Branches[idx]);
  675.                
  676.                 // Choice ending states consist of combination of all branch states
  677.                 if (xstatesChoice != this.xstates)
  678.                     xstatesChoice = PossibleXmlStates.Any;
  679.             }
  680.            
  681.             this.xstates = xstatesChoice;
  682.         }
  683.        
  684.         /// <summary>
  685.         /// Analyze copying items.
  686.         /// </summary>
  687.         protected virtual void AnalyzeCopy(QilNode ndCopy, XmlILConstructInfo info)
  688.         {
  689.             XmlQueryType typ = ndCopy.XmlType;
  690.            
  691.             // Copying item(s) to output involves looping if there is not exactly one item in the sequence
  692.             if (!typ.IsSingleton)
  693.                 StartLoop(typ, info);
  694.            
  695.             // Determine state transitions that may take place
  696.             if (MaybeContent(typ)) {
  697.                 if (MaybeAttrNmsp(typ)) {
  698.                     // Node might be Attr/Nmsp or non-Attr/Nmsp, so transition from EnumAttrs to WithinContent *may* occur
  699.                     if (this.xstates == PossibleXmlStates.EnumAttrs)
  700.                         this.xstates = PossibleXmlStates.Any;
  701.                 }
  702.                 else {
  703.                     // Node is guaranteed not to be Attr/Nmsp, so transition to WithinContent will occur if starting
  704.                     // state is EnumAttrs or if constructing within an element (guaranteed to be in EnumAttrs or WithinContent state)
  705.                     if (this.xstates == PossibleXmlStates.EnumAttrs || this.withinElem)
  706.                         this.xstates = PossibleXmlStates.WithinContent;
  707.                 }
  708.             }
  709.            
  710.             if (!typ.IsSingleton)
  711.                 EndLoop(typ, info);
  712.         }
  713.        
  714.         /// <summary>
  715.         /// Calculate starting xml states that will result when iterating over and constructing an expression of the specified type.
  716.         /// </summary>
  717.         private void StartLoop(XmlQueryType typ, XmlILConstructInfo info)
  718.         {
  719.             Debug.Assert(!typ.IsSingleton);
  720.            
  721.             // This is tricky, because the looping introduces a feedback loop:
  722.             // 1. Because loops may be executed many times, the beginning set of states must include the ending set of states.
  723.             // 2. Because loops may be executed 0 times, the final set of states after all looping is complete must include
  724.             // the initial set of states.
  725.             //
  726.             // +-- states-initial
  727.             // | |
  728.             // | states-begin-loop <--+
  729.             // | | |
  730.             // | +--------------+ |
  731.             // | | Construction | |
  732.             // | +--------------+ |
  733.             // | | |
  734.             // | states-end-loop ----+
  735.             // | |
  736.             // +--> states-final
  737.            
  738.             // Save starting loop states
  739.             info.BeginLoopStates = this.xstates;
  740.            
  741.             if (typ.MaybeMany) {
  742.                 // If transition might occur from EnumAttrs to WithinContent, then states-end might be WithinContent, which
  743.                 // means states-begin needs to also include WithinContent.
  744.                 if (this.xstates == PossibleXmlStates.EnumAttrs && MaybeContent(typ))
  745.                     info.BeginLoopStates = this.xstates = PossibleXmlStates.Any;
  746.             }
  747.         }
  748.        
  749.         /// <summary>
  750.         /// Calculate ending xml states that will result when iterating over and constructing an expression of the specified type.
  751.         /// </summary>
  752.         private void EndLoop(XmlQueryType typ, XmlILConstructInfo info)
  753.         {
  754.             Debug.Assert(!typ.IsSingleton);
  755.            
  756.             // Save ending loop states
  757.             info.EndLoopStates = this.xstates;
  758.            
  759.             // If it's possible to loop zero times, then states-final needs to include states-initial
  760.             if (typ.MaybeEmpty && info.InitialStates != this.xstates)
  761.                 this.xstates = PossibleXmlStates.Any;
  762.         }
  763.        
  764.         /// <summary>
  765.         /// Return true if an instance of the specified type might be an attribute or a namespace node.
  766.         /// </summary>
  767.         private bool MaybeAttrNmsp(XmlQueryType typ)
  768.         {
  769.             return (typ.NodeKinds & (XmlNodeKindFlags.Attribute | XmlNodeKindFlags.Namespace)) != XmlNodeKindFlags.None;
  770.         }
  771.        
  772.         /// <summary>
  773.         /// Return true if an instance of the specified type might be a non-empty content type (attr/nsmp don't count).
  774.         /// </summary>
  775.         private bool MaybeContent(XmlQueryType typ)
  776.         {
  777.             return !typ.IsNode || (typ.NodeKinds & ~(XmlNodeKindFlags.Attribute | XmlNodeKindFlags.Namespace)) != XmlNodeKindFlags.None;
  778.         }
  779.     }
  780.    
  781.    
  782.     /// <summary>
  783.     /// Scans the content of an ElementCtor and tries to minimize the number of well-formed checks that will have
  784.     /// to be made at runtime when constructing content.
  785.     /// </summary>
  786.     internal class XmlILElementAnalyzer : XmlILStateAnalyzer
  787.     {
  788.         private NameTable attrNames = new NameTable();
  789.         private ArrayList dupAttrs = new ArrayList();
  790.        
  791.         /// <summary>
  792.         /// Constructor.
  793.         /// </summary>
  794.         public XmlILElementAnalyzer(QilFactory fac) : base(fac)
  795.         {
  796.         }
  797.        
  798.         /// <summary>
  799.         /// Analyze the content argument of the ElementCtor. Try to eliminate as many runtime checks as possible,
  800.         /// both for the ElementCtor and for content constructors.
  801.         /// </summary>
  802.         public override QilNode Analyze(QilNode ndElem, QilNode ndContent)
  803.         {
  804.             Debug.Assert(ndElem.NodeType == QilNodeType.ElementCtor);
  805.             this.parentInfo = XmlILConstructInfo.Write(ndElem);
  806.            
  807.             // Start by assuming that these properties are false (they default to true, but analyzer might be able to
  808.             // prove they are really false).
  809.             this.parentInfo.MightHaveNamespacesAfterAttributes = false;
  810.             this.parentInfo.MightHaveAttributes = false;
  811.             this.parentInfo.MightHaveDuplicateAttributes = false;
  812.            
  813.             // The element's namespace might need to be declared
  814.             this.parentInfo.MightHaveNamespaces = !this.parentInfo.IsNamespaceInScope;
  815.            
  816.             // Clear list of duplicate attributes
  817.             this.dupAttrs.Clear();
  818.            
  819.             return base.Analyze(ndElem, ndContent);
  820.         }
  821.        
  822.         /// <summary>
  823.         /// Analyze loop.
  824.         /// </summary>
  825.         protected override void AnalyzeLoop(QilLoop ndLoop, XmlILConstructInfo info)
  826.         {
  827.             // Constructing attributes/namespaces in a loop can cause duplicates, namespaces after attributes, etc.
  828.             if (ndLoop.XmlType.MaybeMany)
  829.                 CheckAttributeNamespaceConstruct(ndLoop.XmlType);
  830.            
  831.             base.AnalyzeLoop(ndLoop, info);
  832.         }
  833.        
  834.         /// <summary>
  835.         /// Analyze copying items.
  836.         /// </summary>
  837.         protected override void AnalyzeCopy(QilNode ndCopy, XmlILConstructInfo info)
  838.         {
  839.             if (ndCopy.NodeType == QilNodeType.AttributeCtor) {
  840.                 AnalyzeAttributeCtor(ndCopy as QilBinary, info);
  841.             }
  842.             else {
  843.                 CheckAttributeNamespaceConstruct(ndCopy.XmlType);
  844.             }
  845.            
  846.             base.AnalyzeCopy(ndCopy, info);
  847.         }
  848.        
  849.         /// <summary>
  850.         /// Analyze attribute constructor.
  851.         /// </summary>
  852.         private void AnalyzeAttributeCtor(QilBinary ndAttr, XmlILConstructInfo info)
  853.         {
  854.             if (ndAttr.Left.NodeType == QilNodeType.LiteralQName) {
  855.                 QilName ndName = ndAttr.Left as QilName;
  856.                 XmlQualifiedName qname;
  857.                 int idx;
  858.                
  859.                 // This attribute might be constructed on the parent element
  860.                 this.parentInfo.MightHaveAttributes = true;
  861.                
  862.                 // Check to see whether this attribute is a duplicate of a previous attribute
  863.                 if (!this.parentInfo.MightHaveDuplicateAttributes) {
  864.                     qname = new XmlQualifiedName(this.attrNames.Add(ndName.LocalName), this.attrNames.Add(ndName.NamespaceUri));
  865.                    
  866.                     for (idx = 0; idx < this.dupAttrs.Count; idx++) {
  867.                         XmlQualifiedName qnameDup = (XmlQualifiedName)this.dupAttrs[idx];
  868.                        
  869.                         if ((object)qnameDup.Name == (object)qname.Name && (object)qnameDup.Namespace == (object)qname.Namespace) {
  870.                             // A duplicate attribute has been encountered
  871.                             this.parentInfo.MightHaveDuplicateAttributes = true;
  872.                         }
  873.                     }
  874.                    
  875.                     if (idx >= this.dupAttrs.Count) {
  876.                         // This is not a duplicate attribute, so add it to the set
  877.                         this.dupAttrs.Add(qname);
  878.                     }
  879.                 }
  880.                
  881.                 // The attribute's namespace might need to be declared
  882.                 if (!info.IsNamespaceInScope)
  883.                     this.parentInfo.MightHaveNamespaces = true;
  884.             }
  885.             else {
  886.                 // Attribute prefix and namespace are not known at compile-time
  887.                 CheckAttributeNamespaceConstruct(ndAttr.XmlType);
  888.             }
  889.         }
  890.        
  891.         /// <summary>
  892.         /// If type might contain attributes or namespaces, set appropriate parent element flags.
  893.         /// </summary>
  894.         private void CheckAttributeNamespaceConstruct(XmlQueryType typ)
  895.         {
  896.             // If content might contain attributes,
  897.             if ((typ.NodeKinds & XmlNodeKindFlags.Attribute) != XmlNodeKindFlags.None) {
  898.                 // Mark element as possibly having attributes and duplicate attributes (since we don't know the names)
  899.                 this.parentInfo.MightHaveAttributes = true;
  900.                 this.parentInfo.MightHaveDuplicateAttributes = true;
  901.                
  902.                 // Attribute namespaces might be declared
  903.                 this.parentInfo.MightHaveNamespaces = true;
  904.             }
  905.            
  906.             // If content might contain namespaces,
  907.             if ((typ.NodeKinds & XmlNodeKindFlags.Namespace) != XmlNodeKindFlags.None) {
  908.                 // Then element might have namespaces,
  909.                 this.parentInfo.MightHaveNamespaces = true;
  910.                
  911.                 // If attributes might already have been constructed,
  912.                 if (this.parentInfo.MightHaveAttributes) {
  913.                     // Then attributes might precede namespace declarations
  914.                     this.parentInfo.MightHaveNamespacesAfterAttributes = true;
  915.                 }
  916.             }
  917.         }
  918.     }
  919.    
  920.    
  921.     /// <summary>
  922.     /// Scans constructed content, looking for redundant namespace declarations. If any are found, then they are marked
  923.     /// and removed later.
  924.     /// </summary>
  925.     internal class XmlILNamespaceAnalyzer
  926.     {
  927.         private XmlNamespaceManager nsmgr = new XmlNamespaceManager(new NameTable());
  928.         private bool addInScopeNmsp;
  929.         private int cntNmsp;
  930.        
  931.         /// <summary>
  932.         /// Perform scan.
  933.         /// </summary>
  934.         public void Analyze(QilNode nd, bool defaultNmspInScope)
  935.         {
  936.             this.addInScopeNmsp = false;
  937.             this.cntNmsp = 0;
  938.            
  939.             // If xmlns="" is in-scope, push it onto the namespace stack
  940.             if (defaultNmspInScope) {
  941.                 this.nsmgr.PushScope();
  942.                 this.nsmgr.AddNamespace(string.Empty, string.Empty);
  943.                 this.cntNmsp++;
  944.             }
  945.            
  946.             AnalyzeContent(nd);
  947.            
  948.             if (defaultNmspInScope)
  949.                 this.nsmgr.PopScope();
  950.         }
  951.        
  952.         /// <summary>
  953.         /// Recursively analyze content. Return "nd" or a replacement for it.
  954.         /// </summary>
  955.         private void AnalyzeContent(QilNode nd)
  956.         {
  957.             int cntNmspSave;
  958.            
  959.             switch (nd.NodeType) {
  960.                 case QilNodeType.Loop:
  961.                     this.addInScopeNmsp = false;
  962.                     AnalyzeContent((nd as QilLoop).Body);
  963.                     break;
  964.                 case QilNodeType.Sequence:
  965.                    
  966.                     foreach (QilNode ndContent in nd)
  967.                         AnalyzeContent(ndContent);
  968.                     break;
  969.                 case QilNodeType.Conditional:
  970.                    
  971.                     this.addInScopeNmsp = false;
  972.                     AnalyzeContent((nd as QilTernary).Center);
  973.                     AnalyzeContent((nd as QilTernary).Right);
  974.                     break;
  975.                 case QilNodeType.Choice:
  976.                    
  977.                     this.addInScopeNmsp = false;
  978.                     QilList ndBranches = (nd as QilChoice).Branches;
  979.                     for (int idx = 0; idx < ndBranches.Count; idx++)
  980.                         AnalyzeContent(ndBranches[idx]);
  981.                    
  982.                     break;
  983.                 case QilNodeType.ElementCtor:
  984.                    
  985.                     // Start a new namespace scope
  986.                     this.addInScopeNmsp = true;
  987.                     this.nsmgr.PushScope();
  988.                     cntNmspSave = this.cntNmsp;
  989.                    
  990.                     if (CheckNamespaceInScope(nd as QilBinary))
  991.                         AnalyzeContent((nd as QilBinary).Right);
  992.                    
  993.                     this.nsmgr.PopScope();
  994.                     this.addInScopeNmsp = false;
  995.                     this.cntNmsp = cntNmspSave;
  996.                     break;
  997.                 case QilNodeType.AttributeCtor:
  998.                    
  999.                     this.addInScopeNmsp = false;
  1000.                     CheckNamespaceInScope(nd as QilBinary);
  1001.                     break;
  1002.                 case QilNodeType.NamespaceDecl:
  1003.                    
  1004.                     CheckNamespaceInScope(nd as QilBinary);
  1005.                     break;
  1006.                 case QilNodeType.Nop:
  1007.                    
  1008.                     AnalyzeContent((nd as QilUnary).Child);
  1009.                     break;
  1010.                 default:
  1011.                    
  1012.                     this.addInScopeNmsp = false;
  1013.                     break;
  1014.             }
  1015.         }
  1016.        
  1017.         /// <summary>
  1018.         /// Determine whether an ElementCtor, AttributeCtor, or NamespaceDecl's namespace is already declared. If it is,
  1019.         /// set the IsNamespaceInScope property to True. Otherwise, add the namespace to the set of in-scope namespaces if
  1020.         /// addInScopeNmsp is True. Return false if the name is computed or is invalid.
  1021.         /// </summary>
  1022.         private bool CheckNamespaceInScope(QilBinary nd)
  1023.         {
  1024.             QilName ndName;
  1025.             string prefix;
  1026.             string ns;
  1027.             string prefixExisting;
  1028.             string nsExisting;
  1029.             XPathNodeType nodeType;
  1030.            
  1031.             switch (nd.NodeType) {
  1032.                 case QilNodeType.ElementCtor:
  1033.                 case QilNodeType.AttributeCtor:
  1034.                     ndName = nd.Left as QilName;
  1035.                     if (ndName != null) {
  1036.                         prefix = ndName.Prefix;
  1037.                         ns = ndName.NamespaceUri;
  1038.                         nodeType = (nd.NodeType == QilNodeType.ElementCtor) ? XPathNodeType.Element : XPathNodeType.Attribute;
  1039.                         break;
  1040.                     }
  1041.                    
  1042.                     // Not a literal name, so return false
  1043.                     return false;
  1044.                 default:
  1045.                    
  1046.                     Debug.Assert(nd.NodeType == QilNodeType.NamespaceDecl);
  1047.                     prefix = (string)(QilLiteral)nd.Left;
  1048.                     ns = (string)(QilLiteral)nd.Right;
  1049.                     nodeType = XPathNodeType.Namespace;
  1050.                     break;
  1051.             }
  1052.            
  1053.             // Attribute with null namespace and xmlns:xml are always in-scope
  1054.             if (nd.NodeType == QilNodeType.AttributeCtor && ns.Length == 0 || prefix == "xml" && ns == XmlReservedNs.NsXml) {
  1055.                 XmlILConstructInfo.Write(nd).IsNamespaceInScope = true;
  1056.                 return true;
  1057.             }
  1058.            
  1059.             // Don't process names that are invalid
  1060.             if (!ValidateNames.ValidateName(prefix, string.Empty, ns, nodeType, ValidateNames.Flags.CheckPrefixMapping))
  1061.                 return false;
  1062.            
  1063.             // Atomize names
  1064.             prefix = this.nsmgr.NameTable.Add(prefix);
  1065.             ns = this.nsmgr.NameTable.Add(ns);
  1066.            
  1067.             // Determine whether namespace is already in-scope
  1068.             for (int iNmsp = 0; iNmsp < this.cntNmsp; iNmsp++) {
  1069.                 this.nsmgr.GetNamespaceDeclaration(iNmsp, out prefixExisting, out nsExisting);
  1070.                
  1071.                 // If prefix is already declared,
  1072.                 if ((object)prefix == (object)prefixExisting) {
  1073.                     // Then if the namespace is the same, this namespace is redundant
  1074.                     if ((object)ns == (object)nsExisting)
  1075.                         XmlILConstructInfo.Write(nd).IsNamespaceInScope = true;
  1076.                    
  1077.                     // Else quit searching, because any further matching prefixes will be hidden (not in-scope)
  1078.                     Debug.Assert(nd.NodeType != QilNodeType.NamespaceDecl || !this.nsmgr.HasNamespace(prefix) || this.nsmgr.LookupNamespace(prefix) == ns, "Compilers must ensure that namespace declarations do not conflict with the namespace used by the element constructor.");
  1079.                     break;
  1080.                 }
  1081.             }
  1082.            
  1083.             // If not in-scope, then add if it's allowed
  1084.             if (this.addInScopeNmsp) {
  1085.                 this.nsmgr.AddNamespace(prefix, ns);
  1086.                 this.cntNmsp++;
  1087.             }
  1088.            
  1089.             return true;
  1090.         }
  1091.     }
  1092. }

Developer Fusion