src/xmlpatterns/expr/qevaluationcache.cpp
changeset 0 1918ee327afb
child 4 3b1da2848fc7
equal deleted inserted replaced
-1:000000000000 0:1918ee327afb
       
     1 /****************************************************************************
       
     2 **
       
     3 ** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
       
     4 ** All rights reserved.
       
     5 ** Contact: Nokia Corporation (qt-info@nokia.com)
       
     6 **
       
     7 ** This file is part of the QtXmlPatterns module of the Qt Toolkit.
       
     8 **
       
     9 ** $QT_BEGIN_LICENSE:LGPL$
       
    10 ** No Commercial Usage
       
    11 ** This file contains pre-release code and may not be distributed.
       
    12 ** You may use this file in accordance with the terms and conditions
       
    13 ** contained in the Technology Preview License Agreement accompanying
       
    14 ** this package.
       
    15 **
       
    16 ** GNU Lesser General Public License Usage
       
    17 ** Alternatively, this file may be used under the terms of the GNU Lesser
       
    18 ** General Public License version 2.1 as published by the Free Software
       
    19 ** Foundation and appearing in the file LICENSE.LGPL included in the
       
    20 ** packaging of this file.  Please review the following information to
       
    21 ** ensure the GNU Lesser General Public License version 2.1 requirements
       
    22 ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
       
    23 **
       
    24 ** In addition, as a special exception, Nokia gives you certain additional
       
    25 ** rights.  These rights are described in the Nokia Qt LGPL Exception
       
    26 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
       
    27 **
       
    28 ** If you have questions regarding the use of this file, please contact
       
    29 ** Nokia at qt-info@nokia.com.
       
    30 **
       
    31 **
       
    32 **
       
    33 **
       
    34 **
       
    35 **
       
    36 **
       
    37 **
       
    38 ** $QT_END_LICENSE$
       
    39 **
       
    40 ****************************************************************************/
       
    41 
       
    42 /**
       
    43  * @file
       
    44  * @short This file is included by qevaluationcache_p.h.
       
    45  * If you need includes in this file, put them in qevaluationcache_p.h, outside of the namespace.
       
    46  */
       
    47 
       
    48 template<bool IsForGlobal>
       
    49 EvaluationCache<IsForGlobal>::EvaluationCache(const Expression::Ptr &op,
       
    50                                               const VariableDeclaration::Ptr &varDecl,
       
    51                                               const VariableSlotID aSlot) : SingleContainer(op)
       
    52                                                                           , m_declaration(varDecl)
       
    53                                                                           , m_varSlot(aSlot)
       
    54 {
       
    55     Q_ASSERT(m_declaration);
       
    56     Q_ASSERT(m_varSlot > -1);
       
    57 }
       
    58 
       
    59 template<bool IsForGlobal>
       
    60 DynamicContext::Ptr EvaluationCache<IsForGlobal>::topFocusContext(const DynamicContext::Ptr &context)
       
    61 {
       
    62     DynamicContext::Ptr result(context);
       
    63 
       
    64     while(true)
       
    65     {
       
    66         DynamicContext::Ptr candidate(result->previousContext());
       
    67 
       
    68         /* We want the top focus, not GenericDynamicContext. */
       
    69         if(candidate && candidate->focusIterator())
       
    70             result = candidate;
       
    71         else
       
    72             return result;
       
    73     }
       
    74 }
       
    75 
       
    76 template<bool IsForGlobal>
       
    77 Item EvaluationCache<IsForGlobal>::evaluateSingleton(const DynamicContext::Ptr &context) const
       
    78 {
       
    79     ItemCacheCell &cell = IsForGlobal ? context->globalItemCacheCell(m_varSlot) : context->itemCacheCell(m_varSlot);
       
    80 
       
    81     if(cell.cacheState == ItemCacheCell::Full)
       
    82         return cell.cachedItem;
       
    83     else
       
    84     {
       
    85         Q_ASSERT(cell.cacheState == ItemCacheCell::Empty);
       
    86         cell.cachedItem = m_operand->evaluateSingleton(IsForGlobal ? topFocusContext(context) : context);
       
    87         cell.cacheState = ItemCacheCell::Full;
       
    88         return cell.cachedItem;
       
    89     }
       
    90 }
       
    91 
       
    92 #if defined(Q_OS_IRIX) && defined(Q_CC_MIPS)
       
    93 /**
       
    94  * @short Compile workaround for MIPSPro on IRIX.
       
    95  *
       
    96  * This function is never called.
       
    97  *
       
    98  * It's mere presence means the MIPSPro compiler can accept some other code below.
       
    99  *
       
   100  * I recommend Buddism.
       
   101  */
       
   102 static inline Item::Iterator::Ptr workaroundIrixMIPSPro(const ItemSequenceCacheCell &cell)
       
   103 {
       
   104   return Item::Iterator::Ptr(new ListIterator<Item, Item::List>(cell.cachedItems));
       
   105 }
       
   106 #endif
       
   107 
       
   108 template<bool IsForGlobal>
       
   109 Item::Iterator::Ptr EvaluationCache<IsForGlobal>::evaluateSequence(const DynamicContext::Ptr &context) const
       
   110 {
       
   111     ItemSequenceCacheCell::Vector &cells = IsForGlobal ? context->globalItemSequenceCacheCells(m_varSlot) : context->itemSequenceCacheCells(m_varSlot);
       
   112     ItemSequenceCacheCell &cell = cells[m_varSlot];
       
   113 
       
   114 
       
   115     if(cell.inUse)
       
   116     {
       
   117         context->error(QtXmlPatterns::tr("Circularity detected"),
       
   118                        ReportContext::XTDE0640, this);
       
   119     }
       
   120 
       
   121     switch(cell.cacheState)
       
   122     {
       
   123         case ItemSequenceCacheCell::Full:
       
   124         {
       
   125             /**
       
   126              * We don't use makeListIterator() here because the MIPSPro compiler can't handle it.
       
   127              */
       
   128             return Item::Iterator::Ptr(new ListIterator<Item, Item::List>(cell.cachedItems));
       
   129         }
       
   130         case ItemSequenceCacheCell::Empty:
       
   131         {
       
   132             cell.inUse = true;
       
   133             cell.sourceIterator = m_operand->evaluateSequence(IsForGlobal ? topFocusContext(context) : context);
       
   134             cell.cacheState = ItemSequenceCacheCell::PartiallyPopulated;
       
   135             /* Fallthrough. */
       
   136         }
       
   137         case ItemSequenceCacheCell::PartiallyPopulated:
       
   138         {
       
   139             cell.inUse = false;
       
   140             Q_ASSERT_X(cells.at(m_varSlot).sourceIterator, Q_FUNC_INFO,
       
   141                        "This trigger for a cache bug which hasn't yet been analyzed.");
       
   142             return Item::Iterator::Ptr(new CachingIterator(cells, m_varSlot, IsForGlobal ? topFocusContext(context) : context));
       
   143         }
       
   144         default:
       
   145         {
       
   146             Q_ASSERT_X(false, Q_FUNC_INFO, "This path is not supposed to be run.");
       
   147             return Item::Iterator::Ptr();
       
   148         }
       
   149     }
       
   150 }
       
   151 
       
   152 template<bool IsForGlobal>
       
   153 Expression::Ptr EvaluationCache<IsForGlobal>::typeCheck(const StaticContext::Ptr &context,
       
   154                                                         const SequenceType::Ptr &reqType)
       
   155 {
       
   156     /* It's important that we do the typeCheck() before checking for the use of local variables,
       
   157      * because ExpressionVariableReference can reference an expression that is a local variable,
       
   158      * so it must rewrite itself to it operand before, and it does that in EvaluationCache::typeCheck(). */
       
   159     const Expression::Ptr me(SingleContainer::typeCheck(context, reqType));
       
   160 
       
   161     OperandsIterator it(me, OperandsIterator::ExcludeParent);
       
   162     Expression::Ptr next(it.next());
       
   163 
       
   164     /* If our operand or any sub operand gets its value from a for-loop, we cannot
       
   165      * cache it since then our cache would be filled -- but not invalidated -- on the
       
   166      * first for-iteration. Consider this query:
       
   167      *
       
   168      * <tt>for $i in expr
       
   169      * let $v := $i/p
       
   170      * return ($v, $v)</tt>
       
   171      *
       
   172      * An evaluation cache is inserted for the two operands in the return clause. However,
       
   173      * $i changes for each iteration so the cache can only be active on a per-iteration basis,
       
   174      * it it's possible(which it isn't).
       
   175      *
       
   176      * This means that for some queries we don't cache what we really should, and hence evaluate
       
   177      * in a sub-optimal way, since this DependsOnLocalVariable don't communicate whether it references
       
   178      * a loop that affects us. The correct fix for this would be to let ForExpression reset the
       
   179      * relevant caches only, but we don't know which ones that are. */
       
   180     while(next)
       
   181     {
       
   182         if(next->has(DependsOnLocalVariable))
       
   183             return m_operand->typeCheck(context, reqType);
       
   184 
       
   185         next = it.next();
       
   186     }
       
   187 
       
   188     return me;
       
   189 }
       
   190 
       
   191 template<bool IsForGlobal>
       
   192 Expression::Ptr EvaluationCache<IsForGlobal>::compress(const StaticContext::Ptr &context)
       
   193 {
       
   194     const Expression::Ptr me(SingleContainer::compress(context));
       
   195 
       
   196     if(me != this)
       
   197         return me;
       
   198 
       
   199     if(m_operand->is(IDRangeVariableReference))
       
   200         return m_operand;
       
   201 
       
   202     if(m_declaration->usedByMany())
       
   203     {
       
   204         /* If it's only an atomic value an EvaluationCache is overkill. However,
       
   205          * it's still needed for functions like fn:current-time() that must adhere to
       
   206          * query stability. */
       
   207         const Properties props(m_operand->properties());
       
   208 
       
   209         if(props.testFlag(EvaluationCacheRedundant) ||
       
   210            ((props.testFlag(IsEvaluated)) &&
       
   211             !props.testFlag(DisableElimination) &&
       
   212             CommonSequenceTypes::ExactlyOneAtomicType->matches(m_operand->staticType())))
       
   213         {
       
   214             return m_operand;
       
   215         }
       
   216         else
       
   217             return me;
       
   218     }
       
   219     else
       
   220     {
       
   221         /* If we're only used once, there's no need for an EvaluationCache. */
       
   222         return m_operand;
       
   223     }
       
   224 }
       
   225 
       
   226 template<bool IsForGlobal>
       
   227 SequenceType::Ptr EvaluationCache<IsForGlobal>::staticType() const
       
   228 {
       
   229     return m_operand->staticType();
       
   230 }
       
   231 
       
   232 template<bool IsForGlobal>
       
   233 SequenceType::List EvaluationCache<IsForGlobal>::expectedOperandTypes() const
       
   234 {
       
   235     /* Remember that EvaluationCache::typeCheck() will be called from multiple locations,
       
   236      * which potentially have different type requirements. For instance, one wants a node,
       
   237      * and another requires atomization and casting.
       
   238      *
       
   239      * Returning ZeroOrMoreItems is safe here because staticType() returns the operand's type
       
   240      * and therefore the convertors like Atomizer will be parents to us, and hence only affect
       
   241      * the relevant path.
       
   242      *
       
   243      * ZeroOrMoreItems also make sense logically since we're actually only used where the
       
   244      * variable references reference us. */
       
   245     SequenceType::List result;
       
   246     result.append(CommonSequenceTypes::ZeroOrMoreItems);
       
   247 
       
   248     return result;
       
   249 }
       
   250 
       
   251 template<bool IsForGlobal>
       
   252 Expression::Properties EvaluationCache<IsForGlobal>::properties() const
       
   253 {
       
   254     /* We cannot return the operand's properties unconditionally, because some
       
   255      * doesn't hold for this Expression.
       
   256      *
       
   257      * However, some of the properties must propagate through, which are the ones being OR'd here.
       
   258      */
       
   259     return m_operand->properties() & (DisableElimination | IsEvaluated | DisableTypingDeduction);
       
   260 }
       
   261 
       
   262 template<bool IsForGlobal>
       
   263 ExpressionVisitorResult::Ptr
       
   264 EvaluationCache<IsForGlobal>::accept(const ExpressionVisitor::Ptr &visitor) const
       
   265 {
       
   266     return visitor->visit(this);
       
   267 }
       
   268 
       
   269 template<bool IsForGlobal>
       
   270 const SourceLocationReflection *EvaluationCache<IsForGlobal>::actualReflection() const
       
   271 {
       
   272     return m_operand->actualReflection();
       
   273 }
       
   274