1 /*
2  * Copyright 2006-2008 The FLWOR Foundation.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 #include "stdafx.h"
17 
18 #include "compiler/translator/prolog_graph.h"
19 
20 #include "context/static_context.h"
21 
22 #include "util/string_util.h"
23 
24 #include "zorbatypes/zstring.h"
25 
26 #include "diagnostics/xquery_exception.h"
27 #include "diagnostics/dict.h"
28 #include "diagnostics/util_macros.h"
29 
30 namespace zorba
31 {
32 
33 
34 /*******************************************************************************
35 
36 ********************************************************************************/
reportCycle(const QueryLoc & loc,const PrologGraphVertex * v)37 void PrologGraph::reportCycle(const QueryLoc& loc, const PrologGraphVertex* v)
38 {
39   std::string moduleNS = theModuleSctx->get_module_namespace();
40   zstring varName;
41   if ( v )
42     varName = BUILD_STRING('$', v->getVarExpr()->get_name()->getStringValue());
43 
44   RAISE_ERROR(err::XQST0054, loc, ERROR_PARAMS(varName));
45 }
46 
47 
48 /*******************************************************************************
49 
50 ********************************************************************************/
addEdge(const PrologGraphVertex & v1,const PrologGraphVertex & v2)51 void PrologGraph::addEdge(const PrologGraphVertex& v1, const PrologGraphVertex& v2)
52 {
53   if (v1.isVar() && v2.isVar() && v1.getVarExpr() == v2.getVarExpr())
54   {
55     zstring varName = '$' + v1.getVarExpr()->get_name()->getStringValue();
56 
57     RAISE_ERROR(err::XPST0008, v2.getVarExpr()->get_loc(),
58     ERROR_PARAMS(varName, ZED(VariabledUndeclared)));
59   }
60 
61   addEdge(theGraph, v1, v2);
62 }
63 
64 
65 /*******************************************************************************
66   This method is part of the mechanism for detecting cycles in the dependency
67   graph among prolog variables. The method does not actually detect the cycles
68   but re-orders the declarations of prolog vars (i.e., reorders theGlobalVars
69   list) so that if var v2 depends on var v1, then v1 appears before v2 in the
70   list (and as a result, v1 will be evaluated before v2 during runtime).
71 
72   Circular dependencies among prolog vars can appear only when udfs are involved.
73   Here is an example:
74 
75   declare variable $var := local:func1();
76 
77   declare function local:func1()
78   {
79     local:func2()
80   };
81 
82   declare function local:func2()
83   {
84     local:func3()
85   };
86 
87   declare variable $var2 := local:func2();
88 
89   declare function local:func3()
90   {
91     $var2
92   };
93 
94   boolean($var)
95 
96 
97   In this query, the following cycle exists : var2 --> func2 --> func3 --> var2
98 ********************************************************************************/
reorder_globals(std::list<GlobalBinding> & prologVarBindings)99 void PrologGraph::reorder_globals(std::list<GlobalBinding>& prologVarBindings)
100 {
101   GraphImpl::const_iterator g_ite;
102   GraphImpl::const_iterator g_end = theGraph.end();
103 
104   std::vector<const var_expr*>::const_iterator v_ite;
105   std::vector<const var_expr*>::const_iterator v_end = theVarDecls.end();
106 
107   std::vector<const function*>::const_iterator fend = theFuncDecls.end();
108 
109   // STEP 1: Floyd-Warshall transitive closure of edges starting from functions
110   // At each substep we are adding fn2 -> fn1 -> var/fn dependencies. The resulting
111   // graph has the following property: If there is a path P in the original graph
112   // such that P starts at a udf node F, ends at a var node V and contains only
113   // udf nodes in between, then in the augmented graph there is a direct edge
114   // F --> V. As a result, to go from one variable to another, we never need to
115   // cross 2 or more consequtive udf nodes.
116 
117   std::vector<const function*>::const_iterator f1_ite;
118   std::vector<const function*>::const_iterator f2_ite;
119 
120   for (f1_ite = theFuncDecls.begin(); f1_ite != fend; ++f1_ite)
121   {
122     const function* f1 = *f1_ite;
123     GraphImpl::iterator f1_graph_entry = theGraph.find(f1);
124 
125     if (f1_graph_entry == g_end)
126       continue;
127 
128     for (f2_ite = theFuncDecls.begin(); f2_ite != fend; ++f2_ite)
129     {
130       const function* f2 = *f2_ite;
131       GraphImpl::const_iterator f2_graph_entry = theGraph.find(f2);
132 
133       // if f2 depends on f1, then f2 depends on every var/udf that f1 depends on.
134 
135       // If f2 does not depend on anything, skip it
136       if (f2_graph_entry == g_end)
137         continue;
138 
139       VSet* f1_vset = f1_graph_entry->second;
140       VSet* f2_vset = f2_graph_entry->second;
141 
142       // If f2 does not depend on f1, skip it
143       if (f2_vset->find(f1) == f2_vset->end())
144         continue;
145 
146       f2_vset->insert(f1_vset->begin(), f1_vset->end());
147     }
148   }
149 
150   // STEP 2: Add "1-step" var -> fn -> var/fn dependencies. Steps 1 & 2 together
151   // guarantee that if there is a path P from variable v1 to variable v2 in the
152   // original graph, then in the augmented graph there is a path P' from v1 to
153   // v2 such that P' does not contain any udfs. Therefore, the augmented graph
154   // contains a subgraph VG that consists of edges among variables only and which
155   // which reflects the same (direct and transitive) dependencies among vars as
156   // the original graph.
157 
158   for (v_ite = theVarDecls.begin(); v_ite != v_end; ++v_ite)
159   {
160     const var_expr* v1 = *v_ite;
161     GraphImpl::const_iterator v1_graph_entry = theGraph.find(v1);
162 
163     if (v1_graph_entry == g_end)
164       continue;
165 
166     VSet* v1_vset = v1_graph_entry->second;
167     VSet::const_iterator v1_vset_ite = v1_vset->begin();
168     VSet::const_iterator v1_vset_end = v1_vset->end();
169 
170     for (; v1_vset_ite != v1_vset_end; ++v1_vset_ite)
171     {
172       const PrologGraphVertex& f = *v1_vset_ite;
173 
174       if (f.getKind() == PrologGraphVertex::FUN)
175       {
176         GraphImpl::iterator f_graph_entry = theGraph.find(f);
177 
178         if (f_graph_entry == g_end)
179           continue;
180 
181         VSet* f_vset = f_graph_entry->second;
182         VSet::const_iterator f_vset_ite = f_vset->begin();
183         VSet::const_iterator f_vset_end = f_vset->end();
184 
185         for (; f_vset_ite != f_vset_end; ++f_vset_ite)
186         {
187           const PrologGraphVertex& v2 = *f_vset_ite;
188 
189           if (v2.getKind() == PrologGraphVertex::VAR)
190           {
191             if (v2 == v1)
192             {
193               reportCycle(v2.getVarExpr()->get_loc(), &v2);
194             }
195 
196             addEdge(v1, v2);
197           }
198         }
199       }
200     }
201   }
202 
203   // STEP 3 Check for cycles. First we extract graph VG from the original graph.
204   // We also make a copy of theVarDecls vector into varDels. Then we repeat the
205   // following steps until varDecls gets empty:
206   // 1. We look for a var V in varDecls that does not depend on any other var.
207   // 2. If no such var is found, a cycle exists, and we raise an error.
208   // 3. We remove V from varDecls and from each dependency set that V appears in.
209   // (TODO: use faster algorithm)
210 
211   GraphImpl v_graph;
212   std::set<const var_expr*> varDecls;
213 
214   for (g_ite = theGraph.begin(); g_ite != g_end; ++g_ite)
215   {
216     const PrologGraphVertex& v1 = (*g_ite).first;
217 
218     if (v1.getKind() == PrologGraphVertex::VAR)
219     {
220       VSet* v1_vset = (*g_ite).second;
221       VSet::const_iterator v1_vset_ite = v1_vset->begin();
222       VSet::const_iterator v1_vset_end = v1_vset->end();
223 
224       for (; v1_vset_ite != v1_vset_end; ++v1_vset_ite)
225       {
226         const PrologGraphVertex& v2 = *v1_vset_ite;
227 
228         if (v2.getKind() == PrologGraphVertex::VAR)
229         {
230           PrologGraph::addEdge(v_graph, v1, v2);
231           varDecls.insert(v1.getVarExpr());
232           varDecls.insert(v2.getVarExpr());
233         }
234       }
235     }
236   }
237 
238   g_end = v_graph.end();
239 
240   while (!varDecls.empty())
241   {
242     const var_expr* var = NULL;
243 
244     std::set<const var_expr*>::iterator v_ite = varDecls.begin();
245     std::set<const var_expr*>::iterator v_end = varDecls.end();
246 
247     for (; v_ite != v_end; ++v_ite)
248     {
249       GraphImpl::iterator v_graph_entry = v_graph.find(*v_ite);
250       if (v_graph_entry == g_end ||
251           v_graph_entry->second->empty())
252       {
253         var = *v_ite;
254         varDecls.erase(v_ite);
255         break;
256       }
257     }
258 
259     if (var == NULL)
260     {
261       for (g_ite = v_graph.begin(); g_ite != g_end; ++g_ite)
262       {
263         delete (*g_ite).second;
264       }
265 
266       reportCycle((*varDecls.begin())->get_loc(), 0);
267     }
268 
269     for (g_ite = v_graph.begin(); g_ite != g_end; ++g_ite)
270     {
271       VSet* vset = (*g_ite).second;
272       VSet::iterator vset_entry = vset->find(var);
273 
274       if (vset_entry != vset->end())
275       {
276         vset->erase(vset_entry);
277       }
278     }
279   }
280 
281   for (g_ite = v_graph.begin(); g_ite != g_end; ++g_ite)
282   {
283     delete (*g_ite).second;
284   }
285 
286   // STEP 4: topologically sort global vars.
287   // Implemented using non-recursive (stack-based) DFS traversal. This algorithm
288   // unfortunately does not detect cycles.
289   // Note that steps 1 & 2 are required: we cannot sort the entire set of prolog
290   // vars + udfs, because functions are allowed to be mutually recursive.
291 
292   g_end = theGraph.end();
293 
294   std::vector<const var_expr*> topsorted_vars;  // dependencies first
295   std::set<const var_expr*> visited;
296 
297   std::stack<std::pair<ulong, const var_expr*> > todo;  // format: action code + var_expr
298   // need to declare the reverse end iterator here because of a bug in older gcc's
299   // (see http://gcc.gnu.org/bugzilla/show_bug.cgi?id=11729)
300   std::vector<const var_expr*>::const_reverse_iterator  lEnd = theVarDecls.rend();
301   for (std::vector<const var_expr*>::const_reverse_iterator it = theVarDecls.rbegin();
302        it != lEnd;
303        ++it)
304   {
305     todo.push(std::pair<ulong, const var_expr*>(1, (*it)));
306   }
307 
308   while (! todo.empty())
309   {
310     const var_expr* var = todo.top().second;
311     ulong action = todo.top().first;
312     todo.pop();
313 
314     switch (action)
315     {
316     case 0:  // finish notification
317     {
318       topsorted_vars.push_back(var);
319       break;
320     }
321     case 1:  // visit request
322     {
323       if (visited.find(var) == visited.end())
324       {
325         visited.insert(var);
326         todo.push(std::pair<ulong, const var_expr*>(0, var));
327 
328         GraphImpl::const_iterator var_graph_entry = theGraph.find(var);
329 
330         if (var_graph_entry != g_end)
331         {
332           VSet* var_vset = var_graph_entry->second;
333 
334           VSet::const_iterator var_vset_ite = var_vset->begin();
335           VSet::const_iterator var_vset_end = var_vset->end();
336           for (; var_vset_ite != var_vset_end; ++var_vset_ite)
337           {
338             if (var_vset_ite->getKind() == PrologGraphVertex::VAR)
339             {
340               todo.push(std::pair<ulong, const var_expr*>(1, var_vset_ite->getVarExpr()));
341             }
342           }
343         }
344       }
345       break;
346     }
347     default:
348       assert(false);
349     }
350   }
351 
352   // STEP 5: reorder thePrologVars according to topological order
353   std::map<const var_expr*, GlobalBinding> gvmap;
354   std::list<GlobalBinding>::iterator bindIte = prologVarBindings.begin();
355   for (; bindIte != prologVarBindings.end(); ++bindIte)
356   {
357     gvmap[(*bindIte).theVar] = *bindIte;
358   }
359 
360   prologVarBindings.clear();
361 
362   for (std::vector<const var_expr*>::iterator i = topsorted_vars.begin();
363        i != topsorted_vars.end(); ++i)
364   {
365     std::map<const var_expr*, GlobalBinding>::iterator p = gvmap.find(*i);
366 
367     if (p != gvmap.end())
368       prologVarBindings.push_back((*p).second);
369   }
370 }
371 
372 
373 } // namespace zorba
374 /* vim:set et sw=2 ts=2: */
375