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