1 //------------------------------------------------------------------------------
2 // <copyright file="QilValidationVisitor.cs" company="Microsoft">
3 // Copyright (c) Microsoft Corporation. All rights reserved.
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>
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>
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>
32 internal class QilValidationVisitor
: QilScopedVisitor
{
33 private SubstitutionList subs
= new SubstitutionList();
34 private QilTypeChecker typeCheck
= new QilTypeChecker();
36 //-----------------------------------------------
38 //-----------------------------------------------
40 [Conditional("DEBUG")]
41 public static void Validate(QilNode node
) {
42 Debug
.Assert(node
!= null);
43 new QilValidationVisitor().VisitAssumeReference(node
);
46 protected QilValidationVisitor() {}
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");
68 XmlQueryType type
= this.typeCheck
.Check(parent
);
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
)
82 // Do not allow null anywhere else in the graph
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
]);
101 this.parents
.Remove(parent
);
108 /// Ensure that the function or iterator reference is already in scope.
110 protected override QilNode
VisitReference(QilNode node
) {
111 if (!this.scope
.Contains(node
))
112 SetError(node
, "Out-of-scope reference");
118 //-----------------------------------------------
119 // QilScopedVisitor overrides
120 //-----------------------------------------------
123 /// Add an iterator or function to scope if it hasn't been added already.
125 protected override void BeginScope(QilNode node
) {
126 if (this.scope
.Contains(node
))
127 SetError(node
, "Reference already in scope");
129 this.scope
.Add(node
, node
);
135 protected override void EndScope(QilNode node
) {
136 this.scope
.Remove(node
);
140 //-----------------------------------------------
142 //-----------------------------------------------
144 private class ObjectHashtable
: Hashtable
{
145 protected override bool KeyEquals(object item
, object key
) {
150 private bool AddNode(QilNode n
) {
151 if (!this.allNodes
.Contains(n
)) {
152 this.allNodes
.Add(n
, n
);
156 SetError(n
, "Duplicate " + n
.NodeType
+ " node");
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") + ")]";
170 string s
= n
.Annotation
as string;
172 message
= s
+ "\n" + message
;
174 n
.Annotation
= message
;
175 Debug
.Assert(false, message
);