Updates referencesource to .NET 4.7
[mono-project.git] / mcs / class / referencesource / System.Data.SqlXml / System / Xml / Xsl / QIL / QilValidationVisitor.cs
blob9ebe9a2dca22f7e9dcbc99d0afa6161cb6e7cb98
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 //------------------------------------------------------------------------------
8 using System.Collections;
9 using System.Diagnostics;
11 namespace System.Xml.Xsl.Qil {
12 using Res = System.Xml.Utils.Res;
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();
36 //-----------------------------------------------
37 // Entry
38 //-----------------------------------------------
40 [Conditional("DEBUG")]
41 public static void Validate(QilNode node) {
42 Debug.Assert(node != null);
43 new QilValidationVisitor().VisitAssumeReference(node);
46 protected QilValidationVisitor() {}
48 #if DEBUG
49 protected Hashtable allNodes = new ObjectHashtable();
50 protected Hashtable parents = new ObjectHashtable();
51 protected Hashtable scope = new ObjectHashtable();
54 //-----------------------------------------------
55 // QilVisitor overrides
56 //-----------------------------------------------
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");
63 else if (AddNode(parent)) {
64 if (parent.XmlType == null) {
65 SetError(parent, "Type information missing");
67 else {
68 XmlQueryType type = this.typeCheck.Check(parent);
70 //
71 if (!type.IsSubtypeOf(parent.XmlType))
72 SetError(parent, "Type information was not correctly inferred");
75 this.parents.Add(parent, parent);
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");
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");
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]);
101 this.parents.Remove(parent);
104 return parent;
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");
114 return node;
118 //-----------------------------------------------
119 // QilScopedVisitor overrides
120 //-----------------------------------------------
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);
132 /// <summary>
133 /// Pop scope.
134 /// </summary>
135 protected override void EndScope(QilNode node) {
136 this.scope.Remove(node);
140 //-----------------------------------------------
141 // Helper methods
142 //-----------------------------------------------
144 private class ObjectHashtable : Hashtable {
145 protected override bool KeyEquals(object item, object key) {
146 return item == key;
150 private bool AddNode(QilNode n) {
151 if (!this.allNodes.Contains(n)) {
152 this.allNodes.Add(n, n);
153 return true;
155 else {
156 SetError(n, "Duplicate " + n.NodeType + " node");
157 return false;
160 #endif // DEBUG
162 [Conditional("DEBUG")]
163 internal static void SetError(QilNode n, string message) {
164 message = Res.GetString(Res.Qil_Validation, message);
166 #if QIL_TRACE_NODE_CREATION
167 message += " ["+ n.NodeId + " (" + n.NodeType.ToString("G") + ")]";
168 #endif
170 string s = n.Annotation as string;
171 if (s != null) {
172 message = s + "\n" + message;
174 n.Annotation = message;
175 Debug.Assert(false, message);