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 | |
56 | Expression::~Expression() |
57 | { |
58 | } |
59 | |
60 | 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 | |
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(operand: Expression::Ptr(this), reqType, context); |
74 | } |
75 | |
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(prop: 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(t: (*it)->typeCheck(context, reqType: t)); |
113 | } |
114 | |
115 | if(createsFocus) |
116 | { |
117 | const StaticContext::Ptr newContext(finalizeStaticContext(context)); |
118 | result.append(t: ops.last()->typeCheck(context: newContext, reqType: opTypes.last())); |
119 | } |
120 | |
121 | setOperands(result); |
122 | } |
123 | |
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(expr: 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(expr: ops.first()) && |
186 | pass->operandIdentifiers.last()->matches(expr: ops.last())) |
187 | { |
188 | break; |
189 | } |
190 | else if(pass->operandIdentifiers.first()->matches(expr: ops.last()) && |
191 | pass->operandIdentifiers.last()->matches(expr: 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(i: *mIt); |
216 | } |
217 | |
218 | operands.append(t: sourceExpr); |
219 | } |
220 | |
221 | if(operands.isEmpty()) |
222 | { |
223 | Q_ASSERT(pass->resultCreator); |
224 | return pass->resultCreator->create(operands: 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 | |
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(expr: Expression::Ptr(this), context); |
244 | } |
245 | |
246 | Expression::Ptr retval; |
247 | |
248 | if(hasDependency(prop: DisableElimination)) |
249 | retval = Expression::Ptr(this); |
250 | else |
251 | retval = constantPropagate(context); |
252 | |
253 | return invokeOptimizers(expr: retval, context); |
254 | } |
255 | |
256 | 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: context->dynamicContext())); |
266 | Item::List result; |
267 | Item item(it->next()); |
268 | |
269 | while(item) |
270 | { |
271 | result.append(t: item); |
272 | item = it->next(); |
273 | } |
274 | |
275 | switch(result.count()) |
276 | { |
277 | case 0: |
278 | return EmptySequence::create(replacementFor: this, context); |
279 | case 1: |
280 | return rewrite(to: Expression::Ptr(new Literal(result.first())), context); |
281 | default: |
282 | return rewrite(to: Expression::Ptr(new LiteralSequence(result)), context); |
283 | } |
284 | } |
285 | else |
286 | { |
287 | const Item item(evaluateSingleton(context: context->dynamicContext())); |
288 | |
289 | if(item) |
290 | return rewrite(to: Expression::Ptr(new Literal(item)), context); |
291 | else |
292 | return EmptySequence::create(replacementFor: this, context); |
293 | } |
294 | } |
295 | |
296 | 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 | |
306 | Item Expression::evaluateSingleton(const DynamicContext::Ptr &context) const |
307 | { |
308 | return Boolean::fromValue(value: evaluateEBV(context)); |
309 | } |
310 | |
311 | bool Expression::evaluateEBV(const DynamicContext::Ptr &context) const |
312 | { |
313 | return Boolean::evaluateEBV(e: evaluateSequence(context), context); |
314 | } |
315 | |
316 | 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(item: next); |
325 | next = it->next(); |
326 | } |
327 | } |
328 | |
329 | 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 | |
336 | Expression::Properties Expression::properties() const |
337 | { |
338 | return Properties(); |
339 | } |
340 | |
341 | 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 | |
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 | |
366 | 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 | |
378 | Expression::ID Expression::id() const |
379 | { |
380 | return IDIgnorableExpression; |
381 | } |
382 | |
383 | OptimizationPass::List Expression::optimizationPasses() const |
384 | { |
385 | return OptimizationPass::List(); |
386 | } |
387 | |
388 | 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 | |
395 | const SourceLocationReflection *Expression::actualReflection() const |
396 | { |
397 | return this; |
398 | } |
399 | |
400 | QString Expression::description() const |
401 | { |
402 | return QString::fromLatin1(str: "Expression, id: %1" ).arg(a: QString::number(id())); |
403 | } |
404 | |
405 | PatternPriority Expression::patternPriority() const |
406 | { |
407 | return 0.5; |
408 | } |
409 | |
410 | QT_END_NAMESPACE |
411 | |