XmlILConstructAnalyzer.cs source code in C# .NET

Source code for the .NET framework in C#

                        

Code:

/ Net / Net / 3.5.50727.3053 / DEVDIV / depot / DevDiv / releases / whidbey / netfxsp / ndp / fx / src / XmlUtils / System / Xml / Xsl / IlGen / XmlILConstructAnalyzer.cs / 1 / XmlILConstructAnalyzer.cs

                            //------------------------------------------------------------------------------ 
// 
//     Copyright (c) Microsoft Corporation.  All rights reserved.
// 
// [....] 
//-----------------------------------------------------------------------------
using System; 
using System.Xml; 
using System.Xml.Schema;
using System.Xml.XPath; 
using System.Diagnostics;
using System.Collections;
using System.Collections.Generic;
using System.Collections.Specialized; 
using System.Xml.Xsl.Qil;
 
namespace System.Xml.Xsl.IlGen { 

 
    /// 
    /// Until run-time, the exact xml state cannot always be determined.  However, the construction analyzer
    /// keeps track of the set of possible xml states at each node in order to reduce run-time state management.
    ///  
    internal enum PossibleXmlStates {
        None = 0, 
        WithinSequence, 
        EnumAttrs,
        WithinContent, 
        WithinAttr,
        WithinComment,
        WithinPI,
        Any, 
    };
 
 
    /// 
    /// 1. Some expressions are lazily materialized by creating an iterator over the results (ex. LiteralString, Content). 
    /// 2. Some expressions are incrementally constructed by a Writer (ex. ElementCtor, XsltCopy).
    /// 3. Some expressions can be iterated or written (ex. List).
    /// 
    internal enum XmlILConstructMethod { 
        Iterator,               // Construct iterator over expression's results
        Writer,                 // Construct expression through calls to Writer 
        WriterThenIterator,     // Construct expression through calls to caching Writer; then construct iterator over cached results 
        IteratorThenWriter,     // Iterate over expression's results and send each item to Writer
    }; 


    /// 
    /// Every node is annotated with information about how it will be constructed by ILGen. 
    /// 
    internal class XmlILConstructInfo : IQilAnnotation { 
        private QilNodeType nodeType; 
        private PossibleXmlStates xstatesInitial, xstatesFinal, xstatesBeginLoop, xstatesEndLoop;
        private bool isNmspInScope, mightHaveNmsp, mightHaveAttrs, mightHaveDupAttrs, mightHaveNmspAfterAttrs; 
        private XmlILConstructMethod constrMeth;
        private XmlILConstructInfo parentInfo;
        private ArrayList callersInfo;
        private bool isReadOnly; 

        private static XmlILConstructInfo Default; 
 
        /// 
        /// Get ConstructInfo annotation for the specified node.  Lazily create if necessary. 
        /// 
        public static XmlILConstructInfo Read(QilNode nd) {
            XmlILAnnotation ann = nd.Annotation as XmlILAnnotation;
            XmlILConstructInfo constrInfo = (ann != null) ? ann.ConstructInfo : null; 

            if (constrInfo == null) { 
                if (Default == null) { 
                    constrInfo = new XmlILConstructInfo(QilNodeType.Unknown);
                    constrInfo.isReadOnly = true; 

                    Default = constrInfo;
                }
                else { 
                    constrInfo = Default;
                } 
            } 

            return constrInfo; 
        }

        /// 
        /// Create and initialize XmlILConstructInfo annotation for the specified node. 
        /// 
        public static XmlILConstructInfo Write(QilNode nd) { 
            XmlILAnnotation ann = XmlILAnnotation.Write(nd); 
            XmlILConstructInfo constrInfo = ann.ConstructInfo;
 
            if (constrInfo == null || constrInfo.isReadOnly) {
                constrInfo = new XmlILConstructInfo(nd.NodeType);
                ann.ConstructInfo = constrInfo;
            } 

            return constrInfo; 
        } 

        ///  
        /// Default to worst possible construction information.
        /// 
        private XmlILConstructInfo(QilNodeType nodeType) {
            this.nodeType = nodeType; 
            this.xstatesInitial = this.xstatesFinal = PossibleXmlStates.Any;
            this.xstatesBeginLoop = this.xstatesEndLoop = PossibleXmlStates.None; 
            this.isNmspInScope = false; 
            this.mightHaveNmsp = true;
            this.mightHaveAttrs = true; 
            this.mightHaveDupAttrs = true;
            this.mightHaveNmspAfterAttrs = true;
            this.constrMeth = XmlILConstructMethod.Iterator;
            this.parentInfo = null; 
        }
 
        ///  
        /// Xml states that are possible as construction of the annotated expression begins.
        ///  
        public PossibleXmlStates InitialStates {
            get { return this.xstatesInitial; }
            set {
                Debug.Assert(!this.isReadOnly, "This XmlILConstructInfo instance is read-only."); 
                this.xstatesInitial = value;
            } 
        } 

        ///  
        /// Xml states that are possible as construction of the annotated expression ends.
        /// 
        public PossibleXmlStates FinalStates {
            get { return this.xstatesFinal; } 
            set {
                Debug.Assert(!this.isReadOnly, "This XmlILConstructInfo instance is read-only."); 
                this.xstatesFinal = value; 
            }
        } 

        /// 
        /// Xml states that are possible as looping begins.  This is None if the annotated expression does not loop.
        ///  
        public PossibleXmlStates BeginLoopStates {
            //get { return this.xstatesBeginLoop; } 
            set { 
                Debug.Assert(!this.isReadOnly, "This XmlILConstructInfo instance is read-only.");
                this.xstatesBeginLoop = value; 
            }
        }

        ///  
        /// Xml states that are possible as looping ends.  This is None if the annotated expression does not loop.
        ///  
        public PossibleXmlStates EndLoopStates { 
            //get { return this.xstatesEndLoop; }
            set { 
                Debug.Assert(!this.isReadOnly, "This XmlILConstructInfo instance is read-only.");
                this.xstatesEndLoop = value;
            }
        } 

        ///  
        /// Return the method that will be used to construct the annotated node. 
        /// 
        public XmlILConstructMethod ConstructMethod { 
            get { return this.constrMeth; }
            set {
                Debug.Assert(!this.isReadOnly, "This XmlILConstructInfo instance is read-only.");
                this.constrMeth = value; 
            }
        } 
 
        /// 
        /// Returns true if construction method is Writer or WriterThenIterator. 
        /// 
        public bool PushToWriterFirst {
            get { return this.constrMeth == XmlILConstructMethod.Writer || this.constrMeth == XmlILConstructMethod.WriterThenIterator; }
            set { 
                Debug.Assert(!this.isReadOnly, "This XmlILConstructInfo instance is read-only.");
                Debug.Assert(value); 
 
                switch (this.constrMeth) {
                    case XmlILConstructMethod.Iterator: 
                        this.constrMeth = XmlILConstructMethod.WriterThenIterator;
                        break;

                    case XmlILConstructMethod.IteratorThenWriter: 
                        this.constrMeth = XmlILConstructMethod.Writer;
                        break; 
                } 
            }
        } 

        /// 
        /// Returns true if construction method is Writer or IteratorThenWriter.
        ///  
        public bool PushToWriterLast {
            get { return this.constrMeth == XmlILConstructMethod.Writer || this.constrMeth == XmlILConstructMethod.IteratorThenWriter; } 
            set { 
                Debug.Assert(!this.isReadOnly, "This XmlILConstructInfo instance is read-only.");
                Debug.Assert(value); 

                switch (this.constrMeth) {
                    case XmlILConstructMethod.Iterator:
                        this.constrMeth = XmlILConstructMethod.IteratorThenWriter; 
                        break;
 
                    case XmlILConstructMethod.WriterThenIterator: 
                        this.constrMeth = XmlILConstructMethod.Writer;
                        break; 
                }
            }
        }
 
        /// 
        /// Returns true if construction method is IteratorThenWriter or Iterator. 
        ///  
        public bool PullFromIteratorFirst {
            get { return this.constrMeth == XmlILConstructMethod.IteratorThenWriter || this.constrMeth == XmlILConstructMethod.Iterator; } 
            set {
                Debug.Assert(!this.isReadOnly, "This XmlILConstructInfo instance is read-only.");
                Debug.Assert(value);
 
                switch (this.constrMeth) {
                    case XmlILConstructMethod.Writer: 
                        this.constrMeth = XmlILConstructMethod.IteratorThenWriter; 
                        break;
 
                    case XmlILConstructMethod.WriterThenIterator:
                        this.constrMeth = XmlILConstructMethod.Iterator;
                        break;
                } 
            }
        } 
 
        /// 
        /// If the annotated expression will be constructed as the content of another constructor, and this can be 
        /// guaranteed at compile-time, then this property will be the non-null XmlILConstructInfo of that constructor.
        /// 
        public XmlILConstructInfo ParentInfo {
            //get { return this.parentInfo; } 
            set {
                Debug.Assert(!this.isReadOnly, "This XmlILConstructInfo instance is read-only."); 
                this.parentInfo = value; 
            }
        } 

        /// 
        /// If the annotated expression will be constructed as the content of an ElementCtor, and this can be
        /// guaranteed at compile-time, then this property will be the non-null XmlILConstructInfo of that constructor. 
        /// 
        public XmlILConstructInfo ParentElementInfo { 
            get { 
                if (this.parentInfo != null && this.parentInfo.nodeType == QilNodeType.ElementCtor)
                    return this.parentInfo; 

                return null;
            }
        } 

        ///  
        /// This annotation is only applicable to NamespaceDecl nodes and to ElementCtor and AttributeCtor nodes with 
        /// literal names.  If the namespace is already guaranteed to be constructed, then this property will be true.
        ///  
        public bool IsNamespaceInScope {
            get { return this.isNmspInScope; }
            set {
                Debug.Assert(!this.isReadOnly, "This XmlILConstructInfo instance is read-only."); 
                this.isNmspInScope = value;
            } 
        } 

        ///  
        /// This annotation is only applicable to ElementCtor nodes.  If the element might have local namespaces
        /// added to it at runtime, then this property will be true.
        /// 
        public bool MightHaveNamespaces { 
            get { return this.mightHaveNmsp; }
            set { 
                Debug.Assert(!this.isReadOnly, "This XmlILConstructInfo instance is read-only."); 
                this.mightHaveNmsp = value;
            } 
        }

        /// 
        /// This annotation is only applicable to ElementCtor nodes.  If the element might have namespaces added to it after 
        /// attributes have already been added, then this property will be true.
        ///  
        public bool MightHaveNamespacesAfterAttributes { 
            get { return this.mightHaveNmspAfterAttrs; }
            set { 
                Debug.Assert(!this.isReadOnly, "This XmlILConstructInfo instance is read-only.");
                this.mightHaveNmspAfterAttrs = value;
            }
        } 

        ///  
        /// This annotation is only applicable to ElementCtor nodes.  If the element might have attributes added to it at 
        /// runtime, then this property will be true.
        ///  
        public bool MightHaveAttributes {
            get { return this.mightHaveAttrs; }
            set {
                Debug.Assert(!this.isReadOnly, "This XmlILConstructInfo instance is read-only."); 
                this.mightHaveAttrs = value;
            } 
        } 

        ///  
        /// This annotation is only applicable to ElementCtor nodes.  If the element might have multiple attributes added to
        /// it with the same name, then this property will be true.
        /// 
        public bool MightHaveDuplicateAttributes { 
            get { return this.mightHaveDupAttrs; }
            set { 
                Debug.Assert(!this.isReadOnly, "This XmlILConstructInfo instance is read-only."); 
                this.mightHaveDupAttrs = value;
            } 
        }

        /// 
        /// This annotation is only applicable to Function nodes.  It contains a list of XmlILConstructInfo annontations 
        /// for all QilInvoke nodes which call the annotated function.
        ///  
        public ArrayList CallersInfo { 
            get {
                if (this.callersInfo == null) 
                    this.callersInfo = new ArrayList();

                return this.callersInfo;
            } 
        }
 
        ///  
        /// Return name of this annotation.
        ///  
        public virtual string Name {
            get { return "ConstructInfo"; }
        }
 
        /// 
        /// Return string representation of this annotation. 
        ///  
        public override string ToString() {
            string s = ""; 

            if (this.constrMeth != XmlILConstructMethod.Iterator) {
                s += this.constrMeth.ToString();
 
                s += ", " + this.xstatesInitial;
 
                if (this.xstatesBeginLoop != PossibleXmlStates.None) { 
                    s += " => " + this.xstatesBeginLoop.ToString() + " => " + this.xstatesEndLoop.ToString();
                } 

                s += " => " + this.xstatesFinal;

                if (!MightHaveAttributes) 
                    s += ", NoAttrs";
 
                if (!MightHaveDuplicateAttributes) 
                    s += ", NoDupAttrs";
 
                if (!MightHaveNamespaces)
                    s += ", NoNmsp";

                if (!MightHaveNamespacesAfterAttributes) 
                    s += ", NoNmspAfterAttrs";
            } 
 
            return s;
        } 
    }


    ///  
    /// Scans the content of an constructor and tries to minimize the number of well-formed checks that will have
    /// to be made at runtime when constructing content. 
    ///  
    internal class XmlILStateAnalyzer {
        protected XmlILConstructInfo parentInfo; 
        protected QilFactory fac;
        protected PossibleXmlStates xstates;
        protected bool withinElem;
 
        /// 
        /// Constructor. 
        ///  
        public XmlILStateAnalyzer(QilFactory fac) {
            this.fac = fac; 
        }

        /// 
        /// Perform analysis on the specified constructor and its content.  Return the ndContent that was passed in, 
        /// or a replacement.
        ///  
        public virtual QilNode Analyze(QilNode ndConstr, QilNode ndContent) { 
            if (ndConstr == null) {
                // Root expression is analyzed 
                this.parentInfo = null;
                this.xstates = PossibleXmlStates.WithinSequence;
                this.withinElem = false;
 
                Debug.Assert(ndContent != null);
                ndContent = AnalyzeContent(ndContent); 
            } 
            else {
                this.parentInfo = XmlILConstructInfo.Write(ndConstr); 

                if (ndConstr.NodeType == QilNodeType.Function) {
                    // Results of function should be pushed to writer
                    this.parentInfo.ConstructMethod = XmlILConstructMethod.Writer; 

                    // Start with PossibleXmlStates.None and then add additional possible starting states 
                    PossibleXmlStates xstates = PossibleXmlStates.None; 
                    foreach (XmlILConstructInfo infoCaller in this.parentInfo.CallersInfo) {
                        if (xstates == PossibleXmlStates.None) { 
                            xstates = infoCaller.InitialStates;
                        }
                        else if (xstates != infoCaller.InitialStates) {
                            xstates = PossibleXmlStates.Any; 
                        }
 
                        // Function's results are pushed to Writer, so make sure that Invoke nodes' construct methods match 
                        infoCaller.PushToWriterFirst = true;
                    } 
                    this.parentInfo.InitialStates = xstates;
                }
                else {
                    // Build a standalone tree, with this constructor as its root 
                    if (ndConstr.NodeType != QilNodeType.Choice)
                        this.parentInfo.InitialStates = this.parentInfo.FinalStates = PossibleXmlStates.WithinSequence; 
 
                    // Don't stream Rtf; fully cache the Rtf and copy it into any containing tree in order to simplify XmlILVisitor.VisitRtfCtor
                    if (ndConstr.NodeType != QilNodeType.RtfCtor) 
                        this.parentInfo.ConstructMethod = XmlILConstructMethod.WriterThenIterator;
                }

                // Set withinElem = true if analyzing element content 
                this.withinElem = (ndConstr.NodeType == QilNodeType.ElementCtor);
 
                switch (ndConstr.NodeType) { 
                    case QilNodeType.DocumentCtor: this.xstates = PossibleXmlStates.WithinContent; break;
                    case QilNodeType.ElementCtor: this.xstates = PossibleXmlStates.EnumAttrs; break; 
                    case QilNodeType.AttributeCtor: this.xstates = PossibleXmlStates.WithinAttr; break;
                    case QilNodeType.NamespaceDecl: Debug.Assert(ndContent == null); break;
                    case QilNodeType.TextCtor: Debug.Assert(ndContent == null); break;
                    case QilNodeType.RawTextCtor: Debug.Assert(ndContent == null); break; 
                    case QilNodeType.CommentCtor: this.xstates = PossibleXmlStates.WithinComment; break;
                    case QilNodeType.PICtor: this.xstates = PossibleXmlStates.WithinPI; break; 
                    case QilNodeType.XsltCopy: this.xstates = PossibleXmlStates.Any; break; 
                    case QilNodeType.XsltCopyOf: Debug.Assert(ndContent == null); break;
                    case QilNodeType.Function: this.xstates = this.parentInfo.InitialStates; break; 
                    case QilNodeType.RtfCtor: this.xstates = PossibleXmlStates.WithinContent; break;
                    case QilNodeType.Choice: this.xstates = PossibleXmlStates.Any; break;
                    default: Debug.Assert(false, ndConstr.NodeType + " is not handled by XmlILStateAnalyzer."); break;
                } 

                if (ndContent != null) 
                    ndContent = AnalyzeContent(ndContent); 

                if (ndConstr.NodeType == QilNodeType.Choice) 
                    AnalyzeChoice(ndConstr as QilChoice, this.parentInfo);

                // Since Function will never be another node's content, set its final states here
                if (ndConstr.NodeType == QilNodeType.Function) 
                    this.parentInfo.FinalStates = this.xstates;
            } 
 
            return ndContent;
        } 

        /// 
        /// Recursively analyze content.  Return "nd" or a replacement for it.
        ///  
        protected virtual QilNode AnalyzeContent(QilNode nd) {
            XmlILConstructInfo info; 
            QilNode ndChild; 

            // Handle special node-types that are replaced 
            switch (nd.NodeType) {
                case QilNodeType.For:
                case QilNodeType.Let:
                case QilNodeType.Parameter: 
                    // Iterator references are shared and cannot be annotated directly with ConstructInfo,
                    // so wrap them with Nop node. 
                    nd = this.fac.Nop(nd); 
                    break;
            } 

            // Get node's ConstructInfo annotation
            info = XmlILConstructInfo.Write(nd);
 
            // Set node's guaranteed parent constructor
            info.ParentInfo = this.parentInfo; 
 
            // Construct all content using the Writer
            info.PushToWriterLast = true; 

            // Set states that are possible before expression is constructed
            info.InitialStates = this.xstates;
 
            switch (nd.NodeType) {
                case QilNodeType.Loop: AnalyzeLoop(nd as QilLoop, info); break; 
                case QilNodeType.Sequence: AnalyzeSequence(nd as QilList, info); break; 
                case QilNodeType.Conditional: AnalyzeConditional(nd as QilTernary, info); break;
                case QilNodeType.Choice: AnalyzeChoice(nd as QilChoice, info); break; 

                case QilNodeType.Error:
                case QilNodeType.Warning:
                    // Ensure that construct method is Writer 
                    info.ConstructMethod = XmlILConstructMethod.Writer;
                    break; 
 
                case QilNodeType.Nop:
                    ndChild = (nd as QilUnary).Child; 
                    switch (ndChild.NodeType) {
                        case QilNodeType.For:
                        case QilNodeType.Let:
                        case QilNodeType.Parameter: 
                            // Copy iterator items as content
                            AnalyzeCopy(nd, info); 
                            break; 

                        default: 
                            // Ensure that construct method is Writer and recursively analyze content
                            info.ConstructMethod = XmlILConstructMethod.Writer;
                            AnalyzeContent(ndChild);
                            break; 
                    }
                    break; 
 
                default:
                    AnalyzeCopy(nd, info); 
                    break;
            }

            // Set states that are possible after expression is constructed 
            info.FinalStates = this.xstates;
 
            return nd; 
        }
 
        /// 
        /// Analyze loop.
        /// 
        protected virtual void AnalyzeLoop(QilLoop ndLoop, XmlILConstructInfo info) { 
            XmlQueryType typ = ndLoop.XmlType;
 
            // Ensure that construct method is Writer 
            info.ConstructMethod = XmlILConstructMethod.Writer;
 
            if (!typ.IsSingleton)
                StartLoop(typ, info);

            // Body constructs content 
            ndLoop.Body = AnalyzeContent(ndLoop.Body);
 
            if (!typ.IsSingleton) 
                EndLoop(typ, info);
        } 

        /// 
        /// Analyze list.
        ///  
        protected virtual void AnalyzeSequence(QilList ndSeq, XmlILConstructInfo info) {
            // Ensure that construct method is Writer 
            info.ConstructMethod = XmlILConstructMethod.Writer; 

            // Analyze each item in the list 
            for (int idx = 0; idx < ndSeq.Count; idx++)
                ndSeq[idx] = AnalyzeContent(ndSeq[idx]);
        }
 
        /// 
        /// Analyze conditional. 
        ///  
        protected virtual void AnalyzeConditional(QilTernary ndCond, XmlILConstructInfo info) {
            PossibleXmlStates xstatesTrue; 

            // Ensure that construct method is Writer
            info.ConstructMethod = XmlILConstructMethod.Writer;
 
            // Visit true branch; save resulting states
            ndCond.Center = AnalyzeContent(ndCond.Center); 
            xstatesTrue = this.xstates; 

            // Restore starting states and visit false branch 
            this.xstates = info.InitialStates;
            ndCond.Right = AnalyzeContent(ndCond.Right);

            // Conditional ending states consist of combination of true and false branch states 
            if (xstatesTrue != this.xstates)
                this.xstates = PossibleXmlStates.Any; 
        } 

        ///  
        /// Analyze choice.
        /// 
        protected virtual void AnalyzeChoice(QilChoice ndChoice, XmlILConstructInfo info) {
            PossibleXmlStates xstatesChoice; 
            int idx;
 
            // Visit default branch; save resulting states 
            idx = ndChoice.Branches.Count - 1;
            ndChoice.Branches[idx] = AnalyzeContent(ndChoice.Branches[idx]); 
            xstatesChoice = this.xstates;

            // Visit all other branches
            while (--idx >= 0) { 
                // Restore starting states and visit the next branch
                this.xstates = info.InitialStates; 
                ndChoice.Branches[idx] = AnalyzeContent(ndChoice.Branches[idx]); 

                // Choice ending states consist of combination of all branch states 
                if (xstatesChoice != this.xstates)
                    xstatesChoice = PossibleXmlStates.Any;
            }
 
            this.xstates = xstatesChoice;
        } 
 
        /// 
        /// Analyze copying items. 
        /// 
        protected virtual void AnalyzeCopy(QilNode ndCopy, XmlILConstructInfo info) {
            XmlQueryType typ = ndCopy.XmlType;
 
            // Copying item(s) to output involves looping if there is not exactly one item in the sequence
            if (!typ.IsSingleton) 
                StartLoop(typ, info); 

            // Determine state transitions that may take place 
            if (MaybeContent(typ)) {
                if (MaybeAttrNmsp(typ)) {
                    // Node might be Attr/Nmsp or non-Attr/Nmsp, so transition from EnumAttrs to WithinContent *may* occur
                    if (this.xstates == PossibleXmlStates.EnumAttrs) 
                        this.xstates = PossibleXmlStates.Any;
                } 
                else { 
                    // Node is guaranteed not to be Attr/Nmsp, so transition to WithinContent will occur if starting
                    // state is EnumAttrs or if constructing within an element (guaranteed to be in EnumAttrs or WithinContent state) 
                    if (this.xstates == PossibleXmlStates.EnumAttrs || this.withinElem)
                        this.xstates = PossibleXmlStates.WithinContent;
                }
            } 

            if (!typ.IsSingleton) 
                EndLoop(typ, info); 
        }
 
        /// 
        /// Calculate starting xml states that will result when iterating over and constructing an expression of the specified type.
        /// 
        private void StartLoop(XmlQueryType typ, XmlILConstructInfo info) { 
            Debug.Assert(!typ.IsSingleton);
 
            // This is tricky, because the looping introduces a feedback loop: 
            //   1. Because loops may be executed many times, the beginning set of states must include the ending set of states.
            //   2. Because loops may be executed 0 times, the final set of states after all looping is complete must include 
            //      the initial set of states.
            //
            // +-- states-initial
            // |         | 
            // | states-begin-loop <--+
            // |         |            | 
            // |  +--------------+    | 
            // |  | Construction |    |
            // |  +--------------+    | 
            // |         |            |
            // |  states-end-loop ----+
            // |         |
            // +--> states-final 

            // Save starting loop states 
            info.BeginLoopStates = this.xstates; 

            if (typ.MaybeMany) { 
                // If transition might occur from EnumAttrs to WithinContent, then states-end might be WithinContent, which
                // means states-begin needs to also include WithinContent.
                if (this.xstates == PossibleXmlStates.EnumAttrs && MaybeContent(typ))
                    info.BeginLoopStates = this.xstates = PossibleXmlStates.Any; 
            }
        } 
 
        /// 
        /// Calculate ending xml states that will result when iterating over and constructing an expression of the specified type. 
        /// 
        private void EndLoop(XmlQueryType typ, XmlILConstructInfo info) {
            Debug.Assert(!typ.IsSingleton);
 
            // Save ending loop states
            info.EndLoopStates = this.xstates; 
 
            // If it's possible to loop zero times, then states-final needs to include states-initial
            if (typ.MaybeEmpty && info.InitialStates != this.xstates) 
                this.xstates = PossibleXmlStates.Any;
        }

        ///  
        /// Return true if an instance of the specified type might be an attribute or a namespace node.
        ///  
        private bool MaybeAttrNmsp(XmlQueryType typ) { 
            return (typ.NodeKinds & (XmlNodeKindFlags.Attribute | XmlNodeKindFlags.Namespace)) != XmlNodeKindFlags.None;
        } 

        /// 
        /// Return true if an instance of the specified type might be a non-empty content type (attr/nsmp don't count).
        ///  
        private bool MaybeContent(XmlQueryType typ) {
            return !typ.IsNode || (typ.NodeKinds & ~(XmlNodeKindFlags.Attribute | XmlNodeKindFlags.Namespace)) != XmlNodeKindFlags.None; 
        } 
    }
 

    /// 
    /// Scans the content of an ElementCtor and tries to minimize the number of well-formed checks that will have
    /// to be made at runtime when constructing content. 
    /// 
    internal class XmlILElementAnalyzer : XmlILStateAnalyzer { 
        private NameTable attrNames = new NameTable(); 
        private ArrayList dupAttrs = new ArrayList();
 
        /// 
        /// Constructor.
        /// 
        public XmlILElementAnalyzer(QilFactory fac) : base(fac) { 
        }
 
        ///  
        /// Analyze the content argument of the ElementCtor.  Try to eliminate as many runtime checks as possible,
        /// both for the ElementCtor and for content constructors. 
        /// 
        public override QilNode Analyze(QilNode ndElem, QilNode ndContent) {
            Debug.Assert(ndElem.NodeType == QilNodeType.ElementCtor);
            this.parentInfo = XmlILConstructInfo.Write(ndElem); 

            // Start by assuming that these properties are false (they default to true, but analyzer might be able to 
            // prove they are really false). 
            this.parentInfo.MightHaveNamespacesAfterAttributes = false;
            this.parentInfo.MightHaveAttributes = false; 
            this.parentInfo.MightHaveDuplicateAttributes = false;

            // The element's namespace might need to be declared
            this.parentInfo.MightHaveNamespaces = !this.parentInfo.IsNamespaceInScope; 

            // Clear list of duplicate attributes 
            this.dupAttrs.Clear(); 

            return base.Analyze(ndElem, ndContent); 
        }

        /// 
        /// Analyze loop. 
        /// 
        protected override void AnalyzeLoop(QilLoop ndLoop, XmlILConstructInfo info) { 
            // Constructing attributes/namespaces in a loop can cause duplicates, namespaces after attributes, etc. 
            if (ndLoop.XmlType.MaybeMany)
                CheckAttributeNamespaceConstruct(ndLoop.XmlType); 

            base.AnalyzeLoop(ndLoop, info);
        }
 
        /// 
        /// Analyze copying items. 
        ///  
        protected override void AnalyzeCopy(QilNode ndCopy, XmlILConstructInfo info) {
            if (ndCopy.NodeType == QilNodeType.AttributeCtor) { 
                AnalyzeAttributeCtor(ndCopy as QilBinary, info);
            }
            else {
                CheckAttributeNamespaceConstruct(ndCopy.XmlType); 
            }
 
            base.AnalyzeCopy(ndCopy, info); 
        }
 
        /// 
        /// Analyze attribute constructor.
        /// 
        private void AnalyzeAttributeCtor(QilBinary ndAttr, XmlILConstructInfo info) { 
            if (ndAttr.Left.NodeType == QilNodeType.LiteralQName) {
                QilName ndName = ndAttr.Left as QilName; 
                XmlQualifiedName qname; 
                int idx;
 
                // This attribute might be constructed on the parent element
                this.parentInfo.MightHaveAttributes = true;

                // Check to see whether this attribute is a duplicate of a previous attribute 
                if (!this.parentInfo.MightHaveDuplicateAttributes) {
                    qname = new XmlQualifiedName(this.attrNames.Add(ndName.LocalName), this.attrNames.Add(ndName.NamespaceUri)); 
 
                    for (idx = 0; idx < this.dupAttrs.Count; idx++) {
                        XmlQualifiedName qnameDup = (XmlQualifiedName) this.dupAttrs[idx]; 

                        if ((object) qnameDup.Name == (object) qname.Name && (object) qnameDup.Namespace == (object) qname.Namespace) {
                            // A duplicate attribute has been encountered
                            this.parentInfo.MightHaveDuplicateAttributes = true; 
                        }
                    } 
 
                    if (idx >= this.dupAttrs.Count) {
                        // This is not a duplicate attribute, so add it to the set 
                        this.dupAttrs.Add(qname);
                    }
                }
 
                // The attribute's namespace might need to be declared
                if (!info.IsNamespaceInScope) 
                    this.parentInfo.MightHaveNamespaces = true; 
            }
            else { 
                // Attribute prefix and namespace are not known at compile-time
                CheckAttributeNamespaceConstruct(ndAttr.XmlType);
            }
        } 

        ///  
        /// If type might contain attributes or namespaces, set appropriate parent element flags. 
        /// 
        private void CheckAttributeNamespaceConstruct(XmlQueryType typ) { 
            // If content might contain attributes,
            if ((typ.NodeKinds & XmlNodeKindFlags.Attribute) != XmlNodeKindFlags.None) {
                // Mark element as possibly having attributes and duplicate attributes (since we don't know the names)
                this.parentInfo.MightHaveAttributes = true; 
                this.parentInfo.MightHaveDuplicateAttributes = true;
 
                // Attribute namespaces might be declared 
                this.parentInfo.MightHaveNamespaces = true;
            } 

            // If content might contain namespaces,
            if ((typ.NodeKinds & XmlNodeKindFlags.Namespace) != XmlNodeKindFlags.None) {
                // Then element might have namespaces, 
                this.parentInfo.MightHaveNamespaces = true;
 
                // If attributes might already have been constructed, 
                if (this.parentInfo.MightHaveAttributes) {
                    // Then attributes might precede namespace declarations 
                    this.parentInfo.MightHaveNamespacesAfterAttributes = true;
                }
            }
        } 
    }
 
 
    /// 
    /// Scans constructed content, looking for redundant namespace declarations.  If any are found, then they are marked 
    /// and removed later.
    /// 
    internal class XmlILNamespaceAnalyzer {
        private XmlNamespaceManager nsmgr = new XmlNamespaceManager(new NameTable()); 
        private bool addInScopeNmsp;
        private int cntNmsp; 
 
        /// 
        /// Perform scan. 
        /// 
        public void Analyze(QilNode nd, bool defaultNmspInScope) {
            this.addInScopeNmsp = false;
            this.cntNmsp = 0; 

            // If xmlns="" is in-scope, push it onto the namespace stack 
            if (defaultNmspInScope) { 
                this.nsmgr.PushScope();
                this.nsmgr.AddNamespace(string.Empty, string.Empty); 
                this.cntNmsp++;
            }

            AnalyzeContent(nd); 

            if (defaultNmspInScope) 
                this.nsmgr.PopScope(); 
        }
 
        /// 
        /// Recursively analyze content.  Return "nd" or a replacement for it.
        /// 
        private void AnalyzeContent(QilNode nd) { 
            int cntNmspSave;
 
            switch (nd.NodeType) { 
                case QilNodeType.Loop:
                    this.addInScopeNmsp = false; 
                    AnalyzeContent((nd as QilLoop).Body);
                    break;

                case QilNodeType.Sequence: 
                    foreach (QilNode ndContent in nd)
                        AnalyzeContent(ndContent); 
                    break; 

                case QilNodeType.Conditional: 
                    this.addInScopeNmsp = false;
                    AnalyzeContent((nd as QilTernary).Center);
                    AnalyzeContent((nd as QilTernary).Right);
                    break; 

                case QilNodeType.Choice: 
                    this.addInScopeNmsp = false; 
                    QilList ndBranches = (nd as QilChoice).Branches;
                    for (int idx = 0; idx < ndBranches.Count; idx++) 
                        AnalyzeContent(ndBranches[idx]);

                    break;
 
                case QilNodeType.ElementCtor:
                    // Start a new namespace scope 
                    this.addInScopeNmsp = true; 
                    this.nsmgr.PushScope();
                    cntNmspSave = this.cntNmsp; 

                    if (CheckNamespaceInScope(nd as QilBinary))
                        AnalyzeContent((nd as QilBinary).Right);
 
                    this.nsmgr.PopScope();
                    this.addInScopeNmsp = false; 
                    this.cntNmsp = cntNmspSave; 
                    break;
 
                case QilNodeType.AttributeCtor:
                    this.addInScopeNmsp = false;
                    CheckNamespaceInScope(nd as QilBinary);
                    break; 

                case QilNodeType.NamespaceDecl: 
                    CheckNamespaceInScope(nd as QilBinary); 
                    break;
 
                case QilNodeType.Nop:
                    AnalyzeContent((nd as QilUnary).Child);
                    break;
 
                default:
                    this.addInScopeNmsp = false; 
                    break; 
            }
        } 

        /// 
        /// Determine whether an ElementCtor, AttributeCtor, or NamespaceDecl's namespace is already declared.  If it is,
        /// set the IsNamespaceInScope property to True.  Otherwise, add the namespace to the set of in-scope namespaces if 
        /// addInScopeNmsp is True.  Return false if the name is computed or is invalid.
        ///  
        private bool CheckNamespaceInScope(QilBinary nd) { 
            QilName ndName;
            string prefix, ns, prefixExisting, nsExisting; 
            XPathNodeType nodeType;

            switch (nd.NodeType) {
                case QilNodeType.ElementCtor: 
                case QilNodeType.AttributeCtor:
                    ndName = nd.Left as QilName; 
                    if (ndName != null) { 
                        prefix = ndName.Prefix;
                        ns = ndName.NamespaceUri; 
                        nodeType = (nd.NodeType == QilNodeType.ElementCtor) ? XPathNodeType.Element : XPathNodeType.Attribute;
                        break;
                    }
 
                    // Not a literal name, so return false
                    return false; 
 
                default:
                    Debug.Assert(nd.NodeType == QilNodeType.NamespaceDecl); 
                    prefix = (string) (QilLiteral) nd.Left;
                    ns = (string) (QilLiteral) nd.Right;
                    nodeType = XPathNodeType.Namespace;
                    break; 
            }
 
            // Attribute with null namespace and xmlns:xml are always in-scope 
            if (nd.NodeType == QilNodeType.AttributeCtor && ns.Length == 0 ||
                prefix == "xml" && ns == XmlReservedNs.NsXml) { 
                XmlILConstructInfo.Write(nd).IsNamespaceInScope = true;
                return true;
            }
 
            // Don't process names that are invalid
            if (!ValidateNames.ValidateName(prefix, string.Empty, ns, nodeType, ValidateNames.Flags.CheckPrefixMapping)) 
                return false; 

            // Atomize names 
            prefix = this.nsmgr.NameTable.Add(prefix);
            ns = this.nsmgr.NameTable.Add(ns);

            // Determine whether namespace is already in-scope 
            for (int iNmsp = 0; iNmsp < this.cntNmsp; iNmsp++) {
                this.nsmgr.GetNamespaceDeclaration(iNmsp, out prefixExisting, out nsExisting); 
 
                // If prefix is already declared,
                if ((object) prefix == (object) prefixExisting) { 
                    // Then if the namespace is the same, this namespace is redundant
                    if ((object) ns == (object) nsExisting)
                        XmlILConstructInfo.Write(nd).IsNamespaceInScope = true;
 
                    // Else quit searching, because any further matching prefixes will be hidden (not in-scope)
                    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."); 
                    break;
                } 
            }

            // If not in-scope, then add if it's allowed
            if (this.addInScopeNmsp) { 
                this.nsmgr.AddNamespace(prefix, ns);
                this.cntNmsp++; 
            } 

            return true; 
        }
    }
}

// File provided for Reference Use Only by Microsoft Corporation (c) 2007.
//------------------------------------------------------------------------------ 
// 
//     Copyright (c) Microsoft Corporation.  All rights reserved.
// 
// [....] 
//-----------------------------------------------------------------------------
using System; 
using System.Xml; 
using System.Xml.Schema;
using System.Xml.XPath; 
using System.Diagnostics;
using System.Collections;
using System.Collections.Generic;
using System.Collections.Specialized; 
using System.Xml.Xsl.Qil;
 
namespace System.Xml.Xsl.IlGen { 

 
    /// 
    /// Until run-time, the exact xml state cannot always be determined.  However, the construction analyzer
    /// keeps track of the set of possible xml states at each node in order to reduce run-time state management.
    ///  
    internal enum PossibleXmlStates {
        None = 0, 
        WithinSequence, 
        EnumAttrs,
        WithinContent, 
        WithinAttr,
        WithinComment,
        WithinPI,
        Any, 
    };
 
 
    /// 
    /// 1. Some expressions are lazily materialized by creating an iterator over the results (ex. LiteralString, Content). 
    /// 2. Some expressions are incrementally constructed by a Writer (ex. ElementCtor, XsltCopy).
    /// 3. Some expressions can be iterated or written (ex. List).
    /// 
    internal enum XmlILConstructMethod { 
        Iterator,               // Construct iterator over expression's results
        Writer,                 // Construct expression through calls to Writer 
        WriterThenIterator,     // Construct expression through calls to caching Writer; then construct iterator over cached results 
        IteratorThenWriter,     // Iterate over expression's results and send each item to Writer
    }; 


    /// 
    /// Every node is annotated with information about how it will be constructed by ILGen. 
    /// 
    internal class XmlILConstructInfo : IQilAnnotation { 
        private QilNodeType nodeType; 
        private PossibleXmlStates xstatesInitial, xstatesFinal, xstatesBeginLoop, xstatesEndLoop;
        private bool isNmspInScope, mightHaveNmsp, mightHaveAttrs, mightHaveDupAttrs, mightHaveNmspAfterAttrs; 
        private XmlILConstructMethod constrMeth;
        private XmlILConstructInfo parentInfo;
        private ArrayList callersInfo;
        private bool isReadOnly; 

        private static XmlILConstructInfo Default; 
 
        /// 
        /// Get ConstructInfo annotation for the specified node.  Lazily create if necessary. 
        /// 
        public static XmlILConstructInfo Read(QilNode nd) {
            XmlILAnnotation ann = nd.Annotation as XmlILAnnotation;
            XmlILConstructInfo constrInfo = (ann != null) ? ann.ConstructInfo : null; 

            if (constrInfo == null) { 
                if (Default == null) { 
                    constrInfo = new XmlILConstructInfo(QilNodeType.Unknown);
                    constrInfo.isReadOnly = true; 

                    Default = constrInfo;
                }
                else { 
                    constrInfo = Default;
                } 
            } 

            return constrInfo; 
        }

        /// 
        /// Create and initialize XmlILConstructInfo annotation for the specified node. 
        /// 
        public static XmlILConstructInfo Write(QilNode nd) { 
            XmlILAnnotation ann = XmlILAnnotation.Write(nd); 
            XmlILConstructInfo constrInfo = ann.ConstructInfo;
 
            if (constrInfo == null || constrInfo.isReadOnly) {
                constrInfo = new XmlILConstructInfo(nd.NodeType);
                ann.ConstructInfo = constrInfo;
            } 

            return constrInfo; 
        } 

        ///  
        /// Default to worst possible construction information.
        /// 
        private XmlILConstructInfo(QilNodeType nodeType) {
            this.nodeType = nodeType; 
            this.xstatesInitial = this.xstatesFinal = PossibleXmlStates.Any;
            this.xstatesBeginLoop = this.xstatesEndLoop = PossibleXmlStates.None; 
            this.isNmspInScope = false; 
            this.mightHaveNmsp = true;
            this.mightHaveAttrs = true; 
            this.mightHaveDupAttrs = true;
            this.mightHaveNmspAfterAttrs = true;
            this.constrMeth = XmlILConstructMethod.Iterator;
            this.parentInfo = null; 
        }
 
        ///  
        /// Xml states that are possible as construction of the annotated expression begins.
        ///  
        public PossibleXmlStates InitialStates {
            get { return this.xstatesInitial; }
            set {
                Debug.Assert(!this.isReadOnly, "This XmlILConstructInfo instance is read-only."); 
                this.xstatesInitial = value;
            } 
        } 

        ///  
        /// Xml states that are possible as construction of the annotated expression ends.
        /// 
        public PossibleXmlStates FinalStates {
            get { return this.xstatesFinal; } 
            set {
                Debug.Assert(!this.isReadOnly, "This XmlILConstructInfo instance is read-only."); 
                this.xstatesFinal = value; 
            }
        } 

        /// 
        /// Xml states that are possible as looping begins.  This is None if the annotated expression does not loop.
        ///  
        public PossibleXmlStates BeginLoopStates {
            //get { return this.xstatesBeginLoop; } 
            set { 
                Debug.Assert(!this.isReadOnly, "This XmlILConstructInfo instance is read-only.");
                this.xstatesBeginLoop = value; 
            }
        }

        ///  
        /// Xml states that are possible as looping ends.  This is None if the annotated expression does not loop.
        ///  
        public PossibleXmlStates EndLoopStates { 
            //get { return this.xstatesEndLoop; }
            set { 
                Debug.Assert(!this.isReadOnly, "This XmlILConstructInfo instance is read-only.");
                this.xstatesEndLoop = value;
            }
        } 

        ///  
        /// Return the method that will be used to construct the annotated node. 
        /// 
        public XmlILConstructMethod ConstructMethod { 
            get { return this.constrMeth; }
            set {
                Debug.Assert(!this.isReadOnly, "This XmlILConstructInfo instance is read-only.");
                this.constrMeth = value; 
            }
        } 
 
        /// 
        /// Returns true if construction method is Writer or WriterThenIterator. 
        /// 
        public bool PushToWriterFirst {
            get { return this.constrMeth == XmlILConstructMethod.Writer || this.constrMeth == XmlILConstructMethod.WriterThenIterator; }
            set { 
                Debug.Assert(!this.isReadOnly, "This XmlILConstructInfo instance is read-only.");
                Debug.Assert(value); 
 
                switch (this.constrMeth) {
                    case XmlILConstructMethod.Iterator: 
                        this.constrMeth = XmlILConstructMethod.WriterThenIterator;
                        break;

                    case XmlILConstructMethod.IteratorThenWriter: 
                        this.constrMeth = XmlILConstructMethod.Writer;
                        break; 
                } 
            }
        } 

        /// 
        /// Returns true if construction method is Writer or IteratorThenWriter.
        ///  
        public bool PushToWriterLast {
            get { return this.constrMeth == XmlILConstructMethod.Writer || this.constrMeth == XmlILConstructMethod.IteratorThenWriter; } 
            set { 
                Debug.Assert(!this.isReadOnly, "This XmlILConstructInfo instance is read-only.");
                Debug.Assert(value); 

                switch (this.constrMeth) {
                    case XmlILConstructMethod.Iterator:
                        this.constrMeth = XmlILConstructMethod.IteratorThenWriter; 
                        break;
 
                    case XmlILConstructMethod.WriterThenIterator: 
                        this.constrMeth = XmlILConstructMethod.Writer;
                        break; 
                }
            }
        }
 
        /// 
        /// Returns true if construction method is IteratorThenWriter or Iterator. 
        ///  
        public bool PullFromIteratorFirst {
            get { return this.constrMeth == XmlILConstructMethod.IteratorThenWriter || this.constrMeth == XmlILConstructMethod.Iterator; } 
            set {
                Debug.Assert(!this.isReadOnly, "This XmlILConstructInfo instance is read-only.");
                Debug.Assert(value);
 
                switch (this.constrMeth) {
                    case XmlILConstructMethod.Writer: 
                        this.constrMeth = XmlILConstructMethod.IteratorThenWriter; 
                        break;
 
                    case XmlILConstructMethod.WriterThenIterator:
                        this.constrMeth = XmlILConstructMethod.Iterator;
                        break;
                } 
            }
        } 
 
        /// 
        /// If the annotated expression will be constructed as the content of another constructor, and this can be 
        /// guaranteed at compile-time, then this property will be the non-null XmlILConstructInfo of that constructor.
        /// 
        public XmlILConstructInfo ParentInfo {
            //get { return this.parentInfo; } 
            set {
                Debug.Assert(!this.isReadOnly, "This XmlILConstructInfo instance is read-only."); 
                this.parentInfo = value; 
            }
        } 

        /// 
        /// If the annotated expression will be constructed as the content of an ElementCtor, and this can be
        /// guaranteed at compile-time, then this property will be the non-null XmlILConstructInfo of that constructor. 
        /// 
        public XmlILConstructInfo ParentElementInfo { 
            get { 
                if (this.parentInfo != null && this.parentInfo.nodeType == QilNodeType.ElementCtor)
                    return this.parentInfo; 

                return null;
            }
        } 

        ///  
        /// This annotation is only applicable to NamespaceDecl nodes and to ElementCtor and AttributeCtor nodes with 
        /// literal names.  If the namespace is already guaranteed to be constructed, then this property will be true.
        ///  
        public bool IsNamespaceInScope {
            get { return this.isNmspInScope; }
            set {
                Debug.Assert(!this.isReadOnly, "This XmlILConstructInfo instance is read-only."); 
                this.isNmspInScope = value;
            } 
        } 

        ///  
        /// This annotation is only applicable to ElementCtor nodes.  If the element might have local namespaces
        /// added to it at runtime, then this property will be true.
        /// 
        public bool MightHaveNamespaces { 
            get { return this.mightHaveNmsp; }
            set { 
                Debug.Assert(!this.isReadOnly, "This XmlILConstructInfo instance is read-only."); 
                this.mightHaveNmsp = value;
            } 
        }

        /// 
        /// This annotation is only applicable to ElementCtor nodes.  If the element might have namespaces added to it after 
        /// attributes have already been added, then this property will be true.
        ///  
        public bool MightHaveNamespacesAfterAttributes { 
            get { return this.mightHaveNmspAfterAttrs; }
            set { 
                Debug.Assert(!this.isReadOnly, "This XmlILConstructInfo instance is read-only.");
                this.mightHaveNmspAfterAttrs = value;
            }
        } 

        ///  
        /// This annotation is only applicable to ElementCtor nodes.  If the element might have attributes added to it at 
        /// runtime, then this property will be true.
        ///  
        public bool MightHaveAttributes {
            get { return this.mightHaveAttrs; }
            set {
                Debug.Assert(!this.isReadOnly, "This XmlILConstructInfo instance is read-only."); 
                this.mightHaveAttrs = value;
            } 
        } 

        ///  
        /// This annotation is only applicable to ElementCtor nodes.  If the element might have multiple attributes added to
        /// it with the same name, then this property will be true.
        /// 
        public bool MightHaveDuplicateAttributes { 
            get { return this.mightHaveDupAttrs; }
            set { 
                Debug.Assert(!this.isReadOnly, "This XmlILConstructInfo instance is read-only."); 
                this.mightHaveDupAttrs = value;
            } 
        }

        /// 
        /// This annotation is only applicable to Function nodes.  It contains a list of XmlILConstructInfo annontations 
        /// for all QilInvoke nodes which call the annotated function.
        ///  
        public ArrayList CallersInfo { 
            get {
                if (this.callersInfo == null) 
                    this.callersInfo = new ArrayList();

                return this.callersInfo;
            } 
        }
 
        ///  
        /// Return name of this annotation.
        ///  
        public virtual string Name {
            get { return "ConstructInfo"; }
        }
 
        /// 
        /// Return string representation of this annotation. 
        ///  
        public override string ToString() {
            string s = ""; 

            if (this.constrMeth != XmlILConstructMethod.Iterator) {
                s += this.constrMeth.ToString();
 
                s += ", " + this.xstatesInitial;
 
                if (this.xstatesBeginLoop != PossibleXmlStates.None) { 
                    s += " => " + this.xstatesBeginLoop.ToString() + " => " + this.xstatesEndLoop.ToString();
                } 

                s += " => " + this.xstatesFinal;

                if (!MightHaveAttributes) 
                    s += ", NoAttrs";
 
                if (!MightHaveDuplicateAttributes) 
                    s += ", NoDupAttrs";
 
                if (!MightHaveNamespaces)
                    s += ", NoNmsp";

                if (!MightHaveNamespacesAfterAttributes) 
                    s += ", NoNmspAfterAttrs";
            } 
 
            return s;
        } 
    }


    ///  
    /// Scans the content of an constructor and tries to minimize the number of well-formed checks that will have
    /// to be made at runtime when constructing content. 
    ///  
    internal class XmlILStateAnalyzer {
        protected XmlILConstructInfo parentInfo; 
        protected QilFactory fac;
        protected PossibleXmlStates xstates;
        protected bool withinElem;
 
        /// 
        /// Constructor. 
        ///  
        public XmlILStateAnalyzer(QilFactory fac) {
            this.fac = fac; 
        }

        /// 
        /// Perform analysis on the specified constructor and its content.  Return the ndContent that was passed in, 
        /// or a replacement.
        ///  
        public virtual QilNode Analyze(QilNode ndConstr, QilNode ndContent) { 
            if (ndConstr == null) {
                // Root expression is analyzed 
                this.parentInfo = null;
                this.xstates = PossibleXmlStates.WithinSequence;
                this.withinElem = false;
 
                Debug.Assert(ndContent != null);
                ndContent = AnalyzeContent(ndContent); 
            } 
            else {
                this.parentInfo = XmlILConstructInfo.Write(ndConstr); 

                if (ndConstr.NodeType == QilNodeType.Function) {
                    // Results of function should be pushed to writer
                    this.parentInfo.ConstructMethod = XmlILConstructMethod.Writer; 

                    // Start with PossibleXmlStates.None and then add additional possible starting states 
                    PossibleXmlStates xstates = PossibleXmlStates.None; 
                    foreach (XmlILConstructInfo infoCaller in this.parentInfo.CallersInfo) {
                        if (xstates == PossibleXmlStates.None) { 
                            xstates = infoCaller.InitialStates;
                        }
                        else if (xstates != infoCaller.InitialStates) {
                            xstates = PossibleXmlStates.Any; 
                        }
 
                        // Function's results are pushed to Writer, so make sure that Invoke nodes' construct methods match 
                        infoCaller.PushToWriterFirst = true;
                    } 
                    this.parentInfo.InitialStates = xstates;
                }
                else {
                    // Build a standalone tree, with this constructor as its root 
                    if (ndConstr.NodeType != QilNodeType.Choice)
                        this.parentInfo.InitialStates = this.parentInfo.FinalStates = PossibleXmlStates.WithinSequence; 
 
                    // Don't stream Rtf; fully cache the Rtf and copy it into any containing tree in order to simplify XmlILVisitor.VisitRtfCtor
                    if (ndConstr.NodeType != QilNodeType.RtfCtor) 
                        this.parentInfo.ConstructMethod = XmlILConstructMethod.WriterThenIterator;
                }

                // Set withinElem = true if analyzing element content 
                this.withinElem = (ndConstr.NodeType == QilNodeType.ElementCtor);
 
                switch (ndConstr.NodeType) { 
                    case QilNodeType.DocumentCtor: this.xstates = PossibleXmlStates.WithinContent; break;
                    case QilNodeType.ElementCtor: this.xstates = PossibleXmlStates.EnumAttrs; break; 
                    case QilNodeType.AttributeCtor: this.xstates = PossibleXmlStates.WithinAttr; break;
                    case QilNodeType.NamespaceDecl: Debug.Assert(ndContent == null); break;
                    case QilNodeType.TextCtor: Debug.Assert(ndContent == null); break;
                    case QilNodeType.RawTextCtor: Debug.Assert(ndContent == null); break; 
                    case QilNodeType.CommentCtor: this.xstates = PossibleXmlStates.WithinComment; break;
                    case QilNodeType.PICtor: this.xstates = PossibleXmlStates.WithinPI; break; 
                    case QilNodeType.XsltCopy: this.xstates = PossibleXmlStates.Any; break; 
                    case QilNodeType.XsltCopyOf: Debug.Assert(ndContent == null); break;
                    case QilNodeType.Function: this.xstates = this.parentInfo.InitialStates; break; 
                    case QilNodeType.RtfCtor: this.xstates = PossibleXmlStates.WithinContent; break;
                    case QilNodeType.Choice: this.xstates = PossibleXmlStates.Any; break;
                    default: Debug.Assert(false, ndConstr.NodeType + " is not handled by XmlILStateAnalyzer."); break;
                } 

                if (ndContent != null) 
                    ndContent = AnalyzeContent(ndContent); 

                if (ndConstr.NodeType == QilNodeType.Choice) 
                    AnalyzeChoice(ndConstr as QilChoice, this.parentInfo);

                // Since Function will never be another node's content, set its final states here
                if (ndConstr.NodeType == QilNodeType.Function) 
                    this.parentInfo.FinalStates = this.xstates;
            } 
 
            return ndContent;
        } 

        /// 
        /// Recursively analyze content.  Return "nd" or a replacement for it.
        ///  
        protected virtual QilNode AnalyzeContent(QilNode nd) {
            XmlILConstructInfo info; 
            QilNode ndChild; 

            // Handle special node-types that are replaced 
            switch (nd.NodeType) {
                case QilNodeType.For:
                case QilNodeType.Let:
                case QilNodeType.Parameter: 
                    // Iterator references are shared and cannot be annotated directly with ConstructInfo,
                    // so wrap them with Nop node. 
                    nd = this.fac.Nop(nd); 
                    break;
            } 

            // Get node's ConstructInfo annotation
            info = XmlILConstructInfo.Write(nd);
 
            // Set node's guaranteed parent constructor
            info.ParentInfo = this.parentInfo; 
 
            // Construct all content using the Writer
            info.PushToWriterLast = true; 

            // Set states that are possible before expression is constructed
            info.InitialStates = this.xstates;
 
            switch (nd.NodeType) {
                case QilNodeType.Loop: AnalyzeLoop(nd as QilLoop, info); break; 
                case QilNodeType.Sequence: AnalyzeSequence(nd as QilList, info); break; 
                case QilNodeType.Conditional: AnalyzeConditional(nd as QilTernary, info); break;
                case QilNodeType.Choice: AnalyzeChoice(nd as QilChoice, info); break; 

                case QilNodeType.Error:
                case QilNodeType.Warning:
                    // Ensure that construct method is Writer 
                    info.ConstructMethod = XmlILConstructMethod.Writer;
                    break; 
 
                case QilNodeType.Nop:
                    ndChild = (nd as QilUnary).Child; 
                    switch (ndChild.NodeType) {
                        case QilNodeType.For:
                        case QilNodeType.Let:
                        case QilNodeType.Parameter: 
                            // Copy iterator items as content
                            AnalyzeCopy(nd, info); 
                            break; 

                        default: 
                            // Ensure that construct method is Writer and recursively analyze content
                            info.ConstructMethod = XmlILConstructMethod.Writer;
                            AnalyzeContent(ndChild);
                            break; 
                    }
                    break; 
 
                default:
                    AnalyzeCopy(nd, info); 
                    break;
            }

            // Set states that are possible after expression is constructed 
            info.FinalStates = this.xstates;
 
            return nd; 
        }
 
        /// 
        /// Analyze loop.
        /// 
        protected virtual void AnalyzeLoop(QilLoop ndLoop, XmlILConstructInfo info) { 
            XmlQueryType typ = ndLoop.XmlType;
 
            // Ensure that construct method is Writer 
            info.ConstructMethod = XmlILConstructMethod.Writer;
 
            if (!typ.IsSingleton)
                StartLoop(typ, info);

            // Body constructs content 
            ndLoop.Body = AnalyzeContent(ndLoop.Body);
 
            if (!typ.IsSingleton) 
                EndLoop(typ, info);
        } 

        /// 
        /// Analyze list.
        ///  
        protected virtual void AnalyzeSequence(QilList ndSeq, XmlILConstructInfo info) {
            // Ensure that construct method is Writer 
            info.ConstructMethod = XmlILConstructMethod.Writer; 

            // Analyze each item in the list 
            for (int idx = 0; idx < ndSeq.Count; idx++)
                ndSeq[idx] = AnalyzeContent(ndSeq[idx]);
        }
 
        /// 
        /// Analyze conditional. 
        ///  
        protected virtual void AnalyzeConditional(QilTernary ndCond, XmlILConstructInfo info) {
            PossibleXmlStates xstatesTrue; 

            // Ensure that construct method is Writer
            info.ConstructMethod = XmlILConstructMethod.Writer;
 
            // Visit true branch; save resulting states
            ndCond.Center = AnalyzeContent(ndCond.Center); 
            xstatesTrue = this.xstates; 

            // Restore starting states and visit false branch 
            this.xstates = info.InitialStates;
            ndCond.Right = AnalyzeContent(ndCond.Right);

            // Conditional ending states consist of combination of true and false branch states 
            if (xstatesTrue != this.xstates)
                this.xstates = PossibleXmlStates.Any; 
        } 

        ///  
        /// Analyze choice.
        /// 
        protected virtual void AnalyzeChoice(QilChoice ndChoice, XmlILConstructInfo info) {
            PossibleXmlStates xstatesChoice; 
            int idx;
 
            // Visit default branch; save resulting states 
            idx = ndChoice.Branches.Count - 1;
            ndChoice.Branches[idx] = AnalyzeContent(ndChoice.Branches[idx]); 
            xstatesChoice = this.xstates;

            // Visit all other branches
            while (--idx >= 0) { 
                // Restore starting states and visit the next branch
                this.xstates = info.InitialStates; 
                ndChoice.Branches[idx] = AnalyzeContent(ndChoice.Branches[idx]); 

                // Choice ending states consist of combination of all branch states 
                if (xstatesChoice != this.xstates)
                    xstatesChoice = PossibleXmlStates.Any;
            }
 
            this.xstates = xstatesChoice;
        } 
 
        /// 
        /// Analyze copying items. 
        /// 
        protected virtual void AnalyzeCopy(QilNode ndCopy, XmlILConstructInfo info) {
            XmlQueryType typ = ndCopy.XmlType;
 
            // Copying item(s) to output involves looping if there is not exactly one item in the sequence
            if (!typ.IsSingleton) 
                StartLoop(typ, info); 

            // Determine state transitions that may take place 
            if (MaybeContent(typ)) {
                if (MaybeAttrNmsp(typ)) {
                    // Node might be Attr/Nmsp or non-Attr/Nmsp, so transition from EnumAttrs to WithinContent *may* occur
                    if (this.xstates == PossibleXmlStates.EnumAttrs) 
                        this.xstates = PossibleXmlStates.Any;
                } 
                else { 
                    // Node is guaranteed not to be Attr/Nmsp, so transition to WithinContent will occur if starting
                    // state is EnumAttrs or if constructing within an element (guaranteed to be in EnumAttrs or WithinContent state) 
                    if (this.xstates == PossibleXmlStates.EnumAttrs || this.withinElem)
                        this.xstates = PossibleXmlStates.WithinContent;
                }
            } 

            if (!typ.IsSingleton) 
                EndLoop(typ, info); 
        }
 
        /// 
        /// Calculate starting xml states that will result when iterating over and constructing an expression of the specified type.
        /// 
        private void StartLoop(XmlQueryType typ, XmlILConstructInfo info) { 
            Debug.Assert(!typ.IsSingleton);
 
            // This is tricky, because the looping introduces a feedback loop: 
            //   1. Because loops may be executed many times, the beginning set of states must include the ending set of states.
            //   2. Because loops may be executed 0 times, the final set of states after all looping is complete must include 
            //      the initial set of states.
            //
            // +-- states-initial
            // |         | 
            // | states-begin-loop <--+
            // |         |            | 
            // |  +--------------+    | 
            // |  | Construction |    |
            // |  +--------------+    | 
            // |         |            |
            // |  states-end-loop ----+
            // |         |
            // +--> states-final 

            // Save starting loop states 
            info.BeginLoopStates = this.xstates; 

            if (typ.MaybeMany) { 
                // If transition might occur from EnumAttrs to WithinContent, then states-end might be WithinContent, which
                // means states-begin needs to also include WithinContent.
                if (this.xstates == PossibleXmlStates.EnumAttrs && MaybeContent(typ))
                    info.BeginLoopStates = this.xstates = PossibleXmlStates.Any; 
            }
        } 
 
        /// 
        /// Calculate ending xml states that will result when iterating over and constructing an expression of the specified type. 
        /// 
        private void EndLoop(XmlQueryType typ, XmlILConstructInfo info) {
            Debug.Assert(!typ.IsSingleton);
 
            // Save ending loop states
            info.EndLoopStates = this.xstates; 
 
            // If it's possible to loop zero times, then states-final needs to include states-initial
            if (typ.MaybeEmpty && info.InitialStates != this.xstates) 
                this.xstates = PossibleXmlStates.Any;
        }

        ///  
        /// Return true if an instance of the specified type might be an attribute or a namespace node.
        ///  
        private bool MaybeAttrNmsp(XmlQueryType typ) { 
            return (typ.NodeKinds & (XmlNodeKindFlags.Attribute | XmlNodeKindFlags.Namespace)) != XmlNodeKindFlags.None;
        } 

        /// 
        /// Return true if an instance of the specified type might be a non-empty content type (attr/nsmp don't count).
        ///  
        private bool MaybeContent(XmlQueryType typ) {
            return !typ.IsNode || (typ.NodeKinds & ~(XmlNodeKindFlags.Attribute | XmlNodeKindFlags.Namespace)) != XmlNodeKindFlags.None; 
        } 
    }
 

    /// 
    /// Scans the content of an ElementCtor and tries to minimize the number of well-formed checks that will have
    /// to be made at runtime when constructing content. 
    /// 
    internal class XmlILElementAnalyzer : XmlILStateAnalyzer { 
        private NameTable attrNames = new NameTable(); 
        private ArrayList dupAttrs = new ArrayList();
 
        /// 
        /// Constructor.
        /// 
        public XmlILElementAnalyzer(QilFactory fac) : base(fac) { 
        }
 
        ///  
        /// Analyze the content argument of the ElementCtor.  Try to eliminate as many runtime checks as possible,
        /// both for the ElementCtor and for content constructors. 
        /// 
        public override QilNode Analyze(QilNode ndElem, QilNode ndContent) {
            Debug.Assert(ndElem.NodeType == QilNodeType.ElementCtor);
            this.parentInfo = XmlILConstructInfo.Write(ndElem); 

            // Start by assuming that these properties are false (they default to true, but analyzer might be able to 
            // prove they are really false). 
            this.parentInfo.MightHaveNamespacesAfterAttributes = false;
            this.parentInfo.MightHaveAttributes = false; 
            this.parentInfo.MightHaveDuplicateAttributes = false;

            // The element's namespace might need to be declared
            this.parentInfo.MightHaveNamespaces = !this.parentInfo.IsNamespaceInScope; 

            // Clear list of duplicate attributes 
            this.dupAttrs.Clear(); 

            return base.Analyze(ndElem, ndContent); 
        }

        /// 
        /// Analyze loop. 
        /// 
        protected override void AnalyzeLoop(QilLoop ndLoop, XmlILConstructInfo info) { 
            // Constructing attributes/namespaces in a loop can cause duplicates, namespaces after attributes, etc. 
            if (ndLoop.XmlType.MaybeMany)
                CheckAttributeNamespaceConstruct(ndLoop.XmlType); 

            base.AnalyzeLoop(ndLoop, info);
        }
 
        /// 
        /// Analyze copying items. 
        ///  
        protected override void AnalyzeCopy(QilNode ndCopy, XmlILConstructInfo info) {
            if (ndCopy.NodeType == QilNodeType.AttributeCtor) { 
                AnalyzeAttributeCtor(ndCopy as QilBinary, info);
            }
            else {
                CheckAttributeNamespaceConstruct(ndCopy.XmlType); 
            }
 
            base.AnalyzeCopy(ndCopy, info); 
        }
 
        /// 
        /// Analyze attribute constructor.
        /// 
        private void AnalyzeAttributeCtor(QilBinary ndAttr, XmlILConstructInfo info) { 
            if (ndAttr.Left.NodeType == QilNodeType.LiteralQName) {
                QilName ndName = ndAttr.Left as QilName; 
                XmlQualifiedName qname; 
                int idx;
 
                // This attribute might be constructed on the parent element
                this.parentInfo.MightHaveAttributes = true;

                // Check to see whether this attribute is a duplicate of a previous attribute 
                if (!this.parentInfo.MightHaveDuplicateAttributes) {
                    qname = new XmlQualifiedName(this.attrNames.Add(ndName.LocalName), this.attrNames.Add(ndName.NamespaceUri)); 
 
                    for (idx = 0; idx < this.dupAttrs.Count; idx++) {
                        XmlQualifiedName qnameDup = (XmlQualifiedName) this.dupAttrs[idx]; 

                        if ((object) qnameDup.Name == (object) qname.Name && (object) qnameDup.Namespace == (object) qname.Namespace) {
                            // A duplicate attribute has been encountered
                            this.parentInfo.MightHaveDuplicateAttributes = true; 
                        }
                    } 
 
                    if (idx >= this.dupAttrs.Count) {
                        // This is not a duplicate attribute, so add it to the set 
                        this.dupAttrs.Add(qname);
                    }
                }
 
                // The attribute's namespace might need to be declared
                if (!info.IsNamespaceInScope) 
                    this.parentInfo.MightHaveNamespaces = true; 
            }
            else { 
                // Attribute prefix and namespace are not known at compile-time
                CheckAttributeNamespaceConstruct(ndAttr.XmlType);
            }
        } 

        ///  
        /// If type might contain attributes or namespaces, set appropriate parent element flags. 
        /// 
        private void CheckAttributeNamespaceConstruct(XmlQueryType typ) { 
            // If content might contain attributes,
            if ((typ.NodeKinds & XmlNodeKindFlags.Attribute) != XmlNodeKindFlags.None) {
                // Mark element as possibly having attributes and duplicate attributes (since we don't know the names)
                this.parentInfo.MightHaveAttributes = true; 
                this.parentInfo.MightHaveDuplicateAttributes = true;
 
                // Attribute namespaces might be declared 
                this.parentInfo.MightHaveNamespaces = true;
            } 

            // If content might contain namespaces,
            if ((typ.NodeKinds & XmlNodeKindFlags.Namespace) != XmlNodeKindFlags.None) {
                // Then element might have namespaces, 
                this.parentInfo.MightHaveNamespaces = true;
 
                // If attributes might already have been constructed, 
                if (this.parentInfo.MightHaveAttributes) {
                    // Then attributes might precede namespace declarations 
                    this.parentInfo.MightHaveNamespacesAfterAttributes = true;
                }
            }
        } 
    }
 
 
    /// 
    /// Scans constructed content, looking for redundant namespace declarations.  If any are found, then they are marked 
    /// and removed later.
    /// 
    internal class XmlILNamespaceAnalyzer {
        private XmlNamespaceManager nsmgr = new XmlNamespaceManager(new NameTable()); 
        private bool addInScopeNmsp;
        private int cntNmsp; 
 
        /// 
        /// Perform scan. 
        /// 
        public void Analyze(QilNode nd, bool defaultNmspInScope) {
            this.addInScopeNmsp = false;
            this.cntNmsp = 0; 

            // If xmlns="" is in-scope, push it onto the namespace stack 
            if (defaultNmspInScope) { 
                this.nsmgr.PushScope();
                this.nsmgr.AddNamespace(string.Empty, string.Empty); 
                this.cntNmsp++;
            }

            AnalyzeContent(nd); 

            if (defaultNmspInScope) 
                this.nsmgr.PopScope(); 
        }
 
        /// 
        /// Recursively analyze content.  Return "nd" or a replacement for it.
        /// 
        private void AnalyzeContent(QilNode nd) { 
            int cntNmspSave;
 
            switch (nd.NodeType) { 
                case QilNodeType.Loop:
                    this.addInScopeNmsp = false; 
                    AnalyzeContent((nd as QilLoop).Body);
                    break;

                case QilNodeType.Sequence: 
                    foreach (QilNode ndContent in nd)
                        AnalyzeContent(ndContent); 
                    break; 

                case QilNodeType.Conditional: 
                    this.addInScopeNmsp = false;
                    AnalyzeContent((nd as QilTernary).Center);
                    AnalyzeContent((nd as QilTernary).Right);
                    break; 

                case QilNodeType.Choice: 
                    this.addInScopeNmsp = false; 
                    QilList ndBranches = (nd as QilChoice).Branches;
                    for (int idx = 0; idx < ndBranches.Count; idx++) 
                        AnalyzeContent(ndBranches[idx]);

                    break;
 
                case QilNodeType.ElementCtor:
                    // Start a new namespace scope 
                    this.addInScopeNmsp = true; 
                    this.nsmgr.PushScope();
                    cntNmspSave = this.cntNmsp; 

                    if (CheckNamespaceInScope(nd as QilBinary))
                        AnalyzeContent((nd as QilBinary).Right);
 
                    this.nsmgr.PopScope();
                    this.addInScopeNmsp = false; 
                    this.cntNmsp = cntNmspSave; 
                    break;
 
                case QilNodeType.AttributeCtor:
                    this.addInScopeNmsp = false;
                    CheckNamespaceInScope(nd as QilBinary);
                    break; 

                case QilNodeType.NamespaceDecl: 
                    CheckNamespaceInScope(nd as QilBinary); 
                    break;
 
                case QilNodeType.Nop:
                    AnalyzeContent((nd as QilUnary).Child);
                    break;
 
                default:
                    this.addInScopeNmsp = false; 
                    break; 
            }
        } 

        /// 
        /// Determine whether an ElementCtor, AttributeCtor, or NamespaceDecl's namespace is already declared.  If it is,
        /// set the IsNamespaceInScope property to True.  Otherwise, add the namespace to the set of in-scope namespaces if 
        /// addInScopeNmsp is True.  Return false if the name is computed or is invalid.
        ///  
        private bool CheckNamespaceInScope(QilBinary nd) { 
            QilName ndName;
            string prefix, ns, prefixExisting, nsExisting; 
            XPathNodeType nodeType;

            switch (nd.NodeType) {
                case QilNodeType.ElementCtor: 
                case QilNodeType.AttributeCtor:
                    ndName = nd.Left as QilName; 
                    if (ndName != null) { 
                        prefix = ndName.Prefix;
                        ns = ndName.NamespaceUri; 
                        nodeType = (nd.NodeType == QilNodeType.ElementCtor) ? XPathNodeType.Element : XPathNodeType.Attribute;
                        break;
                    }
 
                    // Not a literal name, so return false
                    return false; 
 
                default:
                    Debug.Assert(nd.NodeType == QilNodeType.NamespaceDecl); 
                    prefix = (string) (QilLiteral) nd.Left;
                    ns = (string) (QilLiteral) nd.Right;
                    nodeType = XPathNodeType.Namespace;
                    break; 
            }
 
            // Attribute with null namespace and xmlns:xml are always in-scope 
            if (nd.NodeType == QilNodeType.AttributeCtor && ns.Length == 0 ||
                prefix == "xml" && ns == XmlReservedNs.NsXml) { 
                XmlILConstructInfo.Write(nd).IsNamespaceInScope = true;
                return true;
            }
 
            // Don't process names that are invalid
            if (!ValidateNames.ValidateName(prefix, string.Empty, ns, nodeType, ValidateNames.Flags.CheckPrefixMapping)) 
                return false; 

            // Atomize names 
            prefix = this.nsmgr.NameTable.Add(prefix);
            ns = this.nsmgr.NameTable.Add(ns);

            // Determine whether namespace is already in-scope 
            for (int iNmsp = 0; iNmsp < this.cntNmsp; iNmsp++) {
                this.nsmgr.GetNamespaceDeclaration(iNmsp, out prefixExisting, out nsExisting); 
 
                // If prefix is already declared,
                if ((object) prefix == (object) prefixExisting) { 
                    // Then if the namespace is the same, this namespace is redundant
                    if ((object) ns == (object) nsExisting)
                        XmlILConstructInfo.Write(nd).IsNamespaceInScope = true;
 
                    // Else quit searching, because any further matching prefixes will be hidden (not in-scope)
                    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."); 
                    break;
                } 
            }

            // If not in-scope, then add if it's allowed
            if (this.addInScopeNmsp) { 
                this.nsmgr.AddNamespace(prefix, ns);
                this.cntNmsp++; 
            } 

            return true; 
        }
    }
}

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

                        

Link Menu

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