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 <QBuffer> |
41 | #include <QByteArray> |
42 | |
43 | #include "qcalltemplate_p.h" |
44 | #include "qcommonsequencetypes_p.h" |
45 | #include "qxmldebug_p.h" |
46 | #include "qexpression_p.h" |
47 | #include "qgenericstaticcontext_p.h" |
48 | #include "qoperandsiterator_p.h" |
49 | #include "qoptimizationpasses_p.h" |
50 | #include "qparsercontext_p.h" |
51 | #include "qpath_p.h" |
52 | #include "qquerytransformparser_p.h" |
53 | #include "qstaticfocuscontext_p.h" |
54 | #include "qtokenrevealer_p.h" |
55 | #include "qxquerytokenizer_p.h" |
56 | #include "qxslttokenizer_p.h" |
57 | |
58 | #include "qexpressionfactory_p.h" |
59 | |
60 | QT_BEGIN_NAMESPACE |
61 | |
62 | namespace QPatternist { |
63 | |
64 | /** |
65 | * @short The entry point to the parser. |
66 | * |
67 | * @param info supplies the information the parser & scanner |
68 | * needs to create expressions. The created expression, if everything |
69 | * succeeds, can be retrieved via the object @p info points to. |
70 | * @returns non-negative if the parser fails. |
71 | * @see ExpressionFactory::createExpression() |
72 | */ |
73 | extern int XPathparse(QPatternist::ParserContext *const info); |
74 | |
75 | Expression::Ptr ExpressionFactory::createExpression(const QString &expr, |
76 | const StaticContext::Ptr &context, |
77 | const QXmlQuery::QueryLanguage lang, |
78 | const SequenceType::Ptr &requiredType, |
79 | const QUrl &queryURI, |
80 | const QXmlName &initialTemplateName) |
81 | { |
82 | if(lang == QXmlQuery::XSLT20) |
83 | { |
84 | QByteArray query(expr.toUtf8()); |
85 | QBuffer buffer(&query); |
86 | buffer.open(openMode: QIODevice::ReadOnly); |
87 | |
88 | return createExpression(device: &buffer, |
89 | context, |
90 | lang, |
91 | requiredType, |
92 | queryURI, |
93 | initialTemplateName); |
94 | } |
95 | else |
96 | { |
97 | return createExpression(tokenizer: Tokenizer::Ptr(new XQueryTokenizer(expr, queryURI)), |
98 | context, |
99 | lang, |
100 | requiredType, |
101 | queryURI, |
102 | initialTemplateName); |
103 | } |
104 | } |
105 | |
106 | Expression::Ptr ExpressionFactory::createExpression(QIODevice *const device, |
107 | const StaticContext::Ptr &context, |
108 | const QXmlQuery::QueryLanguage lang, |
109 | const SequenceType::Ptr &requiredType, |
110 | const QUrl &queryURI, |
111 | const QXmlName &initialTemplateName) |
112 | { |
113 | Q_ASSERT(device); |
114 | Q_ASSERT(device->isReadable()); |
115 | |
116 | Tokenizer::Ptr tokenizer; |
117 | |
118 | if(lang == QXmlQuery::XSLT20) |
119 | tokenizer = Tokenizer::Ptr(new XSLTTokenizer(device, queryURI, context, context->namePool())); |
120 | else |
121 | tokenizer = Tokenizer::Ptr(new XQueryTokenizer(QString::fromUtf8(str: device->readAll()), queryURI)); |
122 | |
123 | return createExpression(tokenizer, context, lang, requiredType, queryURI, initialTemplateName); |
124 | } |
125 | |
126 | Expression::Ptr ExpressionFactory::createExpression(const Tokenizer::Ptr &tokenizer, |
127 | const StaticContext::Ptr &context, |
128 | const QXmlQuery::QueryLanguage lang, |
129 | const SequenceType::Ptr &requiredType, |
130 | const QUrl &queryURI, |
131 | const QXmlName &initialTemplateName) |
132 | { |
133 | Q_ASSERT(context); |
134 | Q_ASSERT(requiredType); |
135 | Q_ASSERT(queryURI.isValid()); |
136 | |
137 | Tokenizer::Ptr effectiveTokenizer(tokenizer); |
138 | #ifdef Patternist_DEBUG |
139 | effectiveTokenizer = Tokenizer::Ptr(new TokenRevealer(queryURI, tokenizer)); |
140 | #endif |
141 | |
142 | OptimizationPasses::Coordinator::init(); |
143 | |
144 | const ParserContext::Ptr info(new ParserContext(context, lang, effectiveTokenizer.data())); |
145 | info->initialTemplateName = initialTemplateName; |
146 | |
147 | effectiveTokenizer->setParserContext(info); |
148 | |
149 | const int bisonRetval = QPatternist::XPathparse(info: info.data()); |
150 | |
151 | Q_ASSERT_X(bisonRetval == 0, Q_FUNC_INFO, |
152 | "We shouldn't be able to get an error, because we throw exceptions." ); |
153 | Q_UNUSED(bisonRetval); /* Needed when not compiled in debug mode, since bisonRetval won't |
154 | * be used in the Q_ASSERT_X above. */ |
155 | |
156 | Expression::Ptr result(info->queryBody); |
157 | |
158 | if(!result) |
159 | { |
160 | context->error(message: QtXmlPatterns::tr(sourceText: "A library module cannot be evaluated " |
161 | "directly. It must be imported from a " |
162 | "main module." ), |
163 | errorCode: ReportContext::XPST0003, |
164 | sourceLocation: QSourceLocation(queryURI, 1, 1)); |
165 | } |
166 | |
167 | /* Optimization: I think many things are done in the wrong order below. We |
168 | * probably want everything typechecked before compressing, since we can |
169 | * have references all over the place(variable references, template |
170 | * invocations, function callsites). This could even be a source to bugs. |
171 | */ |
172 | |
173 | /* Here, we type check user declared functions and global variables. This |
174 | * means that variables and functions that are not used are type |
175 | * checked(which they otherwise wouldn't have been), and those which are |
176 | * used, are type-checked twice, unfortunately. */ |
177 | |
178 | const bool hasExternalFocus = context->contextItemType(); |
179 | |
180 | if(lang == QXmlQuery::XSLT20) |
181 | { |
182 | /* Bind xsl:call-template instructions to their template bodies. |
183 | * |
184 | * We do this before type checking and compressing them, because a |
185 | * CallTemplate obviously needs its template before being compressed. |
186 | * |
187 | * Also, we do this before type checking and compressing user |
188 | * functions, since they can contain template call sites. |
189 | */ |
190 | for(int i = 0; i < info->templateCalls.count(); ++i) |
191 | { |
192 | CallTemplate *const site = info->templateCalls.at(i)->as<CallTemplate>(); |
193 | const QXmlName targetName(site->name()); |
194 | const Template::Ptr t(info->namedTemplates.value(akey: targetName)); |
195 | |
196 | if(t) |
197 | site->setTemplate(t); |
198 | else |
199 | { |
200 | context->error(message: QtXmlPatterns::tr(sourceText: "No template by name %1 exists." ).arg(a: formatKeyword(np: context->namePool(), name: targetName)), |
201 | errorCode: ReportContext::XTSE0650, |
202 | reflection: site); |
203 | } |
204 | } |
205 | } |
206 | |
207 | /* Type check and compress user functions. */ |
208 | { |
209 | const UserFunction::List::const_iterator end(info->userFunctions.constEnd()); |
210 | UserFunction::List::const_iterator it(info->userFunctions.constBegin()); |
211 | |
212 | /* If the query has a focus(which is common, in the case of a |
213 | * stylesheet), we must ensure that the focus isn't visible in the |
214 | * function body. */ |
215 | StaticContext::Ptr effectiveContext; |
216 | |
217 | if(hasExternalFocus) |
218 | { |
219 | effectiveContext = StaticContext::Ptr(new StaticFocusContext(ItemType::Ptr(), |
220 | context)); |
221 | } |
222 | else |
223 | effectiveContext = context; |
224 | |
225 | for(; it != end; ++it) |
226 | { |
227 | pDebug() << "----- User Function Typecheck -----" ; |
228 | registerLastPath(operand: (*it)->body()); |
229 | |
230 | /* We will most likely call body()->typeCheck() again, once for |
231 | * each callsite. That is, it will be called from |
232 | * UserFunctionCallsite::typeCheck(), which will be called |
233 | * indirectly when we check the query body. */ |
234 | const Expression::Ptr typeCheck((*it)->body()->typeCheck(context: effectiveContext, |
235 | reqType: (*it)->signature()->returnType())); |
236 | /* We don't have to call (*it)->setBody(typeCheck) here since it's |
237 | * only used directly below. */ |
238 | processTreePass(tree: typeCheck, stage: UserFunctionTypeCheck); |
239 | pDebug() << "------------------------------" ; |
240 | |
241 | pDebug() << "----- User Function Compress -----" ; |
242 | const Expression::Ptr comp(typeCheck->compress(context: effectiveContext)); |
243 | (*it)->setBody(comp); |
244 | processTreePass(tree: comp, stage: UserFunctionCompression); |
245 | pDebug() << "------------------------------" ; |
246 | } |
247 | } |
248 | |
249 | /* Type check and compress global variables. */ |
250 | { |
251 | const VariableDeclaration::Stack::const_iterator vend(info->variables.constEnd()); |
252 | VariableDeclaration::Stack::const_iterator vit(info->variables.constBegin()); |
253 | for(; vit != vend; ++vit) |
254 | { |
255 | Q_ASSERT(*vit); |
256 | /* This is a bit murky, the global variable will have it |
257 | * Expression::typeCheck() function called from all its references, |
258 | * but we also want to check it here globally, so we do |
259 | * typechecking using a proper focus. */ |
260 | if((*vit)->type == VariableDeclaration::ExternalVariable) |
261 | continue; |
262 | |
263 | pDebug() << "----- Global Variable Typecheck -----" ; |
264 | Q_ASSERT((*vit)->expression()); |
265 | /* We supply ZeroOrMoreItems, meaning the variable can evaluate to anything. */ |
266 | // FIXME which is a source to bugs |
267 | // TODO What about compressing variables? |
268 | const Expression::Ptr |
269 | nev((*vit)->expression()->typeCheck(context, reqType: CommonSequenceTypes::ZeroOrMoreItems)); |
270 | processTreePass(tree: nev, stage: GlobalVariableTypeCheck); |
271 | pDebug() << "------------------------------" ; |
272 | } |
273 | } |
274 | |
275 | /* Do all tests specific to XSL-T. */ |
276 | if(lang == QXmlQuery::XSLT20) |
277 | { |
278 | /* Type check and compress named templates. */ |
279 | { |
280 | pDebug() << "Have " << info->namedTemplates.count() << "named templates" ; |
281 | |
282 | for (auto it = info->namedTemplates.cbegin(), end = info->namedTemplates.cend(); it != end; ++it) { |
283 | processNamedTemplate(name: it.key(), tree: it.value()->body, stage: TemplateInitial); |
284 | |
285 | it.value()->body = it.value()->body->typeCheck(context, reqType: CommonSequenceTypes::ZeroOrMoreItems); |
286 | processNamedTemplate(name: it.key(), tree: it.value()->body, stage: TemplateTypeCheck); |
287 | |
288 | it.value()->body = it.value()->body->compress(context); |
289 | processNamedTemplate(name: it.key(), tree: it.value()->body, stage: TemplateCompress); |
290 | |
291 | it.value()->compileParameters(context); |
292 | } |
293 | } |
294 | |
295 | /* Type check and compress template rules. */ |
296 | { |
297 | |
298 | /* Since a pattern can exist of AxisStep, its typeCheck() stage |
299 | * requires a focus. In the case that we're invoked with a name but |
300 | * no focus, this will yield a compile error, unless we declare a |
301 | * focus manually. This only needs to be done for the pattern |
302 | * expression, since the static type of the pattern is used as the |
303 | * static type for the focus of the template body. */ |
304 | StaticContext::Ptr patternContext; |
305 | if(hasExternalFocus) |
306 | patternContext = context; |
307 | else |
308 | patternContext = StaticContext::Ptr(new StaticFocusContext(BuiltinTypes::node, context)); |
309 | |
310 | /* For each template pattern. */ |
311 | for (auto it = info->templateRules.cbegin(), end = info->templateRules.cend(); it != end; ++it) { |
312 | const TemplateMode::Ptr &mode = it.value(); |
313 | const int len = mode->templatePatterns.count(); |
314 | TemplatePattern::ID currentTemplateID = -1; |
315 | bool hasDoneItOnce = false; |
316 | |
317 | /* For each template pattern. */ |
318 | for(int i = 0; i < len; ++i) |
319 | { |
320 | /* We can't use references for these two members, since we |
321 | * assign to them. */ |
322 | const TemplatePattern::Ptr &pattern = mode->templatePatterns.at(i); |
323 | Expression::Ptr matchPattern(pattern->matchPattern()); |
324 | |
325 | processTemplateRule(body: pattern->templateTarget()->body, |
326 | pattern, mode: mode->name(), stage: TemplateInitial); |
327 | |
328 | matchPattern = matchPattern->typeCheck(context: patternContext, reqType: CommonSequenceTypes::ZeroOrMoreItems); |
329 | matchPattern = matchPattern->compress(context: patternContext); |
330 | pattern->setMatchPattern(matchPattern); |
331 | |
332 | if(currentTemplateID == -1 && hasDoneItOnce) |
333 | { |
334 | currentTemplateID = pattern->id(); |
335 | continue; |
336 | } |
337 | else if(currentTemplateID == pattern->id() && hasDoneItOnce) |
338 | { |
339 | hasDoneItOnce = false; |
340 | continue; |
341 | } |
342 | |
343 | hasDoneItOnce = true; |
344 | currentTemplateID = pattern->id(); |
345 | Expression::Ptr body(pattern->templateTarget()->body); |
346 | |
347 | /* Patterns for a new template has started, we must |
348 | * deal with the body & parameters. */ |
349 | { |
350 | /* TODO type is wrong, it has to be the union of all |
351 | * patterns. */ |
352 | const StaticContext::Ptr focusContext(new StaticFocusContext(matchPattern->staticType()->itemType(), |
353 | context)); |
354 | body = body->typeCheck(context: focusContext, reqType: CommonSequenceTypes::ZeroOrMoreItems); |
355 | |
356 | pattern->templateTarget()->compileParameters(context: focusContext); |
357 | } |
358 | |
359 | processTemplateRule(body, pattern, mode: mode->name(), stage: TemplateTypeCheck); |
360 | |
361 | body = body->compress(context); |
362 | |
363 | pattern->templateTarget()->body = body; |
364 | processTemplateRule(body, pattern, mode: mode->name(), stage: TemplateCompress); |
365 | } |
366 | |
367 | mode->finalize(); |
368 | } |
369 | } |
370 | |
371 | /* Add templates in mode #all to all other modes. |
372 | * |
373 | * We do this after the templates has been typechecked and compressed, |
374 | * since otherwise it will be done N times for the built-in templates, |
375 | * where N is the count of different templates, instead of once. */ |
376 | { |
377 | const QXmlName nameModeAll(QXmlName(StandardNamespaces::InternalXSLT, |
378 | StandardLocalNames::all)); |
379 | const TemplateMode::Ptr &modeAll = info->templateRules[nameModeAll]; |
380 | |
381 | Q_ASSERT_X(modeAll, Q_FUNC_INFO, |
382 | "We should at least have the builtin templates." ); |
383 | for (auto it = info->templateRules.cbegin(), end = info->templateRules.cend(); it != end; ++it) { |
384 | /* Don't add mode #all to mode #all. */ |
385 | if(it.key() == nameModeAll) |
386 | continue; |
387 | |
388 | it.value()->addMode(mode: modeAll); |
389 | } |
390 | } |
391 | } |
392 | |
393 | /* Type check and compress the query body. */ |
394 | { |
395 | pDebug() << "----- Initial AST build. -----" ; |
396 | processTreePass(tree: result, stage: QueryBodyInitial); |
397 | pDebug() << "------------------------------" ; |
398 | |
399 | pDebug() << "----- Type Check -----" ; |
400 | registerLastPath(operand: result); |
401 | result->rewrite(old&: result, New: result->typeCheck(context, reqType: requiredType), context); |
402 | processTreePass(tree: result, stage: QueryBodyTypeCheck); |
403 | pDebug() << "------------------------------" ; |
404 | |
405 | pDebug() << "----- Compress -----" ; |
406 | result->rewrite(old&: result, New: result->compress(context), context); |
407 | processTreePass(tree: result, stage: QueryBodyCompression); |
408 | pDebug() << "------------------------------" ; |
409 | } |
410 | |
411 | return result; |
412 | } |
413 | |
414 | void ExpressionFactory::registerLastPath(const Expression::Ptr &operand) |
415 | { |
416 | OperandsIterator it(operand, OperandsIterator::IncludeParent); |
417 | Expression::Ptr next(it.next()); |
418 | |
419 | while(next) |
420 | { |
421 | if(next->is(i: Expression::IDPath)) |
422 | { |
423 | next->as<Path>()->setLast(); |
424 | next = it.skipOperands(); |
425 | } |
426 | else |
427 | next = it.next(); |
428 | } |
429 | } |
430 | |
431 | void ExpressionFactory::processTreePass(const Expression::Ptr &, |
432 | const CompilationStage) |
433 | { |
434 | } |
435 | |
436 | void ExpressionFactory::processTemplateRule(const Expression::Ptr &body, |
437 | const TemplatePattern::Ptr &pattern, |
438 | const QXmlName &mode, |
439 | const TemplateCompilationStage stage) |
440 | { |
441 | Q_UNUSED(body); |
442 | Q_UNUSED(pattern); |
443 | Q_UNUSED(mode); |
444 | Q_UNUSED(stage); |
445 | } |
446 | |
447 | void ExpressionFactory::processNamedTemplate(const QXmlName &name, |
448 | const Expression::Ptr &tree, |
449 | const TemplateCompilationStage stage) |
450 | { |
451 | Q_UNUSED(name); |
452 | Q_UNUSED(tree); |
453 | Q_UNUSED(stage); |
454 | } |
455 | |
456 | } // namespace QPatternist |
457 | |
458 | QT_END_NAMESPACE |
459 | |
460 | |