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