1 /****************************************************************************
2 **
3 ** Copyright (C) 2016 The Qt Company Ltd.
4 ** Contact: https://www.qt.io/licensing/
5 **
6 ** This file is part of the QtXmlPatterns module of the Qt Toolkit.
7 **
8 ** $QT_BEGIN_LICENSE:LGPL$
9 ** Commercial License Usage
10 ** Licensees holding valid commercial Qt licenses may use this file in
11 ** accordance with the commercial license agreement provided with the
12 ** Software or, alternatively, in accordance with the terms contained in
13 ** a written agreement between you and The Qt Company. For licensing terms
14 ** and conditions see https://www.qt.io/terms-conditions. For further
15 ** information use the contact form at https://www.qt.io/contact-us.
16 **
17 ** GNU Lesser General Public License Usage
18 ** Alternatively, this file may be used under the terms of the GNU Lesser
19 ** General Public License version 3 as published by the Free Software
20 ** Foundation and appearing in the file LICENSE.LGPL3 included in the
21 ** packaging of this file. Please review the following information to
22 ** ensure the GNU Lesser General Public License version 3 requirements
23 ** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
24 **
25 ** GNU General Public License Usage
26 ** Alternatively, this file may be used under the terms of the GNU
27 ** General Public License version 2.0 or (at your option) the GNU General
28 ** Public license version 3 or any later version approved by the KDE Free
29 ** Qt Foundation. The licenses are as published by the Free Software
30 ** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
31 ** included in the packaging of this file. Please review the following
32 ** information to ensure the GNU General Public License requirements will
33 ** be met: https://www.gnu.org/licenses/gpl-2.0.html and
34 ** https://www.gnu.org/licenses/gpl-3.0.html.
35 **
36 ** $QT_END_LICENSE$
37 **
38 ****************************************************************************/
39 
40 #include "qboolean_p.h"
41 #include "qcommonvalues_p.h"
42 #include "qemptysequence_p.h"
43 #include "qliteral_p.h"
44 #include "qliteralsequence_p.h"
45 #include "qoperandsiterator_p.h"
46 #include "qoptimizerframework_p.h"
47 #include "qstaticfocuscontext_p.h"
48 #include "qtypechecker_p.h"
49 
50 #include "qexpression_p.h"
51 
52 QT_BEGIN_NAMESPACE
53 
54 using namespace QPatternist;
55 
~Expression()56 Expression::~Expression()
57 {
58 }
59 
finalizeStaticContext(const StaticContext::Ptr & context) const60 StaticContext::Ptr Expression::finalizeStaticContext(const StaticContext::Ptr &context) const
61 {
62     Q_ASSERT(context);
63     const ItemType::Ptr focusType(newFocusType());
64     Q_ASSERT(focusType);
65     return StaticContext::Ptr(new StaticFocusContext(focusType, context));
66 }
67 
typeCheck(const StaticContext::Ptr & context,const SequenceType::Ptr & reqType)68 Expression::Ptr Expression::typeCheck(const StaticContext::Ptr &context,
69                                       const SequenceType::Ptr &reqType)
70 {
71     Q_ASSERT(reqType);
72     typeCheckOperands(context);
73     return TypeChecker::applyFunctionConversion(Expression::Ptr(this), reqType, context);
74 }
75 
typeCheckOperands(const StaticContext::Ptr & context)76 void Expression::typeCheckOperands(const StaticContext::Ptr &context)
77 {
78     const Expression::List ops(operands());
79 
80     /* Check if this expression has any operands at all. */
81     if(ops.isEmpty())
82         return; /* We're done, early exit. */
83 
84     const SequenceType::List opTypes(expectedOperandTypes());
85     Expression::List result;
86 
87     /* If we create a focus, we handle the last one specially, so avoid it in the loop. */
88     const bool createsFocus = has(CreatesFocusForLast);
89     const SequenceType::List::const_iterator typeEnd(createsFocus ? --opTypes.constEnd()
90                                                                   : opTypes.constEnd());
91     const Expression::List::const_iterator end(createsFocus ? --ops.constEnd()
92                                                             : ops.constEnd());
93 
94     SequenceType::List::const_iterator reqType(opTypes.constBegin());
95     SequenceType::Ptr t(*reqType);
96     // TODO we assign twice to t here(also below in loop) when ops.size() > 1
97 
98     Expression::List::const_iterator it(ops.constBegin());
99 
100     for(; it != end; ++it)
101     {
102         /* This ensures that the last expectedOperandType stays, and is
103          * used for all other operands. This is used for expressions that
104          * have an infinite amount of operands, such as the concat() function. */
105         if(reqType != typeEnd)
106         {
107             t = *reqType;
108             ++reqType;
109         }
110 
111          /* Let the child & its children typecheck. */
112         result.append((*it)->typeCheck(context, t));
113     }
114 
115     if(createsFocus)
116     {
117         const StaticContext::Ptr newContext(finalizeStaticContext(context));
118         result.append(ops.last()->typeCheck(newContext, opTypes.last()));
119     }
120 
121     setOperands(result);
122 }
123 
invokeOptimizers(const Expression::Ptr & expr,const StaticContext::Ptr & context)124 Expression::Ptr Expression::invokeOptimizers(const Expression::Ptr &expr,
125                                              const StaticContext::Ptr &context)
126 {
127     Q_ASSERT(expr);
128 
129     const OptimizationPass::List opts(expr->optimizationPasses());
130 
131     if(opts.isEmpty()) /* Early exit. */
132     {
133         return expr;
134     }
135 
136     const OptimizationPass::List::const_iterator passEnd(opts.constEnd());
137     OptimizationPass::List::const_iterator passIt(opts.constBegin());
138 
139     for(; passIt != passEnd; ++passIt) /* Invoke each optimization pass. */
140     {
141         const OptimizationPass::Ptr pass(*passIt); /* Alias, for readability. */
142         OptimizationPass::ExpressionMarker sourceMarker(pass->sourceExpression);
143 
144         if(pass->startIdentifier && !pass->startIdentifier->matches(expr))
145         {
146             /* This pass specified a start identifier and it did
147              * not match -- let's try the next OptimizationPass. */
148             continue;
149         }
150 
151         ExpressionIdentifier::List::const_iterator idIt(pass->operandIdentifiers.constBegin());
152         const Expression::List ops(expr->operands());
153         const Expression::List::const_iterator opEnd(ops.constEnd());
154         Expression::List::const_iterator opIt(ops.constBegin());
155 
156         switch(pass->operandsMatchMethod)
157         {
158             case OptimizationPass::Sequential:
159             {
160                 for(; opIt != opEnd; ++opIt)
161                 {
162                     const Expression::Ptr operand(*opIt); /* Alias, for readability. */
163                     const ExpressionIdentifier::Ptr opIdentifier(*idIt); /* Alias, for readability. */
164                     if(opIdentifier && !opIdentifier->matches(operand))
165                     {
166                         break;
167                     }
168 
169                     ++idIt;
170                 }
171 
172                 if(opIt == opEnd)
173                     break; /* All operands matched, so this pass matched. */
174                 else
175                 {
176                     /* The loop above did not finish which means all operands did not match.
177                        Therefore, this OptimizationPass did not match -- let's try the next one. */
178                     continue;
179                 }
180             }
181             case OptimizationPass::AnyOrder:
182             {
183                 Q_ASSERT_X(ops.count() == 2, Q_FUNC_INFO,
184                            "AnyOrder is currently only supported for Expressions with two operands.");
185                 if(pass->operandIdentifiers.first()->matches(ops.first()) &&
186                    pass->operandIdentifiers.last()->matches(ops.last()))
187                 {
188                     break;
189                 }
190                 else if(pass->operandIdentifiers.first()->matches(ops.last()) &&
191                         pass->operandIdentifiers.last()->matches(ops.first()))
192                 {
193                     sourceMarker.first() = 1;
194                     sourceMarker[1] = 0;
195                     break; /* This pass matched. */
196                 }
197                 else
198                     continue; /* This pass didn't match, let's loop through the next pass. */
199             }
200         }
201 
202         /* Figure out the source Expression, if any. */
203         Expression::List operands;
204         Expression::Ptr sourceExpr;
205 
206         if(!sourceMarker.isEmpty())
207         {
208             const OptimizationPass::ExpressionMarker::const_iterator mEnd(sourceMarker.constEnd());
209             OptimizationPass::ExpressionMarker::const_iterator mIt(sourceMarker.constBegin());
210             sourceExpr = expr;
211 
212             for(; mIt != mEnd; ++mIt)
213             {
214                 Q_ASSERT(*mIt >= 0);
215                 sourceExpr = sourceExpr->operands().at(*mIt);
216             }
217 
218             operands.append(sourceExpr);
219         }
220 
221         if(operands.isEmpty())
222         {
223             Q_ASSERT(pass->resultCreator);
224             return pass->resultCreator->create(Expression::List(), context, expr.data())->compress(context);
225         }
226         else if(pass->resultCreator)
227             return pass->resultCreator->create(operands, context, expr.data())->compress(context);
228         else
229         {
230             return sourceExpr;
231         }
232     }
233 
234     return expr;
235 }
236 
compress(const StaticContext::Ptr & context)237 Expression::Ptr Expression::compress(const StaticContext::Ptr &context)
238 {
239     if(!compressOperands(context))
240     {
241         /* At least one of the operands cannot be evaluated at compile, so
242          * 'this' Expression cannot const fold. */
243         return invokeOptimizers(Expression::Ptr(this), context);
244     }
245 
246     Expression::Ptr retval;
247 
248     if(hasDependency(DisableElimination))
249         retval = Expression::Ptr(this);
250     else
251         retval = constantPropagate(context);
252 
253     return invokeOptimizers(retval, context);
254 }
255 
constantPropagate(const StaticContext::Ptr & context) const256 Expression::Ptr Expression::constantPropagate(const StaticContext::Ptr &context) const
257 {
258     Q_ASSERT(context);
259 
260     /* Optimization: We rewrite literals to literals here, which is pointless.
261      * Maybe we should have a property which says "doesn't disable elimination
262      * but don't eliminate me." */
263     if(staticType()->cardinality().allowsMany())
264     {
265         Item::Iterator::Ptr it(evaluateSequence(context->dynamicContext()));
266         Item::List result;
267         Item item(it->next());
268 
269         while(item)
270         {
271             result.append(item);
272             item = it->next();
273         }
274 
275         switch(result.count())
276         {
277             case 0:
278                 return EmptySequence::create(this, context);
279             case 1:
280                 return rewrite(Expression::Ptr(new Literal(result.first())), context);
281             default:
282                 return rewrite(Expression::Ptr(new LiteralSequence(result)), context);
283         }
284     }
285     else
286     {
287         const Item item(evaluateSingleton(context->dynamicContext()));
288 
289         if(item)
290             return rewrite(Expression::Ptr(new Literal(item)), context);
291         else
292             return EmptySequence::create(this, context);
293     }
294 }
295 
evaluateSequence(const DynamicContext::Ptr & context) const296 Item::Iterator::Ptr Expression::evaluateSequence(const DynamicContext::Ptr &context) const
297 {
298     const Item item(evaluateSingleton(context));
299 
300     if(item)
301         return makeSingletonIterator(item);
302     else
303         return CommonValues::emptyIterator;
304 }
305 
evaluateSingleton(const DynamicContext::Ptr & context) const306 Item Expression::evaluateSingleton(const DynamicContext::Ptr &context) const
307 {
308     return Boolean::fromValue(evaluateEBV(context));
309 }
310 
evaluateEBV(const DynamicContext::Ptr & context) const311 bool Expression::evaluateEBV(const DynamicContext::Ptr &context) const
312 {
313     return Boolean::evaluateEBV(evaluateSequence(context), context);
314 }
315 
evaluateToSequenceReceiver(const DynamicContext::Ptr & context) const316 void Expression::evaluateToSequenceReceiver(const DynamicContext::Ptr &context) const
317 {
318     QAbstractXmlReceiver *const receiver = context->outputReceiver();
319     const Item::Iterator::Ptr it(evaluateSequence(context));
320     Item next(it->next());
321 
322     while(next)
323     {
324         receiver->item(next);
325         next = it->next();
326     }
327 }
328 
expectedContextItemType() const329 ItemType::Ptr Expression::expectedContextItemType() const
330 {
331     Q_ASSERT_X(false, Q_FUNC_INFO,
332                "expectedContextItemType() must be overridden when RequiresContextItem is set.");
333     return ItemType::Ptr();
334 }
335 
properties() const336 Expression::Properties Expression::properties() const
337 {
338     return Properties();
339 }
340 
dependencies() const341 Expression::Properties Expression::dependencies() const
342 {
343     OperandsIterator it(Ptr(const_cast<Expression *>(this)), OperandsIterator::ExcludeParent);
344     Expression::Ptr next(it.next());
345 
346     Properties dependencies(properties());
347 
348     while(next)
349     {
350         dependencies |= next->dependencies();
351         next = it.next();
352     }
353 
354     return dependencies & (Expression::RequiresFocus | Expression::IsEvaluated | Expression::DisableElimination);
355 }
356 
announceFocusType(const ItemType::Ptr & itemType)357 void Expression::announceFocusType(const ItemType::Ptr &itemType)
358 {
359     const Expression::List ops(operands());
360     const int len = ops.count();
361 
362     for(int i = 0; i < len; ++i)
363         ops.at(i)->announceFocusType(itemType);
364 }
365 
deepProperties() const366 Expression::Properties Expression::deepProperties() const
367 {
368     Properties props(properties());
369     const Expression::List ops(operands());
370     const int len = ops.count();
371 
372     for(int i = 0; i < len; ++i)
373         props |= ops.at(i)->deepProperties();
374 
375     return props;
376 }
377 
id() const378 Expression::ID Expression::id() const
379 {
380     return IDIgnorableExpression;
381 }
382 
optimizationPasses() const383 OptimizationPass::List Expression::optimizationPasses() const
384 {
385     return OptimizationPass::List();
386 }
387 
newFocusType() const388 ItemType::Ptr Expression::newFocusType() const
389 {
390     Q_ASSERT_X(false, Q_FUNC_INFO,
391                "This function must be overridden when CreatesFocusForLast is set.");
392     return ItemType::Ptr();
393 }
394 
actualReflection() const395 const SourceLocationReflection *Expression::actualReflection() const
396 {
397     return this;
398 }
399 
description() const400 QString Expression::description() const
401 {
402     return QString::fromLatin1("Expression, id: %1").arg(QString::number(id()));
403 }
404 
patternPriority() const405 PatternPriority Expression::patternPriority() const
406 {
407     return 0.5;
408 }
409 
410 QT_END_NAMESPACE
411