/****************************************************************************+ −
**+ −
** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).+ −
** All rights reserved.+ −
** Contact: Nokia Corporation (qt-info@nokia.com)+ −
**+ −
** This file is part of the QtXmlPatterns module of the Qt Toolkit.+ −
**+ −
** $QT_BEGIN_LICENSE:LGPL$+ −
** No Commercial Usage+ −
** This file contains pre-release code and may not be distributed.+ −
** You may use this file in accordance with the terms and conditions+ −
** contained in the Technology Preview License Agreement accompanying+ −
** this package.+ −
**+ −
** GNU Lesser General Public License Usage+ −
** Alternatively, this file may be used under the terms of the GNU Lesser+ −
** General Public License version 2.1 as published by the Free Software+ −
** Foundation and appearing in the file LICENSE.LGPL included in the+ −
** packaging of this file. Please review the following information to+ −
** ensure the GNU Lesser General Public License version 2.1 requirements+ −
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.+ −
**+ −
** In addition, as a special exception, Nokia gives you certain additional+ −
** rights. These rights are described in the Nokia Qt LGPL Exception+ −
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.+ −
**+ −
** If you have questions regarding the use of this file, please contact+ −
** Nokia at qt-info@nokia.com.+ −
**+ −
**+ −
**+ −
**+ −
**+ −
**+ −
**+ −
**+ −
** $QT_END_LICENSE$+ −
**+ −
****************************************************************************/+ −
+ −
/**+ −
* @file+ −
* @short This file is included by qevaluationcache_p.h.+ −
* If you need includes in this file, put them in qevaluationcache_p.h, outside of the namespace.+ −
*/+ −
+ −
template<bool IsForGlobal>+ −
EvaluationCache<IsForGlobal>::EvaluationCache(const Expression::Ptr &op,+ −
const VariableDeclaration::Ptr &varDecl,+ −
const VariableSlotID aSlot) : SingleContainer(op)+ −
, m_declaration(varDecl)+ −
, m_varSlot(aSlot)+ −
{+ −
Q_ASSERT(m_declaration);+ −
Q_ASSERT(m_varSlot > -1);+ −
}+ −
+ −
template<bool IsForGlobal>+ −
DynamicContext::Ptr EvaluationCache<IsForGlobal>::topFocusContext(const DynamicContext::Ptr &context)+ −
{+ −
DynamicContext::Ptr result(context);+ −
+ −
while(true)+ −
{+ −
DynamicContext::Ptr candidate(result->previousContext());+ −
+ −
/* We want the top focus, not GenericDynamicContext. */+ −
if(candidate && candidate->focusIterator())+ −
result = candidate;+ −
else+ −
return result;+ −
}+ −
}+ −
+ −
template<bool IsForGlobal>+ −
Item EvaluationCache<IsForGlobal>::evaluateSingleton(const DynamicContext::Ptr &context) const+ −
{+ −
ItemCacheCell &cell = IsForGlobal ? context->globalItemCacheCell(m_varSlot) : context->itemCacheCell(m_varSlot);+ −
+ −
if(cell.cacheState == ItemCacheCell::Full)+ −
return cell.cachedItem;+ −
else+ −
{+ −
Q_ASSERT(cell.cacheState == ItemCacheCell::Empty);+ −
cell.cachedItem = m_operand->evaluateSingleton(IsForGlobal ? topFocusContext(context) : context);+ −
cell.cacheState = ItemCacheCell::Full;+ −
return cell.cachedItem;+ −
}+ −
}+ −
+ −
#if defined(Q_OS_IRIX) && defined(Q_CC_MIPS)+ −
/**+ −
* @short Compile workaround for MIPSPro on IRIX.+ −
*+ −
* This function is never called.+ −
*+ −
* It's mere presence means the MIPSPro compiler can accept some other code below.+ −
*+ −
* I recommend Buddism.+ −
*/+ −
static inline Item::Iterator::Ptr workaroundIrixMIPSPro(const ItemSequenceCacheCell &cell)+ −
{+ −
return Item::Iterator::Ptr(new ListIterator<Item, Item::List>(cell.cachedItems));+ −
}+ −
#endif+ −
+ −
template<bool IsForGlobal>+ −
Item::Iterator::Ptr EvaluationCache<IsForGlobal>::evaluateSequence(const DynamicContext::Ptr &context) const+ −
{+ −
ItemSequenceCacheCell::Vector &cells = IsForGlobal ? context->globalItemSequenceCacheCells(m_varSlot) : context->itemSequenceCacheCells(m_varSlot);+ −
ItemSequenceCacheCell &cell = cells[m_varSlot];+ −
+ −
+ −
if(cell.inUse)+ −
{+ −
context->error(QtXmlPatterns::tr("Circularity detected"),+ −
ReportContext::XTDE0640, this);+ −
}+ −
+ −
switch(cell.cacheState)+ −
{+ −
case ItemSequenceCacheCell::Full:+ −
{+ −
/**+ −
* We don't use makeListIterator() here because the MIPSPro compiler can't handle it.+ −
*/+ −
return Item::Iterator::Ptr(new ListIterator<Item, Item::List>(cell.cachedItems));+ −
}+ −
case ItemSequenceCacheCell::Empty:+ −
{+ −
cell.inUse = true;+ −
cell.sourceIterator = m_operand->evaluateSequence(IsForGlobal ? topFocusContext(context) : context);+ −
cell.cacheState = ItemSequenceCacheCell::PartiallyPopulated;+ −
/* Fallthrough. */+ −
}+ −
case ItemSequenceCacheCell::PartiallyPopulated:+ −
{+ −
cell.inUse = false;+ −
Q_ASSERT_X(cells.at(m_varSlot).sourceIterator, Q_FUNC_INFO,+ −
"This trigger for a cache bug which hasn't yet been analyzed.");+ −
return Item::Iterator::Ptr(new CachingIterator(cells, m_varSlot, IsForGlobal ? topFocusContext(context) : context));+ −
}+ −
default:+ −
{+ −
Q_ASSERT_X(false, Q_FUNC_INFO, "This path is not supposed to be run.");+ −
return Item::Iterator::Ptr();+ −
}+ −
}+ −
}+ −
+ −
template<bool IsForGlobal>+ −
Expression::Ptr EvaluationCache<IsForGlobal>::typeCheck(const StaticContext::Ptr &context,+ −
const SequenceType::Ptr &reqType)+ −
{+ −
/* It's important that we do the typeCheck() before checking for the use of local variables,+ −
* because ExpressionVariableReference can reference an expression that is a local variable,+ −
* so it must rewrite itself to it operand before, and it does that in EvaluationCache::typeCheck(). */+ −
const Expression::Ptr me(SingleContainer::typeCheck(context, reqType));+ −
+ −
OperandsIterator it(me, OperandsIterator::ExcludeParent);+ −
Expression::Ptr next(it.next());+ −
+ −
/* If our operand or any sub operand gets its value from a for-loop, we cannot+ −
* cache it since then our cache would be filled -- but not invalidated -- on the+ −
* first for-iteration. Consider this query:+ −
*+ −
* <tt>for $i in expr+ −
* let $v := $i/p+ −
* return ($v, $v)</tt>+ −
*+ −
* An evaluation cache is inserted for the two operands in the return clause. However,+ −
* $i changes for each iteration so the cache can only be active on a per-iteration basis,+ −
* it it's possible(which it isn't).+ −
*+ −
* This means that for some queries we don't cache what we really should, and hence evaluate+ −
* in a sub-optimal way, since this DependsOnLocalVariable don't communicate whether it references+ −
* a loop that affects us. The correct fix for this would be to let ForExpression reset the+ −
* relevant caches only, but we don't know which ones that are. */+ −
while(next)+ −
{+ −
if(next->has(DependsOnLocalVariable))+ −
return m_operand->typeCheck(context, reqType);+ −
+ −
next = it.next();+ −
}+ −
+ −
return me;+ −
}+ −
+ −
template<bool IsForGlobal>+ −
Expression::Ptr EvaluationCache<IsForGlobal>::compress(const StaticContext::Ptr &context)+ −
{+ −
const Expression::Ptr me(SingleContainer::compress(context));+ −
+ −
if(me != this)+ −
return me;+ −
+ −
if(m_operand->is(IDRangeVariableReference))+ −
return m_operand;+ −
+ −
if(m_declaration->usedByMany())+ −
{+ −
/* If it's only an atomic value an EvaluationCache is overkill. However,+ −
* it's still needed for functions like fn:current-time() that must adhere to+ −
* query stability. */+ −
const Properties props(m_operand->properties());+ −
+ −
if(props.testFlag(EvaluationCacheRedundant) ||+ −
((props.testFlag(IsEvaluated)) &&+ −
!props.testFlag(DisableElimination) &&+ −
CommonSequenceTypes::ExactlyOneAtomicType->matches(m_operand->staticType())))+ −
{+ −
return m_operand;+ −
}+ −
else+ −
return me;+ −
}+ −
else+ −
{+ −
/* If we're only used once, there's no need for an EvaluationCache. */+ −
return m_operand;+ −
}+ −
}+ −
+ −
template<bool IsForGlobal>+ −
SequenceType::Ptr EvaluationCache<IsForGlobal>::staticType() const+ −
{+ −
return m_operand->staticType();+ −
}+ −
+ −
template<bool IsForGlobal>+ −
SequenceType::List EvaluationCache<IsForGlobal>::expectedOperandTypes() const+ −
{+ −
/* Remember that EvaluationCache::typeCheck() will be called from multiple locations,+ −
* which potentially have different type requirements. For instance, one wants a node,+ −
* and another requires atomization and casting.+ −
*+ −
* Returning ZeroOrMoreItems is safe here because staticType() returns the operand's type+ −
* and therefore the convertors like Atomizer will be parents to us, and hence only affect+ −
* the relevant path.+ −
*+ −
* ZeroOrMoreItems also make sense logically since we're actually only used where the+ −
* variable references reference us. */+ −
SequenceType::List result;+ −
result.append(CommonSequenceTypes::ZeroOrMoreItems);+ −
+ −
return result;+ −
}+ −
+ −
template<bool IsForGlobal>+ −
Expression::Properties EvaluationCache<IsForGlobal>::properties() const+ −
{+ −
/* We cannot return the operand's properties unconditionally, because some+ −
* doesn't hold for this Expression.+ −
*+ −
* However, some of the properties must propagate through, which are the ones being OR'd here.+ −
*/+ −
return m_operand->properties() & (DisableElimination | IsEvaluated | DisableTypingDeduction);+ −
}+ −
+ −
template<bool IsForGlobal>+ −
ExpressionVisitorResult::Ptr+ −
EvaluationCache<IsForGlobal>::accept(const ExpressionVisitor::Ptr &visitor) const+ −
{+ −
return visitor->visit(this);+ −
}+ −
+ −
template<bool IsForGlobal>+ −
const SourceLocationReflection *EvaluationCache<IsForGlobal>::actualReflection() const+ −
{+ −
return m_operand->actualReflection();+ −
}+ −
+ −