The Labs \ Source Viewer \ SSCLI \ System.Xml.Xsl.Qil \ QilValidationVisitor

  1. //------------------------------------------------------------------------------
  2. // <copyright file="QilValidationVisitor.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.Collections;
  16. using System.Diagnostics;
  17. namespace System.Xml.Xsl.Qil
  18. {
  19.     using Res = System.Xml.Utils.Res;
  20.    
  21.     /// <summary>A internal class that validates QilExpression graphs.</summary>
  22.     /// <remarks>
  23.     /// QilValidationVisitor traverses the QilExpression graph once to enforce the following constraints:
  24.     /// <list type="bullet">
  25.     /// <item>No circular references</item>
  26.     /// <item>No duplicate nodes (except for references)</item>
  27.     /// <item>No out-of-scope references</item>
  28.     /// <item>Type constraints on operands</item>
  29.     /// <item>Type constraints on operators</item>
  30.     /// <item>No null objects (except where allowed)</item>
  31.     /// <item>No Unknown node types</item>
  32.     /// </list>
  33.     /// <p>When an error occurs, it marks the offending node with an annotation and continues checking,
  34.     /// allowing the detection of multiple errors at once and printing the structure after validation.
  35.     /// (In the case of circular references, it breaks the loop at the circular reference to allow the graph
  36.     /// to print correctly.)</p>
  37.     /// </remarks>
  38.     ///
  39.     internal class QilValidationVisitor : QilScopedVisitor
  40.     {
  41.         private SubstitutionList subs = new SubstitutionList();
  42.         private QilTypeChecker typeCheck = new QilTypeChecker();
  43.        
  44.         //-----------------------------------------------
  45.         // Entry
  46.         //-----------------------------------------------
  47.        
  48.         [Conditional("DEBUG")]
  49.         public static void Validate(QilNode node)
  50.         {
  51.             Debug.Assert(node != null);
  52.             new QilValidationVisitor().VisitAssumeReference(node);
  53.         }
  54.        
  55.         protected QilValidationVisitor()
  56.         {
  57.         }
  58.        
  59.         #if DEBUG
  60.         protected Hashtable allNodes = new ObjectHashtable();
  61.         protected Hashtable parents = new ObjectHashtable();
  62.         protected Hashtable scope = new ObjectHashtable();
  63.        
  64.        
  65.         //-----------------------------------------------
  66.         // QilVisitor overrides
  67.         //-----------------------------------------------
  68.        
  69.         protected override QilNode VisitChildren(QilNode parent)
  70.         {
  71.             if (this.parents.Contains(parent)) {
  72.                 // We have already visited the node that starts the infinite loop, but don't visit its children
  73.                 SetError(parent, "Infinite loop");
  74.             }
  75.             else if (AddNode(parent)) {
  76.                 if (parent.XmlType == null) {
  77.                     SetError(parent, "Type information missing");
  78.                 }
  79.                 else {
  80.                     XmlQueryType type = this.typeCheck.Check(parent);
  81.                    
  82.                     if (!type.IsSubtypeOf(parent.XmlType))
  83.                         SetError(parent, "Type information was not correctly inferred");
  84.                 }
  85.                
  86.                 this.parents.Add(parent, parent);
  87.                
  88.                 for (int i = 0; i < parent.Count; i++) {
  89.                     if (parent[i] == null) {
  90.                         // Allow parameter name and default value to be null
  91.                         if (parent.NodeType == QilNodeType.Parameter)
  92.                             continue;
  93.                         else
  94.                             // Do not allow null anywhere else in the graph
  95.                             SetError(parent, "Child " + i + " must not be null");
  96.                     }
  97.                    
  98.                     if (parent.NodeType == QilNodeType.GlobalVariableList || parent.NodeType == QilNodeType.GlobalParameterList || parent.NodeType == QilNodeType.FunctionList) {
  99.                         if (((QilReference)parent[i]).DebugName == null)
  100.                             SetError(parent[i], "DebugName must not be null");
  101.                     }
  102.                    
  103.                     // If child is a reference, then call VisitReference instead of Visit in order to avoid circular visits.
  104.                     if (IsReference(parent, i))
  105.                         VisitReference(parent[i]);
  106.                     else
  107.                         Visit(parent[i]);
  108.                 }
  109.                
  110.                 this.parents.Remove(parent);
  111.             }
  112.            
  113.             return parent;
  114.         }
  115.        
  116.         /// <summary>
  117.         /// Ensure that the function or iterator reference is already in scope.
  118.         /// </summary>
  119.         protected override QilNode VisitReference(QilNode node)
  120.         {
  121.             if (!this.scope.Contains(node))
  122.                 SetError(node, "Out-of-scope reference");
  123.            
  124.             return node;
  125.         }
  126.        
  127.        
  128.         //-----------------------------------------------
  129.         // QilScopedVisitor overrides
  130.         //-----------------------------------------------
  131.        
  132.         /// <summary>
  133.         /// Add an iterator or function to scope if it hasn't been added already.
  134.         /// </summary>
  135.         protected override void BeginScope(QilNode node)
  136.         {
  137.             if (this.scope.Contains(node))
  138.                 SetError(node, "Reference already in scope");
  139.             else
  140.                 this.scope.Add(node, node);
  141.         }
  142.        
  143.         /// <summary>
  144.         /// Pop scope.
  145.         /// </summary>
  146.         protected override void EndScope(QilNode node)
  147.         {
  148.             this.scope.Remove(node);
  149.         }
  150.        
  151.        
  152.         //-----------------------------------------------
  153.         // Helper methods
  154.         //-----------------------------------------------
  155.        
  156.         private class ObjectHashtable : Hashtable
  157.         {
  158.             protected override bool KeyEquals(object item, object key)
  159.             {
  160.                 return item == key;
  161.             }
  162.         }
  163.        
  164.         private bool AddNode(QilNode n)
  165.         {
  166.             if (!this.allNodes.Contains(n)) {
  167.                 this.allNodes.Add(n, n);
  168.                 return true;
  169.             }
  170.             else {
  171.                 SetError(n, "Duplicate " + n.NodeType + " node");
  172.                 return false;
  173.             }
  174.         }
  175.         #endif // DEBUG
  176.        
  177.         [Conditional("DEBUG")]
  178.         static internal void SetError(QilNode n, string message)
  179.         {
  180.             message = Res.GetString(Res.Qil_Validation, message);
  181.            
  182.             #if QIL_TRACE_NODE_CREATION
  183.             message += " [" + n.NodeId + " (" + n.NodeType.ToString("G") + ")]";
  184.             #endif
  185.            
  186.             string s = n.Annotation as string;
  187.             if (s != null) {
  188.                 message = s + "\n" + message;
  189.             }
  190.             n.Annotation = message;
  191.             Debug.Assert(false, message);
  192.         }
  193.     }
  194. }

Developer Fusion