| 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 | |