← Back to team overview

zorba-coders team mailing list archive

[Merge] lp:~zorba-coders/zorba/no-expr-serialization into lp:zorba

 

Markos Zaharioudakis has proposed merging lp:~zorba-coders/zorba/no-expr-serialization into lp:zorba.

Requested reviews:
  Markos Zaharioudakis (markos-za)

For more details, see:
https://code.launchpad.net/~zorba-coders/zorba/no-expr-serialization/+merge/104856

Plan serializer does not serialize expressions anymore.
-- 
https://code.launchpad.net/~zorba-coders/zorba/no-expr-serialization/+merge/104856
Your team Zorba Coders is subscribed to branch lp:zorba.
=== modified file 'src/api/xqueryimpl.cpp'
--- src/api/xqueryimpl.cpp	2012-05-03 12:31:51 +0000
+++ src/api/xqueryimpl.cpp	2012-05-06 14:39:24 +0000
@@ -194,7 +194,15 @@
   }
   else
   {
-    //ar.compiler_cb = theCompilerCB;
+    ar.set_ccb(theCompilerCB);
+    // This is needed to handle the following scenarion (which occurs in
+    // the scripting/flwor15.xq test): We start serializing the sctx -->
+    // serialize a global var V --> V has a set exrp E, which is inside a
+    // UDF F --> E contains a recursive call to F --> so while serializing
+    // E, we call F->serialize() --> F->serialize() will do codegen on the
+    // body of F, and this codegen may alter E (e.g. by adding a materialize
+    // clause in a flwor expr within E --> so, while serializing E we may
+    // modify E --> PROBLEMS!!  
     theCompilerCB->prepare_for_serialize();
   }
 

=== modified file 'src/compiler/api/compilercb.cpp'
--- src/compiler/api/compilercb.cpp	2012-05-03 12:31:51 +0000
+++ src/compiler/api/compilercb.cpp	2012-05-06 14:39:24 +0000
@@ -114,6 +114,7 @@
 #ifdef ZORBA_WITH_DEBUGGER
   theDebuggerCommons(0),
 #endif
+  theHasEval(false),
   theIsEval(false),
   theIsLoadProlog(false),
   theIsUpdating(false),
@@ -137,6 +138,7 @@
 #ifdef ZORBA_WITH_DEBUGGER
   theDebuggerCommons(cb.theDebuggerCommons),
 #endif
+  theHasEval(false),
   theIsEval(false),
   theIsLoadProlog(false),
   theIsUpdating(false),
@@ -160,6 +162,7 @@
 #ifdef ZORBA_WITH_DEBUGGER
   theDebuggerCommons(NULL),
 #endif
+  theHasEval(false),
   theIsEval(false)
 {
 }
@@ -202,6 +205,7 @@
 ********************************************************************************/
 void CompilerCB::serialize(::zorba::serialization::Archiver& ar)
 {
+  ar & theHasEval;
   ar & theIsEval;
   ar & theIsLoadProlog;
   ar & theIsUpdating;

=== modified file 'src/compiler/api/compilercb.h'
--- src/compiler/api/compilercb.h	2012-05-03 12:31:51 +0000
+++ src/compiler/api/compilercb.h	2012-05-06 14:39:24 +0000
@@ -47,11 +47,13 @@
   created as a copy of the query-level ccb during the execution of the eval/xqdoc
   expr.
 
-  - theXQueryDiagnostics :
+  theXQueryDiagnostics :
+  ----------------------
   Pointer to the query's XQueryDiagnostics obj. (see src/api/xqueryimpl.h). The eval
   CompilerCBs share the query's XQueryDiagnostics.
 
-  - theSctxMap :
+  theSctxMap :
+  ------------
   A query-level (or eval-level) map that stores the sctx objs that need to be
   kept around for the whole duration of a query (including runtime). In non-
   DEBUGGER mode, the map stores only for root sctx of each module. In DEBUGGER
@@ -60,46 +62,60 @@
   numeric ids to their associated sctx objs. The map is modified by the methods
   TranslatorImpl::end_visit(ModuleImport) and TranslatorImpl::push_scope().
 
-  - theRootSctx :
+  theRootSctx :
+  -------------
   The root static ctx for the query or for one of the query's eval exprs. For
   an eval expr, its root sctx is a child of the query's root sctx. For the query,
   its root sctx may be (a) a child of a user-provided sctx, or (b) if the query
   is a load-prolog query, the user-provided sctx, or (c) if the user did not
   provide any sctx, a child of zorba's root sctx.
 
-  - theDebuggerCommons :
-
-  - theIsEval :
+  theDebuggerCommons :
+  --------------------
+
+  theHasEval :
+  ------------
+  True if there is an eval expr within the compilation unit covered by this CCB.
+
+  theIsEval :
+  -----------
   True if this is the CCB for an eval query. This flag is needed to determine
   if a PUL returned by the main program must be applied or not.
 
-  - theIsLoadProlog :
+  theIsLoadProlog :
+  -----------------
   Whether this is a load-prolog query or not (load-prolog queries are created
   internally by the StaticContextImpl::loadProlog() method).
 
-  - theIsUpdating :
+  theIsUpdating :
+  ---------------
   Set to true if the root expr of the query or eval expr is an updating expr. 
 
-  - theTimeout :
+  theTimeout :
+  ------------
 
-  - theTempIndexCounter :
+  theTempIndexCounter :
+  ---------------------
   A counter used to create unique names for temporary (query-specific) indexes
   created to perform hashjoins (see rewriter/rules/index_join_rule.cpp).
 
-  - theConfig.lib_module :
+  theConfig.lib_module :
+  ----------------------
   If true, then if the query string that is given by the user is a library
   module, zorba will wrap it in a dummy main module and compile/execute that
   dummy module (see  XQueryCompiler::createMainModule() method). This flag is
   a copy of the lib_module flag in Zorba_CompilerHints_t.
 
-  - theConfig.for_serialization_only :
+  theConfig.for_serialization_only :
+  ----------------------------------
   This flag is a copy of the for_serialization_only flag in Zorba_CompilerHints_t.
 
-  - theConfig.parse_cb :
+  theConfig.parse_cb :
   Pointer to the function to call to print the AST that results from parsing
   the query.
 
-  - theConfig.translate_cb :
+  theConfig.translate_cb :
+  ------------------------
   Pointer to the function to call to print the expr tree that results from
   translating the query AST.
 ********************************************************************************/
@@ -152,6 +168,8 @@
   DebuggerCommons*          theDebuggerCommons;
 #endif
 
+  bool                      theHasEval;
+
   bool                      theIsEval;
 
   bool                      theIsLoadProlog;

=== modified file 'src/compiler/codegen/plan_visitor.cpp'
--- src/compiler/codegen/plan_visitor.cpp	2012-05-03 12:31:51 +0000
+++ src/compiler/codegen/plan_visitor.cpp	2012-05-06 14:39:24 +0000
@@ -2335,7 +2335,7 @@
     {
       // need to computeResultCaching here for iterprint to work
       user_function* udf = static_cast<user_function*>(func);
-      udf->computeResultCaching(theCCB->theXQueryDiagnostics);
+      udf->computeResultCaching();
     }
   }
   else

=== modified file 'src/compiler/expression/expr.cpp'
--- src/compiler/expression/expr.cpp	2012-05-03 12:31:51 +0000
+++ src/compiler/expression/expr.cpp	2012-05-06 14:39:24 +0000
@@ -42,6 +42,7 @@
 #include "compiler/expression/expr_utils.h"
 #include "compiler/expression/expr_visitor.h"
 #include "compiler/parser/parse_constants.h"
+#include "compiler/api/compilercb.h"
 
 #include "zorbaserialization/serialize_template_types.h"
 #include "zorbaserialization/serialize_zorba_types.h"
@@ -1367,6 +1368,7 @@
 
 ********************************************************************************/
 eval_expr::eval_expr(
+    CompilerCB* ccb,
     static_context* sctx,
     const QueryLoc& loc, 
     const expr_t& e,
@@ -1378,6 +1380,9 @@
   theInnerScriptingKind(scriptingKind),
   theDoNodeCopy(false)
 {
+  if (ccb)
+    ccb->theHasEval = true;
+
   compute_scripting_kind();
 }
 
@@ -1417,7 +1422,8 @@
 
 expr_t eval_expr::clone(substitution_t& s) const
 {
-  rchandle<eval_expr> new_eval = new eval_expr(theSctx, 
+  rchandle<eval_expr> new_eval = new eval_expr(NULL,
+                                               theSctx, 
                                                theLoc, 
                                                theExpr->clone(s),
                                                theInnerScriptingKind,

=== modified file 'src/compiler/expression/expr.h'
--- src/compiler/expression/expr.h	2012-05-03 12:31:51 +0000
+++ src/compiler/expression/expr.h	2012-05-06 14:39:24 +0000
@@ -1190,6 +1190,7 @@
 
 public:
   eval_expr(
+      CompilerCB* ccb,
       static_context* sctx,
       const QueryLoc& loc, 
       const expr_t& e,

=== modified file 'src/compiler/expression/var_expr.cpp'
--- src/compiler/expression/var_expr.cpp	2012-05-03 12:31:51 +0000
+++ src/compiler/expression/var_expr.cpp	2012-05-06 14:39:24 +0000
@@ -133,7 +133,7 @@
   ar & theCopyClause;
   ar & theParamPos;
   ar & theUDF;
-  ar & theSetExprs;
+  //ar & theSetExprs;
   ar & theIsPrivate;
   ar & theIsExternal;
   ar & theIsMutable;

=== modified file 'src/compiler/rewriter/rules/fold_rules.cpp'
--- src/compiler/rewriter/rules/fold_rules.cpp	2012-05-03 12:31:51 +0000
+++ src/compiler/rewriter/rules/fold_rules.cpp	2012-05-06 14:39:24 +0000
@@ -190,31 +190,7 @@
 
       if (!udf->isOptimized())
       {
-        // Set the Optimized flag in advance to prevent an infinte loop (for
-        // recursive functions, an optimization could be attempted again)
-        udf->setOptimized(true);
-
-        RewriterContext rctx(rCtx.theCCB,
-                             udf->getBody(),
-                             udf,
-                             "",
-                             udf->getBody()->get_sctx()->is_in_ordered_mode());
-
-        GENV_COMPILERSUBSYS.getDefaultOptimizingRewriter()->rewrite(rctx);
-        udf->setBody(rctx.getRoot());
-
-        if (rCtx.theCCB->theConfig.optimize_cb != NULL)
-        {
-          if (udf->getName())
-          {
-            rCtx.theCCB->theConfig.optimize_cb(udf->getBody(),
-                                               udf->getName()->getStringValue().c_str());
-          }
-          else
-          {
-            rCtx.theCCB->theConfig.optimize_cb(udf->getBody(), "inline function");
-          }
-        }
+        udf->optimize(rCtx.theCCB);
       }
 
       if (rCtx.theUDF != NULL &&

=== modified file 'src/compiler/rewriter/rules/nodeid_rules.cpp'
--- src/compiler/rewriter/rules/nodeid_rules.cpp	2012-05-03 12:31:51 +0000
+++ src/compiler/rewriter/rules/nodeid_rules.cpp	2012-05-06 14:39:24 +0000
@@ -681,7 +681,7 @@
     fo_expr* e = static_cast<fo_expr *>(node);
     function* f = e->get_func();
 
-    if (f->isUdf())
+    if (f->isUdf() && static_cast<user_function*>(f)->getBody() != NULL)
     {
       user_function* udf = static_cast<user_function*>(f);
 
@@ -1001,7 +1001,9 @@
 
 
 /*******************************************************************************
-
+  The purpose of this method is to find patrh exprs that are inside the subtree
+  of "node" and which return nodes that may propagated in the result of the
+  "node" expr.
 ********************************************************************************/
 void MarkNodeCopyProps::markForSerialization(expr* node)
 {
@@ -1013,7 +1015,7 @@
   if (TypeOps::is_subtype(tm, *retType, *rtm.ANY_ATOMIC_TYPE_STAR))
     return;
 
-  switch (node->get_expr_kind()) 
+  switch (node->get_expr_kind())
   {
   case const_expr_kind:
   {
@@ -1135,7 +1137,7 @@
 
     e->setWillBeSerialized(ANNOTATION_TRUE);
 
-    if (f->isUdf())
+    if (f->isUdf() && static_cast<user_function*>(f)->getBody() != NULL)
     {
       user_function* udf = static_cast<user_function*>(f);
       expr* body = udf->getBody();

=== modified file 'src/compiler/rewriter/tools/dataflow_annotations.cpp'
--- src/compiler/rewriter/tools/dataflow_annotations.cpp	2012-05-03 12:31:51 +0000
+++ src/compiler/rewriter/tools/dataflow_annotations.cpp	2012-05-06 14:39:24 +0000
@@ -1097,7 +1097,7 @@
     fo_expr* e = static_cast<fo_expr *>(node);
     function* f = e->get_func();
 
-    if (f->isUdf())
+    if (f->isUdf() && static_cast<user_function*>(f)->getBody() != NULL)
     {
       user_function* udf = static_cast<user_function*>(f);
  
@@ -1153,9 +1153,7 @@
           if (source->get_expr_kind() == var_expr_kind)
           {
             var_expr* argVar = static_cast<var_expr*>(source);
-            
             ZORBA_ASSERT(argVar->get_kind() == var_expr::arg_var);
-            
             expr* argExpr = e->get_arg(argVar->get_param_pos());
             
             findNodeSourcesRec(argExpr, sources, currentUdf);
@@ -1259,17 +1257,6 @@
     // is going to propagate its inputs, so find the sources in the arguments.
     // TODO: look for function_item_expr in the subtree to check if this assumption
     // is really true. 
-    dynamic_function_invocation_expr* e = 
-    static_cast<dynamic_function_invocation_expr*>(node);
-
-    const std::vector<expr_t>& args = e->get_args();
-
-    FOR_EACH(std::vector<expr_t>, ite, args)
-    {
-      std::vector<expr*> sources;
-      findNodeSourcesRec((*ite).getp(), sources, currentUdf);
-    }
-
     break;
   }
 
@@ -1312,5 +1299,367 @@
   }
 }
 
+
+/*******************************************************************************
+
+********************************************************************************/
+void SourceFinder::findLocalNodeSources(
+    expr* node,
+    std::vector<expr*>& sources)
+{
+  TypeManager* tm = node->get_type_manager();
+  RootTypeManager& rtm = GENV_TYPESYSTEM;
+
+  xqtref_t retType = node->get_return_type();
+
+  if (TypeOps::is_subtype(tm, *retType, *rtm.ANY_ATOMIC_TYPE_STAR, node->get_loc()))
+    return;
+
+  switch(node->get_expr_kind()) 
+  {
+  case const_expr_kind:
+  {
+    return;
+  }
+
+  case var_expr_kind:
+  {
+    var_expr* e = static_cast<var_expr*>(node);
+
+    switch (e->get_kind())
+    {
+    case var_expr::for_var:
+    case var_expr::let_var:
+    case var_expr::win_var:
+    case var_expr::wincond_out_var:
+    case var_expr::wincond_in_var:
+    case var_expr::groupby_var:
+    case var_expr::non_groupby_var:
+    {
+      VarSourcesMap::iterator ite = theVarSourcesMap.find(e);
+
+      std::vector<expr*>* varSources;
+
+      if (ite == theVarSourcesMap.end())
+      {
+        varSources = new std::vector<expr*>;
+        theVarSourcesMap.insert(VarSourcesPair(e, varSources));
+
+        findLocalNodeSources(e->get_domain_expr(), *varSources);
+      }
+      else
+      {
+        varSources = (*ite).second;
+      }
+
+      std::vector<expr*>::const_iterator ite2 = (*varSources).begin();
+      std::vector<expr*>::const_iterator end2 = (*varSources).end();
+      for (; ite2 != end2; ++ite2)
+      {
+        if (std::find(sources.begin(), sources.end(), *ite2) == sources.end())
+          sources.push_back(*ite2);
+      }
+
+      return;
+    }
+
+    case var_expr::wincond_in_pos_var:
+    case var_expr::wincond_out_pos_var:
+    case var_expr::pos_var:
+    case var_expr::score_var:
+    case var_expr::count_var:
+    {
+      return;
+    }
+
+    case var_expr::copy_var:
+    {
+      // A copy var holds a standalone copy of the node produced by its domain
+      // expr. Although such a copy is a constructed tree, it was not constructed
+      // by node-constructor exprs, and as a result, a copy var is not a source.
+      return;
+    }
+
+    case var_expr::arg_var:
+    {
+      return;
+    }
+
+    case var_expr::prolog_var: 
+    case var_expr::local_var:
+    {
+      VarSourcesMap::iterator ite = theVarSourcesMap.find(e);
+
+      std::vector<expr*>* varSources;
+
+      if (ite == theVarSourcesMap.end())
+      {
+        varSources = new std::vector<expr*>;;
+        theVarSourcesMap.insert(VarSourcesPair(e, varSources));
+
+        std::vector<expr*>::const_iterator ite2 = e->setExprsBegin();
+        std::vector<expr*>::const_iterator end2 = e->setExprsEnd();
+
+        for (; ite2 != end2; ++ite2)
+        {
+          expr* setExpr = *ite2;
+
+          if (setExpr->get_expr_kind() == var_decl_expr_kind)
+          {
+            findLocalNodeSources(static_cast<var_decl_expr*>(setExpr)->get_init_expr(),
+                                 *varSources);
+          }
+          else
+          {
+            assert(setExpr->get_expr_kind() == var_set_expr_kind);
+
+            findLocalNodeSources(static_cast<var_set_expr*>(setExpr)->get_expr(),
+                                 *varSources);
+          }
+        }
+      }
+      else
+      {
+        varSources = (*ite).second;
+      }
+
+      std::vector<expr*>::const_iterator ite2 = (*varSources).begin();
+      std::vector<expr*>::const_iterator end2 = (*varSources).end();
+      for (; ite2 != end2; ++ite2)
+      {
+        if (std::find(sources.begin(), sources.end(), *ite2) == sources.end())
+          sources.push_back(*ite2);
+      }
+
+      return;
+    }
+
+    case var_expr::catch_var:
+    {
+      // If in the try clause there is an fn:error that generates nodes, it will 
+      // be (conservatively) treated as a "must copy" function, so all of those
+      // nodes will be in standalone trees.
+      return;
+    }
+
+    case var_expr::eval_var:
+    default:
+    {
+      ZORBA_ASSERT(false);
+    }
+    }
+
+    break;
+  }
+
+  case doc_expr_kind:
+  case elem_expr_kind:
+  {
+    if (std::find(sources.begin(), sources.end(), node) == sources.end())
+    {
+      sources.push_back(node);
+    }
+
+    std::vector<expr*> enclosedExprs;
+    node->get_fo_exprs_of_kind(FunctionConsts::OP_ENCLOSED_1, false, enclosedExprs);
+
+    std::vector<expr*>::const_iterator ite = enclosedExprs.begin();
+    std::vector<expr*>::const_iterator end = enclosedExprs.end();
+    for (; ite != end; ++ite)
+    {
+      fo_expr* fo = static_cast<fo_expr*>(*ite);
+
+      assert(fo->get_func()->getKind() == FunctionConsts::OP_ENCLOSED_1);
+
+      findLocalNodeSources(fo, sources);
+    }
+
+    return;
+  }
+
+  case attr_expr_kind:
+  case text_expr_kind:
+  case pi_expr_kind:
+  {
+    return;
+  }
+
+  case relpath_expr_kind:
+  {
+    relpath_expr* e = static_cast<relpath_expr *>(node);
+    findLocalNodeSources((*e)[0], sources);
+    return;
+  }
+
+  case gflwor_expr_kind:
+  case flwor_expr_kind:
+  {
+    flwor_expr* e = static_cast<flwor_expr *>(node);
+
+    // We don't need to drill down to the domain exprs of variables that
+    // are not referenced in the return clause.
+    findLocalNodeSources(e->get_return_expr(), sources);
+    return;
+  }
+
+  case if_expr_kind:
+  {
+    if_expr* e = static_cast<if_expr *>(node);
+    findLocalNodeSources(e->get_then_expr(), sources);
+    findLocalNodeSources(e->get_else_expr(), sources);
+    return;
+  }
+
+  case trycatch_expr_kind:
+  {
+    break;
+  }
+
+  case fo_expr_kind:
+  {
+    fo_expr* e = static_cast<fo_expr *>(node);
+    function* f = e->get_func();
+
+    if (f->isUdf() && static_cast<user_function*>(f)->getBody() != NULL)
+    {
+      csize numArgs = e->num_args();
+      for (csize i = 0; i < numArgs; ++i)
+      {
+        findLocalNodeSources(e->get_arg(i), sources);
+      }
+    }
+    else
+    {
+      csize numArgs = e->num_args();
+      for (csize i = 0; i < numArgs; ++i)
+      {
+        if (f->propagatesInputNodes(e, i))
+        {
+          findLocalNodeSources(e->get_arg(i), sources);
+        }
+      }
+    }
+
+    return;
+  }
+
+  case promote_expr_kind:
+  case treat_expr_kind:
+  case order_expr_kind:
+  case wrapper_expr_kind:
+  case function_trace_expr_kind:
+  case extension_expr_kind:
+  case validate_expr_kind:
+  {
+    break;
+  }
+
+  case transform_expr_kind:
+  {
+    transform_expr* e = static_cast<transform_expr*>(node);
+
+    findLocalNodeSources(e->getReturnExpr(), sources);
+
+    return;
+  }
+
+  case block_expr_kind:
+  {
+    block_expr* e = static_cast<block_expr*>(node);
+
+    findLocalNodeSources((*e)[e->size()-1], sources);
+
+    return;
+  }
+
+  case var_decl_expr_kind:
+  case var_set_expr_kind:
+  {
+    return;
+  }
+
+  case apply_expr_kind:
+  {
+    break;
+  }
+
+  case exit_catcher_expr_kind: 
+  {
+    exit_catcher_expr* e = static_cast<exit_catcher_expr*>(node);
+
+    std::vector<expr*>::const_iterator ite = e->exitExprsBegin();
+    std::vector<expr*>::const_iterator end = e->exitExprsEnd();
+
+    for (; ite != end; ++ite)
+    {
+      exit_expr* ex = static_cast<exit_expr*>(*ite);
+
+      findLocalNodeSources(ex->get_expr(), sources);
+    }
+
+    break;
+  }
+
+  case eval_expr_kind:
+  {
+    eval_expr* e = static_cast<eval_expr*>(node);
+    // Make sure that the eval iterator will produce standalone trees.
+    e->setNodeCopy(true);
+    return;
+  }
+
+  case debugger_expr_kind:
+  {
+    break;
+  }
+
+  case dynamic_function_invocation_expr_kind:
+  {
+    // Conservatively assume that the function item that is going to be executed
+    // is going to propagate its inputs, so find the sources in the arguments.
+    // TODO: look for function_item_expr in the subtree to check if this assumption
+    // is really true. 
+    break;
+  }
+
+  case function_item_expr_kind:
+  {
+    //function_item_expr* e = static_cast<function_item_expr*>(node);
+    // TODO
+    return;
+  }
+    
+  case castable_expr_kind:
+  case cast_expr_kind:
+  case instanceof_expr_kind:
+  case name_cast_expr_kind:
+  case axis_step_expr_kind:
+  case match_expr_kind:
+  case delete_expr_kind:
+  case insert_expr_kind:
+  case rename_expr_kind:
+  case replace_expr_kind:
+  case while_expr_kind:
+  case exit_expr_kind:
+  case flowctl_expr_kind:
+#ifndef ZORBA_NO_FULL_TEXT
+  case ft_expr_kind:
+#endif
+  default:
+    ZORBA_ASSERT(false);
+  }
+
+  ExprIterator iter(node);
+  while(!iter.done()) 
+  {
+    expr* child = (*iter).getp();
+    if (child != NULL) 
+    {
+      findLocalNodeSources(child, sources);
+    }
+    iter.next();
+  }
+}
+
 }
 /* vim:set et sw=2 ts=2: */

=== modified file 'src/compiler/rewriter/tools/dataflow_annotations.h'
--- src/compiler/rewriter/tools/dataflow_annotations.h	2012-05-03 12:31:51 +0000
+++ src/compiler/rewriter/tools/dataflow_annotations.h	2012-05-06 14:39:24 +0000
@@ -88,11 +88,11 @@
   typedef std::pair<expr*, user_function*> SourceUdfMapPair;
 
 protected:
-  VarSourcesMap                theVarSourcesMap;
-  UdfSourcesMap                theUdfSourcesMap;
-  SourceUdfMap                 theSourceUdfMap;
+  VarSourcesMap            theVarSourcesMap;
+  UdfSourcesMap            theUdfSourcesMap;
+  SourceUdfMap             theSourceUdfMap;
 
-  user_function              * theStartingUdf;
+  user_function          * theStartingUdf;
 
 protected:
   void findNodeSourcesRec(
@@ -107,6 +107,10 @@
       expr* inExpr,
       UDFCallChain* udfChain,
       std::vector<expr*>& sources);
+
+  void findLocalNodeSources(
+      expr* inExpr,
+      std::vector<expr*>& sources);
 };
 
 

=== modified file 'src/compiler/rewriter/tools/udf_graph.cpp'
--- src/compiler/rewriter/tools/udf_graph.cpp	2012-05-03 12:31:51 +0000
+++ src/compiler/rewriter/tools/udf_graph.cpp	2012-05-06 14:39:24 +0000
@@ -198,7 +198,7 @@
 
   node->theVisitId = visit;
 
-  for (ulong i = 0; i < node->theChildren.size(); ++i)
+  for (csize i = 0; i < node->theChildren.size(); ++i)
   {
     optimizeUDFs(ccb, node->theChildren[i], visit);
   }
@@ -209,22 +209,12 @@
   user_function* udf = node->theUDF;
   expr_t body = udf->getBody();
 
-  // inline functions are optimized during translation.
-  if (udf->isOptimized())
-    return;
-
   // Note: the body can be NULL when using Plan Serialization
   while (body != NULL)
   {
-    // Set the Optimized flag in advance to prevent an infinte loop (for
-    // recursive functions, an optimization could be attempted again)
-    udf->setOptimized(true);
+    udf->optimize(ccb);
 
-    RewriterContext rctx(ccb, body, udf,
-                         zstring(),
-                         body->get_sctx()->is_in_ordered_mode());
-    GENV_COMPILERSUBSYS.getDefaultOptimizingRewriter()->rewrite(rctx);
-    body = rctx.getRoot();
+    body = udf->getBody();
 
     TypeManager* tm = body->get_type_manager();
 
@@ -253,25 +243,16 @@
     {
       udf->getSignature().returnType() = bodyType;
       if (!udf->isLeaf())
+      {
+        udf->setOptimized(false);
         continue;
+      }
     }
 #endif
 
     udf->setBody(body);
     break;
   }
-
-  if (ccb->theConfig.optimize_cb != NULL)
-  {
-    if (udf->getName())
-    {
-      ccb->theConfig.optimize_cb(body, udf->getName()->getStringValue().c_str());
-    }
-    else
-    {
-      ccb->theConfig.optimize_cb(body, "inline function");
-    }
-  }
 }
 
 

=== modified file 'src/compiler/rewriter/tools/udf_graph.h'
--- src/compiler/rewriter/tools/udf_graph.h	2012-05-03 12:31:51 +0000
+++ src/compiler/rewriter/tools/udf_graph.h	2012-05-06 14:39:24 +0000
@@ -75,19 +75,31 @@
   during the evaluation of a given expr E. We call this graph the "call-graph
   of E".
 
-  theExpr    : The expr whose call graph is represented by "this".
-  theNodes   : Maps a user_function ptr to the node that represents that udf in
-               the graph.
-  theRoot    : A "dummy" udf node representing theExpr 
-  theVisitId : During a traversal of the graph, we need to know if we reach at
-               a node that we have visited already in the same traversal. This
-               is done as follows: Every time we start a new traversal, we 
-               increment theVisitId, and pass its value to the traversal 
-               method(s). Each node has a "visit id" data member. When a
-               traversal reaches a node, we check whether the visit id of the
-               node is == to the traversal visit id. If true, we know that the
-               node has been visited already by the current traversal. Otherwise,
-               we set its visit id to the visit id of the traversal.   
+  The graph is built right after translation is done and before optimization
+  starts (see XQueryCompiler::optimize() in src/compiler/api/compiler_api.cpp)
+
+  theExpr:
+  --------
+  The expr whose call graph is represented by "this".
+
+  theNodes:
+  ---------
+  Maps a user_function ptr to the node that represents that udf in the graph.
+
+  theRoot:
+  --------
+  A "dummy" udf node representing theExpr 
+
+  theVisitId:
+  -----------
+  During a traversal of the graph, we need to know if we reach at a node that
+  we have visited already in the same traversal. This is done as follows: Every
+  time we start a new traversal, we increment theVisitId, and pass its value to
+  the traversal method(s). Each node has a "visit id" data member. When a
+  traversal reaches a node, we check whether the visit id of the node is == to
+  the traversal visit id. If true, we know that the node has been visited already
+  by the current traversal. Otherwise, we set its visit id to the visit id of
+  the traversal.   
 *******************************************************************************/
 class UDFGraph 
 {

=== modified file 'src/compiler/translator/translator.cpp'
--- src/compiler/translator/translator.cpp	2012-05-05 02:39:12 +0000
+++ src/compiler/translator/translator.cpp	2012-05-06 14:39:24 +0000
@@ -10212,7 +10212,8 @@
           scriptingKind = SEQUENTIAL_FUNC_EXPR;
         }
 
-        rchandle<eval_expr> evalExpr = new eval_expr(theRootSctx,
+        rchandle<eval_expr> evalExpr = new eval_expr(theCCB,
+                                                     theRootSctx,
                                                      loc,
                                                      foExpr->get_arg(0),
                                                      scriptingKind,
@@ -10365,7 +10366,8 @@
                                 GET_BUILTIN_FUNCTION(FN_CONCAT_N),
                                 concat_args);
 
-        rchandle<eval_expr> evalExpr = new eval_expr(theRootSctx,
+        rchandle<eval_expr> evalExpr = new eval_expr(theCCB,
+                                                     theRootSctx,
                                                      loc,
                                                      qnameExpr,
                                                      scriptingKind,
@@ -10693,7 +10695,7 @@
     // begin_visit). We need to add these to the udf obj so that they will bound
     // at runtime. We must do this here (before we optimize the inline function
     // body, because optimization may remove clauses from the flwor expr
-    for (ulong i = 0; i < flwor->num_clauses(); ++i)
+    for (csize i = 0; i < flwor->num_clauses(); ++i)
     {
       const flwor_clause* lClause = flwor->get_clause(i);
       const let_clause* letClause = dynamic_cast<const let_clause*>(lClause);
@@ -10717,7 +10719,7 @@
   // Translate the type declarations for the function params
   std::vector<xqtref_t> paramTypes;
   rchandle<ParamList> params = v.getParamList();
-  if(params != 0)
+  if (params != 0)
   {
     std::vector<rchandle<Param> >::const_iterator lIt = params->begin();
     for(; lIt != params->end(); ++lIt)

=== modified file 'src/functions/func_hoist.cpp'
--- src/functions/func_hoist.cpp	2012-05-03 12:31:51 +0000
+++ src/functions/func_hoist.cpp	2012-05-06 14:39:24 +0000
@@ -36,7 +36,7 @@
     return caller->get_arg(0)->get_return_type();
   }
 
-  bool isMap(ulong input) const
+  bool isMap(csize input) const
   {
     return true;
   }
@@ -81,7 +81,7 @@
     return caller->get_arg(0)->get_return_type();
   }
 
-  bool isMap(ulong input) const
+  bool isMap(csize input) const
   {
     return true;
   }

=== modified file 'src/functions/func_sequences_impl.h'
--- src/functions/func_sequences_impl.h	2012-05-03 12:31:51 +0000
+++ src/functions/func_sequences_impl.h	2012-05-06 14:39:24 +0000
@@ -45,7 +45,7 @@
     return caller->get_arg(0)->get_return_type();
   }
 
-  bool isMap(ulong input) const
+  bool isMap(csize input) const
   {
     return true;
   }

=== modified file 'src/functions/function.cpp'
--- src/functions/function.cpp	2012-05-03 12:31:51 +0000
+++ src/functions/function.cpp	2012-05-06 14:39:24 +0000
@@ -79,15 +79,6 @@
 /*******************************************************************************
 
 ********************************************************************************/
-xqtref_t function::getReturnType(const fo_expr*) const
-{
-  return theSignature.returnType();
-}
-
-
-/*******************************************************************************
-
-********************************************************************************/
 bool function::validate_args(std::vector<PlanIter_t>& argv) const
 {
   ulong n = theSignature.paramCount();
@@ -120,6 +111,16 @@
 
 
 /*******************************************************************************
+  This is a virstual method. It is redefined by udf and external-function
+  classes. 
+********************************************************************************/
+short function::getScriptingKind() const 
+{
+  return SIMPLE_EXPR;
+}
+
+
+/*******************************************************************************
 
 ********************************************************************************/
 bool function::isSequential() const
@@ -129,9 +130,33 @@
 
 
 /*******************************************************************************
-  Check whether this function is a map with respect to the given input
-********************************************************************************/
-bool function::isMap(ulong input) const
+  This is a virstual method. It is NOT redefined by udf and external-function
+  classes. However, for UDFs it is possible that the user-declared type is
+  narrowed down to a subtypes; see UDFGraph::optimizeUDFs() in udf_graph.cpp
+********************************************************************************/
+xqtref_t function::getReturnType(const fo_expr*) const
+{
+  return theSignature.returnType();
+}
+
+
+/*******************************************************************************
+  This is a virstual method. It is redefined by udf and external-function
+  classes. 
+********************************************************************************/
+bool function::accessesDynCtx() const 
+{
+  return false;
+}
+
+
+/*******************************************************************************
+  Check whether this function is a map with respect to the given input.
+
+  This is a virstual method. It is NOT redefined by udf and external-function
+  classes. TODO ???? redefine it for UDFs
+********************************************************************************/
+bool function::isMap(csize input) const
 {
   if (!theSignature.isVariadic() &&
       theSignature.paramCount() > 0 &&
@@ -151,6 +176,8 @@
 ********************************************************************************/
 bool function::propagatesInputNodes(expr* fo, csize input) const
 {
+  assert(!isUdf());
+
   TypeManager* tm = fo->get_type_manager();
 
   // This method should be called only if the function may indeed return nodes
@@ -179,6 +206,8 @@
 ********************************************************************************/
 bool function::mustCopyInputNodes(expr* fo, csize input) const
 {
+  assert(!isUdf());
+
   TypeManager* tm = fo->get_type_manager();
 
   xqtref_t argType = static_cast<fo_expr*>(fo)->get_arg(input)->get_return_type();
@@ -240,6 +269,9 @@
   input parameter is in document order or not. The decision may depend on
   whether the result of this function, at the point where it is called, must
   be in doc order or not.
+
+  This is a virstual method. It is redefined by the udf class but not by the
+  external-function class. 
 ********************************************************************************/
 BoolAnnotationValue function::ignoresSortedNodes(expr* fo, csize input) const
 {
@@ -266,6 +298,9 @@
   input parameter contains duplicate nodes or not. The decision may depend on
   whether the result of this function, at the point where it is called, must
   contain distinct nodes or not.
+
+  This is a virstual method. It is redefined by the udf class but not by the
+  external-function class. 
 ********************************************************************************/
 BoolAnnotationValue function::ignoresDuplicateNodes(expr* fo, csize input) const
 {

=== modified file 'src/functions/function.h'
--- src/functions/function.h	2012-05-03 12:31:51 +0000
+++ src/functions/function.h	2012-05-06 14:39:24 +0000
@@ -165,13 +165,13 @@
 
   bool isSequential() const;
 
-  virtual short getScriptingKind() const { return SIMPLE_EXPR; }
+  virtual short getScriptingKind() const;
 
   virtual xqtref_t getReturnType(const fo_expr* caller) const;
 
-  virtual bool accessesDynCtx() const { return false; }
+  virtual bool accessesDynCtx() const;
 
-  virtual bool isMap(ulong input) const;
+  virtual bool isMap(csize input) const;
 
   virtual bool propagatesInputNodes(expr* fo, csize input) const;
 

=== modified file 'src/functions/function_consts.h'
--- src/functions/function_consts.h	2012-05-03 12:31:51 +0000
+++ src/functions/function_consts.h	2012-05-06 14:39:24 +0000
@@ -256,7 +256,8 @@
   isBuiltin = 16,
   isUDF = 32,
   propagatesInputNodes = 64,
-  mustCopyInputNodes = 128
+  mustCopyInputNodes = 128,
+  AccessesDynCtx = 256
 } AnnotationFlags;
 
 };

=== modified file 'src/functions/pregenerated/func_accessors.h'
--- src/functions/pregenerated/func_accessors.h	2012-05-03 12:31:51 +0000
+++ src/functions/pregenerated/func_accessors.h	2012-05-06 14:39:24 +0000
@@ -134,7 +134,7 @@
 
   xqtref_t getReturnType(const fo_expr* caller) const;
 
-  bool isMap(ulong producer) const { return producer == 0; }
+  bool isMap(csize producer) const { return producer == 0; }
 
   BoolAnnotationValue ignoresSortedNodes(expr* fo, csize producer) const;
 

=== modified file 'src/functions/pregenerated/func_errors_and_diagnostics.h'
--- src/functions/pregenerated/func_errors_and_diagnostics.h	2012-05-03 12:31:51 +0000
+++ src/functions/pregenerated/func_errors_and_diagnostics.h	2012-05-06 14:39:24 +0000
@@ -68,7 +68,7 @@
 
   }
 
-  bool isMap(ulong producer) const { return producer == 0; }
+  bool isMap(csize producer) const { return producer == 0; }
 
   bool propagatesDistinctNodes(csize producer) const { return producer == 0; }
 

=== modified file 'src/functions/pregenerated/func_sequences.h'
--- src/functions/pregenerated/func_sequences.h	2012-05-03 12:31:51 +0000
+++ src/functions/pregenerated/func_sequences.h	2012-05-06 14:39:24 +0000
@@ -319,7 +319,7 @@
 
   xqtref_t getReturnType(const fo_expr* caller) const;
 
-  bool isMap(ulong producer) const { return producer == 0; }
+  bool isMap(csize producer) const { return producer == 0; }
 
   bool propagatesDistinctNodes(csize producer) const { return producer == 0; }
 

=== modified file 'src/functions/udf.cpp'
--- src/functions/udf.cpp	2012-05-03 12:31:51 +0000
+++ src/functions/udf.cpp	2012-05-06 14:39:24 +0000
@@ -1,3 +1,4 @@
+
 /*
  * Copyright 2006-2008 The FLWOR Foundation.
  *
@@ -24,12 +25,14 @@
 #include "compiler/api/compilercb.h"
 #include "compiler/rewriter/framework/rewriter_context.h"
 #include "compiler/rewriter/framework/rewriter.h"
+#include "compiler/rewriter/tools/dataflow_annotations.h"
 
 #include "functions/udf.h"
 #include "annotations/annotations.h"
 #include "functions/function_impl.h"
 
 #include "diagnostics/xquery_warning.h"
+#include "diagnostics/assert.h"
 
 #include "types/typeops.h"
 
@@ -53,12 +56,13 @@
     const signature& sig,
     expr_t expr_body,
     short scriptingKind,
-    CompilerCB* compilerCB)
+    CompilerCB* ccb)
   :
   function(sig, FunctionConsts::FN_UNKNOWN),
+  theCCB(ccb),
   theLoc(loc),
+  theScriptingKind(scriptingKind),
   theBodyExpr(expr_body),
-  theScriptingKind(scriptingKind),
   theIsExiting(false),
   theIsLeaf(true),
   theIsOptimized(false),
@@ -71,7 +75,7 @@
   resetFlag(FunctionConsts::isBuiltin);
   setDeterministic(true);
   setPrivate(false);
-  theLocalUdfs = compilerCB->get_local_udfs();
+  theLocalUdfs = ccb->get_local_udfs();
   theLocalUdfs->push_back(this);
 }
 
@@ -85,6 +89,8 @@
 {
   setFlag(FunctionConsts::isUDF);
   resetFlag(FunctionConsts::isBuiltin);
+
+  theIsOptimized = true;
 }
 
 
@@ -124,17 +130,52 @@
 {
   if (ar.is_serializing_out())
   {
-    try
-    {
-      //uint32_t planStateSize;
-      //getPlan(ar.compiler_cb, planStateSize);
-      assert(thePlan != NULL);
-      ZORBA_ASSERT(thePlan != NULL);
-    }
-    catch(...)
-    {
-      // cannot compile user defined function, maybe it is not even used,
-      // so don't fire an error yet
+    // This assertion is here because of the prepare_for_serialize() call in
+    // xqueryimpl.cpp
+    ZORBA_ASSERT(thePlan != NULL);
+
+    uint32_t planStateSize;
+    getPlan(ar.get_ccb(), planStateSize);
+    ZORBA_ASSERT(thePlan != NULL);
+
+    computeResultCaching();
+    
+    if (ar.get_ccb()->theHasEval)
+    {
+      SourceFinder sourceFinder;
+      std::vector<expr*> sources;
+      sourceFinder.findLocalNodeSources(theBodyExpr, sources);
+
+      if (!sources.empty())
+      {
+        std::vector<expr*>::const_iterator ite = sources.begin();
+        std::vector<expr*>::const_iterator end = sources.end();
+        for (; ite != end; ++ite)
+        {
+          expr* source = (*ite);
+    
+          if (source->get_expr_kind() == doc_expr_kind)
+          {
+            doc_expr* e = static_cast<doc_expr*>(source);
+            if (!e->copyInputNodes())
+              e->setCopyInputNodes();
+          }
+          else if (source->get_expr_kind() == elem_expr_kind)
+          {
+            elem_expr* e = static_cast<elem_expr*>(source);
+            if (!e->copyInputNodes())
+              e->setCopyInputNodes();
+          }
+          else
+          {
+            ZORBA_ASSERT(false);
+          }
+        }
+
+        invalidatePlan();
+        getPlan(ar.get_ccb(), planStateSize);
+        ZORBA_ASSERT(thePlan != NULL);
+      }
     }
   }
   else
@@ -144,14 +185,23 @@
   }
 
   serialize_baseclass(ar, (function*)this);
-  ar & theLoc;
-  ar & theBodyExpr;
-  ar & theArgVars;
+  ar & theCCB;
+  //ar & theLoc;
   ar & theScriptingKind;
-  ar & theIsExiting;
-  ar & theIsLeaf;
-  ar & theMutuallyRecursiveUDFs;
-  ar & theIsOptimized;
+  //ar & theBodyExpr;
+  //ar & theArgVars;
+  ar & theIgnoresSortedNodes;
+  ar & theIgnoresDuplicateNodes;
+
+  ar & theMustCopyInputNodes;
+  ar & thePropagatesInputNodes;
+
+  //ar & theIsExiting;
+  //ar & theIsLeaf;
+  //ar & theMutuallyRecursiveUDFs;
+
+  // ar & theIsOptimized;
+
   ar & thePlan;
   ar & thePlanStateSize;
   ar & theArgVarsRefs;
@@ -188,13 +238,6 @@
   // but the function body is not really updating/sequential, an error/warning is
   // raised by the translator.
   return theScriptingKind;
-
-#if 0
-  if (theBodyExpr == NULL)
-    return theScriptingKind;
-
-  return theBodyExpr->get_scripting_detail();
-#endif
 }
 
 
@@ -241,6 +284,8 @@
     const std::vector<user_function*>& udfs,
     const std::vector<user_function*>::const_iterator& cycle)
 {
+  assert(theBodyExpr != NULL);
+
   theMutuallyRecursiveUDFs.insert(theMutuallyRecursiveUDFs.end(),
                                   cycle,
                                   udfs.end());
@@ -252,6 +297,8 @@
 ********************************************************************************/
 void user_function::addRecursiveCall(expr* call)
 {
+  assert(theBodyExpr != NULL);
+
   if (std::find(theRecursiveCalls.begin(), theRecursiveCalls.end(), call) ==
       theRecursiveCalls.end())
   {
@@ -266,6 +313,7 @@
 bool user_function::isRecursive() const
 {
   assert(isOptimized());
+  assert(theBodyExpr != NULL);
   return !theMutuallyRecursiveUDFs.empty();
 }
 
@@ -276,6 +324,7 @@
 bool user_function::isMutuallyRecursiveWith(const user_function* udf)
 {
   assert(isOptimized());
+  assert(theBodyExpr != NULL);
 
   if (std::find(theMutuallyRecursiveUDFs.begin(), 
                 theMutuallyRecursiveUDFs.end(),
@@ -298,8 +347,31 @@
     assert(isOptimized());
   }
 
-  // All undeclared functions unfoldable. TODO: better analysis
-  return (theBodyExpr == NULL || theBodyExpr->isUnfoldable());
+  return testFlag(FunctionConsts::AccessesDynCtx);
+}
+
+
+/*******************************************************************************
+  This method may be called after deserializing a query plan, and the query
+  contains an eval expr.
+********************************************************************************/
+bool user_function::mustCopyInputNodes(expr* fo, csize input) const
+{
+  assert(theBodyExpr == NULL);
+
+  return (theMustCopyInputNodes[input] == 0 ? false : true);
+}
+
+
+/*******************************************************************************
+  This method may be called after deserializing a query plan, and the query
+  contains an eval expr.
+********************************************************************************/
+bool user_function::propagatesInputNodes(expr* fo, csize input) const
+{
+  assert(theBodyExpr == NULL);
+
+  return (thePropagatesInputNodes[input] == 0 ? false : true);
 }
 
 
@@ -309,8 +381,14 @@
 BoolAnnotationValue user_function::ignoresSortedNodes(expr* fo, csize input) const
 {
   assert(isOptimized());
+
+  if (theBodyExpr == NULL)
+  {
+    assert(input < theIgnoresSortedNodes.size());
+    return static_cast<BoolAnnotationValue>(theIgnoresSortedNodes[input]);
+  }
+
   assert(input < theArgVars.size());
-
   return theArgVars[input]->getIgnoresSortedNodes();
 }
 
@@ -323,8 +401,14 @@
     csize input) const
 {
   assert(isOptimized());
+
+  if (theBodyExpr == NULL)
+  {
+    assert(input < theIgnoresDuplicateNodes.size());
+    return static_cast<BoolAnnotationValue>(theIgnoresDuplicateNodes[input]);
+  }
+
   assert(input < theArgVars.size());
-
   return theArgVars[input]->getIgnoresDuplicateNodes();
 }
 
@@ -332,9 +416,61 @@
 /*******************************************************************************
 
 ********************************************************************************/
-const std::vector<user_function::ArgVarRefs>& user_function::getArgVarsRefs() const
+void user_function::optimize(CompilerCB* ccb)
 {
-  return theArgVarsRefs;
+  ZORBA_ASSERT(theBodyExpr);
+
+  if (!theIsOptimized && 
+      ccb->theConfig.opt_level > CompilerCB::config::O0)
+  {
+    // Set the Optimized flag in advance to prevent an infinte loop (for
+    // recursive functions, an optimization could be attempted again)
+    theIsOptimized = true;
+
+    csize numParams = theArgVars.size();
+
+    expr_t body = getBody();
+
+    RewriterContext rctx(ccb,
+                         body,
+                         this,
+                         zstring(),
+                         body->get_sctx()->is_in_ordered_mode());
+
+    GENV_COMPILERSUBSYS.getDefaultOptimizingRewriter()->rewrite(rctx);
+    body = rctx.getRoot();
+    setBody(body);
+
+    if (theBodyExpr->isUnfoldable())
+      setFlag(FunctionConsts::AccessesDynCtx);
+
+    numParams = theArgVars.size();
+
+    theIgnoresSortedNodes.resize(numParams);
+    theIgnoresDuplicateNodes.resize(numParams);
+    theMustCopyInputNodes.resize(numParams);
+    thePropagatesInputNodes.resize(numParams);
+
+    for (csize i = 0; i < numParams; ++i)
+    {
+      theIgnoresSortedNodes[i] = theArgVars[i]->getIgnoresSortedNodes();
+      theIgnoresDuplicateNodes[i] = theArgVars[i]->getIgnoresDuplicateNodes();
+      theMustCopyInputNodes[i] = 1;
+      thePropagatesInputNodes[i] = 1;
+    }
+
+    if (ccb->theConfig.optimize_cb != NULL)
+    {
+      if (getName())
+      {
+        ccb->theConfig.optimize_cb(body, getName()->getStringValue().c_str());
+      }
+      else
+      {
+        ccb->theConfig.optimize_cb(body, "inline function");
+      }
+    }
+  }
 }
 
 
@@ -355,23 +491,7 @@
 {
   if (thePlan == NULL)
   {
-    if (!theIsOptimized && 
-        ccb->theConfig.opt_level > CompilerCB::config::O0)
-    {
-      theIsOptimized = true;
-
-      expr_t body = getBody();
-
-      RewriterContext rctx(ccb,
-                           body,
-                           this,
-                           zstring(),
-                           body->get_sctx()->is_in_ordered_mode());
-
-      GENV_COMPILERSUBSYS.getDefaultOptimizingRewriter()->rewrite(rctx);
-      body = rctx.getRoot();
-      setBody(body);
-    }
+    optimize(ccb);
 
     csize numArgs = theArgVars.size();
 
@@ -405,6 +525,29 @@
 /*******************************************************************************
 
 ********************************************************************************/
+PlanIter_t user_function::codegen(
+      CompilerCB* cb,
+      static_context* sctx,
+      const QueryLoc& loc,
+      std::vector<PlanIter_t>& argv,
+      expr& ann) const
+{
+  return new UDFunctionCallIterator(sctx, loc, argv, this);
+}
+
+
+/*******************************************************************************
+
+********************************************************************************/
+const std::vector<user_function::ArgVarRefs>& user_function::getArgVarsRefs() const
+{
+  return theArgVarsRefs;
+}
+
+
+/*******************************************************************************
+
+********************************************************************************/
 store::Index* user_function::getCache() const
 {
   return theCache.getp();
@@ -433,7 +576,7 @@
  only cache recursive (non-sequential, non-updating, deterministic)
  functions with singleton atomic input and output
 ********************************************************************************/
-void user_function::computeResultCaching(XQueryDiagnostics* diag)
+void user_function::computeResultCaching()
 {
   if (theCacheComputed)
   {
@@ -460,6 +603,8 @@
     }
   };
 
+  XQueryDiagnostics* diag = theCCB->theXQueryDiagnostics;
+
   // will be destroyed when the function is exited
   // set caching to true if cache() is called
   OnExit lExit(theCacheResults, theCacheComputed);
@@ -483,16 +628,16 @@
     if (lExplicitCacheRequest)
     {
       diag->add_warning(
-        NEW_XQUERY_WARNING(zwarn::ZWST0005_CACHING_NOT_POSSIBLE,
-        WARN_PARAMS(getName()->getStringValue(), ZED(ZWST0005_VARIADIC)),
-        WARN_LOC(theLoc)));
+      NEW_XQUERY_WARNING(zwarn::ZWST0005_CACHING_NOT_POSSIBLE,
+      WARN_PARAMS(getName()->getStringValue(), ZED(ZWST0005_VARIADIC)),
+      WARN_LOC(theLoc)));
     }
     return;
   }
 
   // parameter and return types are subtype of xs:anyAtomicType?
   const xqtref_t& lRes = theSignature.returnType();
-  TypeManager* tm = lRes->get_manager();
+  TypeManager* tm = theBodyExpr->get_sctx()->get_typemanager();
 
   if (!TypeOps::is_subtype(tm,
                            *lRes,
@@ -502,11 +647,11 @@
     if (lExplicitCacheRequest)
     {
       diag->add_warning(
-        NEW_XQUERY_WARNING(zwarn::ZWST0005_CACHING_NOT_POSSIBLE,
-        WARN_PARAMS(getName()->getStringValue(),
-                    ZED(ZWST0005_RETURN_TYPE),
-                    lRes->toString()),
-        WARN_LOC(theLoc)));
+      NEW_XQUERY_WARNING(zwarn::ZWST0005_CACHING_NOT_POSSIBLE,
+      WARN_PARAMS(getName()->getStringValue(),
+                  ZED(ZWST0005_RETURN_TYPE),
+                  lRes->toString()),
+      WARN_LOC(theLoc)));
     }
     return;
   }
@@ -523,12 +668,12 @@
       if (lExplicitCacheRequest)
       {
         diag->add_warning(
-            NEW_XQUERY_WARNING(zwarn::ZWST0005_CACHING_NOT_POSSIBLE,
-            WARN_PARAMS(getName()->getStringValue(),
-                        ZED(ZWST0005_PARAM_TYPE),
-                        i+1,
-                        lArg->toString()),
-            WARN_LOC(theLoc)));
+        NEW_XQUERY_WARNING(zwarn::ZWST0005_CACHING_NOT_POSSIBLE,
+        WARN_PARAMS(getName()->getStringValue(),
+                    ZED(ZWST0005_PARAM_TYPE),
+                    i+1,
+                    lArg->toString()),
+        WARN_LOC(theLoc)));
       }
       return;
     }
@@ -540,9 +685,9 @@
     if (lExplicitCacheRequest)
     {
       diag->add_warning(
-        NEW_XQUERY_WARNING(zwarn::ZWST0005_CACHING_NOT_POSSIBLE,
-        WARN_PARAMS(getName()->getStringValue(), ZED(ZWST0005_UPDATING)),
-        WARN_LOC(theLoc)));
+      NEW_XQUERY_WARNING(zwarn::ZWST0005_CACHING_NOT_POSSIBLE,
+      WARN_PARAMS(getName()->getStringValue(), ZED(ZWST0005_UPDATING)),
+      WARN_LOC(theLoc)));
     }
     return;
   }
@@ -552,10 +697,10 @@
     if (lExplicitCacheRequest)
     {
       diag->add_warning(
-        NEW_XQUERY_WARNING(zwarn::ZWST0006_CACHING_MIGHT_NOT_BE_INTENDED,
-        WARN_PARAMS(getName()->getStringValue(),
-                    (isSequential()?"sequential":"non-deterministic")),
-        WARN_LOC(theLoc)));
+      NEW_XQUERY_WARNING(zwarn::ZWST0006_CACHING_MIGHT_NOT_BE_INTENDED,
+      WARN_PARAMS(getName()->getStringValue(),
+                  (isSequential()?"sequential":"non-deterministic")),
+      WARN_LOC(theLoc)));
 
       lExit.cache();
     }
@@ -573,20 +718,6 @@
 }
 
 
-/*******************************************************************************
-
-********************************************************************************/
-PlanIter_t user_function::codegen(
-      CompilerCB* cb,
-      static_context* sctx,
-      const QueryLoc& loc,
-      std::vector<PlanIter_t>& argv,
-      expr& ann) const
-{
-  return new UDFunctionCallIterator(sctx, loc, argv, this);
-}
-
-
 }
 
 /* vim:set et sw=2 ts=2: */

=== modified file 'src/functions/udf.h'
--- src/functions/udf.h	2012-05-03 12:31:51 +0000
+++ src/functions/udf.h	2012-05-06 14:39:24 +0000
@@ -111,12 +111,19 @@
   typedef std::vector<LetVarIter_t> ArgVarRefs;
 
 private:
+  CompilerCB                * theCCB;
+
   QueryLoc                    theLoc;
 
+  short                       theScriptingKind;
+
   expr_t                      theBodyExpr;
   std::vector<var_expr_t>     theArgVars;
 
-  short                       theScriptingKind;
+  std::vector<int>            theIgnoresSortedNodes;
+  std::vector<int>            theIgnoresDuplicateNodes;
+  std::vector<int>            theMustCopyInputNodes;
+  std::vector<int>            thePropagatesInputNodes;
 
   bool                        theIsExiting;
   bool                        theIsLeaf;
@@ -130,7 +137,7 @@
   uint32_t                    thePlanStateSize;
   std::vector<ArgVarRefs>     theArgVarsRefs;
 
-  store::Index_t              theCache; //note: not for serialization
+  store::Index_t              theCache;
   bool                        theCacheResults;
   bool                        theCacheComputed;
 
@@ -152,8 +159,6 @@
 
   virtual ~user_function();
 
-  short getScriptingKind() const;
-
   //xqtref_t getUDFReturnType(static_context* sctx) const;
 
   const QueryLoc& getLoc() const { return theLoc; }
@@ -176,10 +181,6 @@
 
   var_expr* getArgVar(csize i) const { return theArgVars[i].getp(); }
 
-  void setOptimized(bool v) { theIsOptimized = v; }
-
-  bool isOptimized() const { return theIsOptimized; }
-
   void addMutuallyRecursiveUDFs(
       const std::vector<user_function*>& udfs,
       const std::vector<user_function*>::const_iterator& cycle);
@@ -192,15 +193,38 @@
 
   bool isRecursive() const;
 
+  void setOptimized(bool v) { theIsOptimized = v; }
+
+  bool isOptimized() const { return theIsOptimized; }
+
+  void optimize(CompilerCB* ccb);
+
+  PlanIter_t getPlan(CompilerCB* cb, uint32_t& planStateSize);
+  
+  void invalidatePlan();
+
+  PlanIter_t codegen(
+        CompilerCB* cb,
+        static_context* sctx,
+        const QueryLoc& loc,
+        std::vector<PlanIter_t>& argv,
+        expr& ann) const;
+
+  // The next 6 methods are virtual methods of class function, which are redefined here
+
+  short getScriptingKind() const;
+
   bool accessesDynCtx() const;
 
+  bool mustCopyInputNodes(expr* fo, csize input) const;
+
+  bool propagatesInputNodes(expr* fo, csize input) const;
+
   BoolAnnotationValue ignoresSortedNodes(expr* fo, csize input) const;
 
   BoolAnnotationValue ignoresDuplicateNodes(expr* fo, csize input) const;
 
-  PlanIter_t getPlan(CompilerCB* cb, uint32_t& planStateSize);
-  
-  void invalidatePlan();
+  // Runtime-related methods
 
   void setPlaneStateSize(uint32_t size) { thePlanStateSize = size; }
 
@@ -212,14 +236,7 @@
 
   bool cacheResults() const;
 
-  void computeResultCaching(XQueryDiagnostics*);
-
-  PlanIter_t codegen(
-        CompilerCB* cb,
-        static_context* sctx,
-        const QueryLoc& loc,
-        std::vector<PlanIter_t>& argv,
-        expr& ann) const;
+  void computeResultCaching();
 };
 
 

=== modified file 'src/runtime/spec/codegen-h.xq'
--- src/runtime/spec/codegen-h.xq	2012-05-03 12:31:51 +0000
+++ src/runtime/spec/codegen-h.xq	2012-05-06 14:39:24 +0000
@@ -241,7 +241,7 @@
       else if (name($meth) eq 'zorba:isMap')
       then
         string-join(($gen:newline, $gen:indent,
-                     'bool isMap(ulong producer) const ',
+                     'bool isMap(csize producer) const ',
                      '{ return producer == ', $meth/@producer, '; }',
                       $gen:newline),'')
 

=== modified file 'src/zorbaserialization/archiver.cpp'
--- src/zorbaserialization/archiver.cpp	2012-05-03 12:31:51 +0000
+++ src/zorbaserialization/archiver.cpp	2012-05-06 14:39:24 +0000
@@ -119,15 +119,16 @@
   theSerializingOut(is_serializing_out),
   theSerializeBaseClass(false),
   theIsTempField(0),
+  theCompilerCB(NULL),
   theFieldCounter(0),
   theRootField(0),
   theCurrentCompoundField(0),
   theCurrentLevel(0),
   theNonClassFieldsMap(0),
   theClassFieldsMap(0),
+  theOnlyForEval(0),
   all_reference_list(0),
   internal_archive(internal_archive),
-  theOnlyForEval(0),
   theSerializeEverything(false),
   loading_hardcoded_objects(false),
   theAllowDelay2(ALLOW_DELAY),

=== modified file 'src/zorbaserialization/archiver.h'
--- src/zorbaserialization/archiver.h	2012-05-03 12:31:51 +0000
+++ src/zorbaserialization/archiver.h	2012-05-06 14:39:24 +0000
@@ -397,6 +397,8 @@
   std::stack<unsigned int>      limit_temp_level_stack;
 
 
+  CompilerCB                  * theCompilerCB;
+
   int                           theFieldCounter;
 
   archive_field               * theRootField;
@@ -437,6 +439,10 @@
 
   virtual ~Archiver();
 
+  void set_ccb(CompilerCB* ccb) { theCompilerCB = ccb; }
+
+  CompilerCB* get_ccb() const { return theCompilerCB; }
+
   bool is_serializing_out() { return theSerializingOut; }
 
   void set_loading_hardcoded_objects(bool v) { loading_hardcoded_objects = v; }

=== added file 'test/rbkt/ExpQueryResults/zorba/eval/eval14.xml.res'
--- test/rbkt/ExpQueryResults/zorba/eval/eval14.xml.res	1970-01-01 00:00:00 +0000
+++ test/rbkt/ExpQueryResults/zorba/eval/eval14.xml.res	2012-05-06 14:39:24 +0000
@@ -0,0 +1,2 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<root><b><c/></b></root>

=== added file 'test/rbkt/Queries/zorba/eval/eval14.xq'
--- test/rbkt/Queries/zorba/eval/eval14.xq	1970-01-01 00:00:00 +0000
+++ test/rbkt/Queries/zorba/eval/eval14.xq	2012-05-06 14:39:24 +0000
@@ -0,0 +1,30 @@
+
+import module namespace refl = "http://www.zorba-xquery.com/modules/reflection";;
+
+
+declare variable $doc :=
+<a>
+  <b>
+    <c/>
+  </b>
+</a>
+;
+
+
+declare variable $x := <root>{$doc/b}</root>;
+
+
+declare function local:dummy($n as node())
+{
+  $n
+};
+
+
+declare function local:foo($n as node()) as node()*
+{
+  local:dummy($n),
+  $x/b
+};
+
+
+refl:eval("local:foo(<a/>)/..")

=== modified file 'test/rbkt/testdriver.cpp'
--- test/rbkt/testdriver.cpp	2012-05-03 12:31:51 +0000
+++ test/rbkt/testdriver.cpp	2012-05-06 14:39:24 +0000
@@ -26,7 +26,7 @@
 #include <time.h>
 #endif
 
-//#define ZORBA_TEST_PLAN_SERIALIZATION
+#define ZORBA_TEST_PLAN_SERIALIZATION
 
 #include "testdriverconfig.h" // SRC and BIN dir definitions
 #include "specification.h" // parsing spec files


Follow ups