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
60QT_BEGIN_NAMESPACE
61
62namespace 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 */
73extern int XPathparse(QPatternist::ParserContext *const info);
74
75Expression::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
106Expression::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
126Expression::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
414void 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
431void ExpressionFactory::processTreePass(const Expression::Ptr &,
432 const CompilationStage)
433{
434}
435
436void 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
447void 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
458QT_END_NAMESPACE
459
460

source code of qtxmlpatterns/src/xmlpatterns/expr/qexpressionfactory.cpp