|  | /**************************************************************************** | 
|  | ** | 
|  | ** Copyright (C) 2016 The Qt Company Ltd. | 
|  | ** Contact: https://www.qt.io/licensing/ | 
|  | ** | 
|  | ** This file is part of the QtXmlPatterns module of the Qt Toolkit. | 
|  | ** | 
|  | ** $QT_BEGIN_LICENSE:LGPL$ | 
|  | ** Commercial License Usage | 
|  | ** Licensees holding valid commercial Qt licenses may use this file in | 
|  | ** accordance with the commercial license agreement provided with the | 
|  | ** Software or, alternatively, in accordance with the terms contained in | 
|  | ** a written agreement between you and The Qt Company. For licensing terms | 
|  | ** and conditions see https://www.qt.io/terms-conditions. For further | 
|  | ** information use the contact form at https://www.qt.io/contact-us. | 
|  | ** | 
|  | ** GNU Lesser General Public License Usage | 
|  | ** Alternatively, this file may be used under the terms of the GNU Lesser | 
|  | ** General Public License version 3 as published by the Free Software | 
|  | ** Foundation and appearing in the file LICENSE.LGPL3 included in the | 
|  | ** packaging of this file. Please review the following information to | 
|  | ** ensure the GNU Lesser General Public License version 3 requirements | 
|  | ** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. | 
|  | ** | 
|  | ** GNU General Public License Usage | 
|  | ** Alternatively, this file may be used under the terms of the GNU | 
|  | ** General Public License version 2.0 or (at your option) the GNU General | 
|  | ** Public license version 3 or any later version approved by the KDE Free | 
|  | ** Qt Foundation. The licenses are as published by the Free Software | 
|  | ** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 | 
|  | ** included in the packaging of this file. Please review the following | 
|  | ** information to ensure the GNU General Public License requirements will | 
|  | ** be met: https://www.gnu.org/licenses/gpl-2.0.html and | 
|  | ** https://www.gnu.org/licenses/gpl-3.0.html. | 
|  | ** | 
|  | ** $QT_END_LICENSE$ | 
|  | ** | 
|  | ****************************************************************************/ | 
|  |  | 
|  | #include <QBuffer> | 
|  | #include <QByteArray> | 
|  |  | 
|  | #include "qcalltemplate_p.h" | 
|  | #include "qcommonsequencetypes_p.h" | 
|  | #include "qxmldebug_p.h" | 
|  | #include "qexpression_p.h" | 
|  | #include "qgenericstaticcontext_p.h" | 
|  | #include "qoperandsiterator_p.h" | 
|  | #include "qoptimizationpasses_p.h" | 
|  | #include "qparsercontext_p.h" | 
|  | #include "qpath_p.h" | 
|  | #include "qquerytransformparser_p.h" | 
|  | #include "qstaticfocuscontext_p.h" | 
|  | #include "qtokenrevealer_p.h" | 
|  | #include "qxquerytokenizer_p.h" | 
|  | #include "qxslttokenizer_p.h" | 
|  |  | 
|  | #include "qexpressionfactory_p.h" | 
|  |  | 
|  | QT_BEGIN_NAMESPACE | 
|  |  | 
|  | namespace QPatternist { | 
|  |  | 
|  | /** | 
|  | * @short The entry point to the parser. | 
|  | * | 
|  | * @param info supplies the information the parser & scanner | 
|  | * needs to create expressions. The created expression, if everything | 
|  | * succeeds, can be retrieved via the object @p info points to. | 
|  | * @returns non-negative if the parser fails. | 
|  | * @see ExpressionFactory::createExpression() | 
|  | */ | 
|  | extern int XPathparse(QPatternist::ParserContext *const info); | 
|  |  | 
|  | Expression::Ptr ExpressionFactory::createExpression(const QString &expr, | 
|  | const StaticContext::Ptr &context, | 
|  | const QXmlQuery::QueryLanguage lang, | 
|  | const SequenceType::Ptr &requiredType, | 
|  | const QUrl &queryURI, | 
|  | const QXmlName &initialTemplateName) | 
|  | { | 
|  | if(lang == QXmlQuery::XSLT20) | 
|  | { | 
|  | QByteArray query(expr.toUtf8()); | 
|  | QBuffer buffer(&query); | 
|  | buffer.open(QIODevice::ReadOnly); | 
|  |  | 
|  | return createExpression(&buffer, | 
|  | context, | 
|  | lang, | 
|  | requiredType, | 
|  | queryURI, | 
|  | initialTemplateName); | 
|  | } | 
|  | else | 
|  | { | 
|  | return createExpression(Tokenizer::Ptr(new XQueryTokenizer(expr, queryURI)), | 
|  | context, | 
|  | lang, | 
|  | requiredType, | 
|  | queryURI, | 
|  | initialTemplateName); | 
|  | } | 
|  | } | 
|  |  | 
|  | Expression::Ptr ExpressionFactory::createExpression(QIODevice *const device, | 
|  | const StaticContext::Ptr &context, | 
|  | const QXmlQuery::QueryLanguage lang, | 
|  | const SequenceType::Ptr &requiredType, | 
|  | const QUrl &queryURI, | 
|  | const QXmlName &initialTemplateName) | 
|  | { | 
|  | Q_ASSERT(device); | 
|  | Q_ASSERT(device->isReadable()); | 
|  |  | 
|  | Tokenizer::Ptr tokenizer; | 
|  |  | 
|  | if(lang == QXmlQuery::XSLT20) | 
|  | tokenizer = Tokenizer::Ptr(new XSLTTokenizer(device, queryURI, context, context->namePool())); | 
|  | else | 
|  | tokenizer = Tokenizer::Ptr(new XQueryTokenizer(QString::fromUtf8(device->readAll()), queryURI)); | 
|  |  | 
|  | return createExpression(tokenizer, context, lang, requiredType, queryURI, initialTemplateName); | 
|  | } | 
|  |  | 
|  | Expression::Ptr ExpressionFactory::createExpression(const Tokenizer::Ptr &tokenizer, | 
|  | const StaticContext::Ptr &context, | 
|  | const QXmlQuery::QueryLanguage lang, | 
|  | const SequenceType::Ptr &requiredType, | 
|  | const QUrl &queryURI, | 
|  | const QXmlName &initialTemplateName) | 
|  | { | 
|  | Q_ASSERT(context); | 
|  | Q_ASSERT(requiredType); | 
|  | Q_ASSERT(queryURI.isValid()); | 
|  |  | 
|  | Tokenizer::Ptr effectiveTokenizer(tokenizer); | 
|  | #ifdef Patternist_DEBUG | 
|  | effectiveTokenizer = Tokenizer::Ptr(new TokenRevealer(queryURI, tokenizer)); | 
|  | #endif | 
|  |  | 
|  | OptimizationPasses::Coordinator::init(); | 
|  |  | 
|  | const ParserContext::Ptr info(new ParserContext(context, lang, effectiveTokenizer.data())); | 
|  | info->initialTemplateName = initialTemplateName; | 
|  |  | 
|  | effectiveTokenizer->setParserContext(info); | 
|  |  | 
|  | const int bisonRetval = QPatternist::XPathparse(info.data()); | 
|  |  | 
|  | Q_ASSERT_X(bisonRetval == 0, Q_FUNC_INFO, | 
|  | "We shouldn't be able to get an error, because we throw exceptions."); | 
|  | Q_UNUSED(bisonRetval); /* Needed when not compiled in debug mode, since bisonRetval won't | 
|  | * be used in the Q_ASSERT_X above. */ | 
|  |  | 
|  | Expression::Ptr result(info->queryBody); | 
|  |  | 
|  | if(!result) | 
|  | { | 
|  | context->error(QtXmlPatterns::tr("A library module cannot be evaluated " | 
|  | "directly. It must be imported from a " | 
|  | "main module."), | 
|  | ReportContext::XPST0003, | 
|  | QSourceLocation(queryURI, 1, 1)); | 
|  | } | 
|  |  | 
|  | /* Optimization: I think many things are done in the wrong order below. We | 
|  | * probably want everything typechecked before compressing, since we can | 
|  | * have references all over the place(variable references, template | 
|  | * invocations, function callsites). This could even be a source to bugs. | 
|  | */ | 
|  |  | 
|  | /* Here, we type check user declared functions and global variables. This | 
|  | * means that variables and functions that are not used are type | 
|  | * checked(which they otherwise wouldn't have been), and those which are | 
|  | * used, are type-checked twice, unfortunately. */ | 
|  |  | 
|  | const bool hasExternalFocus = context->contextItemType(); | 
|  |  | 
|  | if(lang == QXmlQuery::XSLT20) | 
|  | { | 
|  | /* Bind xsl:call-template instructions to their template bodies. | 
|  | * | 
|  | * We do this before type checking and compressing them, because a | 
|  | * CallTemplate obviously needs its template before being compressed. | 
|  | * | 
|  | * Also, we do this before type checking and compressing user | 
|  | * functions, since they can contain template call sites. | 
|  | */ | 
|  | for(int i = 0; i < info->templateCalls.count(); ++i) | 
|  | { | 
|  | CallTemplate *const site = info->templateCalls.at(i)->as<CallTemplate>(); | 
|  | const QXmlName targetName(site->name()); | 
|  | const Template::Ptr t(info->namedTemplates.value(targetName)); | 
|  |  | 
|  | if(t) | 
|  | site->setTemplate(t); | 
|  | else | 
|  | { | 
|  | context->error(QtXmlPatterns::tr("No template by name %1 exists.").arg(formatKeyword(context->namePool(), targetName)), | 
|  | ReportContext::XTSE0650, | 
|  | site); | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | /* Type check and compress user functions. */ | 
|  | { | 
|  | const UserFunction::List::const_iterator end(info->userFunctions.constEnd()); | 
|  | UserFunction::List::const_iterator it(info->userFunctions.constBegin()); | 
|  |  | 
|  | /* If the query has a focus(which is common, in the case of a | 
|  | * stylesheet), we must ensure that the focus isn't visible in the | 
|  | * function body. */ | 
|  | StaticContext::Ptr effectiveContext; | 
|  |  | 
|  | if(hasExternalFocus) | 
|  | { | 
|  | effectiveContext = StaticContext::Ptr(new StaticFocusContext(ItemType::Ptr(), | 
|  | context)); | 
|  | } | 
|  | else | 
|  | effectiveContext = context; | 
|  |  | 
|  | for(; it != end; ++it) | 
|  | { | 
|  | pDebug() << "-----      User Function Typecheck      -----"; | 
|  | registerLastPath((*it)->body()); | 
|  |  | 
|  | /* We will most likely call body()->typeCheck() again, once for | 
|  | * each callsite. That is, it will be called from | 
|  | * UserFunctionCallsite::typeCheck(), which will be called | 
|  | * indirectly when we check the query body. */ | 
|  | const Expression::Ptr typeCheck((*it)->body()->typeCheck(effectiveContext, | 
|  | (*it)->signature()->returnType())); | 
|  | /* We don't have to call (*it)->setBody(typeCheck) here since it's | 
|  | * only used directly below. */ | 
|  | processTreePass(typeCheck, UserFunctionTypeCheck); | 
|  | pDebug() << "------------------------------"; | 
|  |  | 
|  | pDebug() << "-----      User Function Compress      -----"; | 
|  | const Expression::Ptr comp(typeCheck->compress(effectiveContext)); | 
|  | (*it)->setBody(comp); | 
|  | processTreePass(comp, UserFunctionCompression); | 
|  | pDebug() << "------------------------------"; | 
|  | } | 
|  | } | 
|  |  | 
|  | /* Type check and compress global variables. */ | 
|  | { | 
|  | const VariableDeclaration::Stack::const_iterator vend(info->variables.constEnd()); | 
|  | VariableDeclaration::Stack::const_iterator vit(info->variables.constBegin()); | 
|  | for(; vit != vend; ++vit) | 
|  | { | 
|  | Q_ASSERT(*vit); | 
|  | /* This is a bit murky, the global variable will have it | 
|  | * Expression::typeCheck() function called from all its references, | 
|  | * but we also want to check it here globally, so we do | 
|  | * typechecking using a proper focus. */ | 
|  | if((*vit)->type == VariableDeclaration::ExternalVariable) | 
|  | continue; | 
|  |  | 
|  | pDebug() << "-----      Global Variable Typecheck      -----"; | 
|  | Q_ASSERT((*vit)->expression()); | 
|  | /* We supply ZeroOrMoreItems, meaning the variable can evaluate to anything. */ | 
|  | // FIXME which is a source to bugs | 
|  | // TODO What about compressing variables? | 
|  | const Expression::Ptr | 
|  | nev((*vit)->expression()->typeCheck(context, CommonSequenceTypes::ZeroOrMoreItems)); | 
|  | processTreePass(nev, GlobalVariableTypeCheck); | 
|  | pDebug() << "------------------------------"; | 
|  | } | 
|  | } | 
|  |  | 
|  | /* Do all tests specific to XSL-T. */ | 
|  | if(lang == QXmlQuery::XSLT20) | 
|  | { | 
|  | /* Type check and compress named templates. */ | 
|  | { | 
|  | pDebug() << "Have " << info->namedTemplates.count() << "named templates"; | 
|  |  | 
|  | for (auto it = info->namedTemplates.cbegin(), end = info->namedTemplates.cend(); it != end; ++it) { | 
|  | processNamedTemplate(it.key(), it.value()->body, TemplateInitial); | 
|  |  | 
|  | it.value()->body = it.value()->body->typeCheck(context, CommonSequenceTypes::ZeroOrMoreItems); | 
|  | processNamedTemplate(it.key(), it.value()->body, TemplateTypeCheck); | 
|  |  | 
|  | it.value()->body = it.value()->body->compress(context); | 
|  | processNamedTemplate(it.key(), it.value()->body, TemplateCompress); | 
|  |  | 
|  | it.value()->compileParameters(context); | 
|  | } | 
|  | } | 
|  |  | 
|  | /* Type check and compress template rules. */ | 
|  | { | 
|  |  | 
|  | /* Since a pattern can exist of AxisStep, its typeCheck() stage | 
|  | * requires a focus. In the case that we're invoked with a name but | 
|  | * no focus, this will yield a compile error, unless we declare a | 
|  | * focus manually. This only needs to be done for the pattern | 
|  | * expression, since the static type of the pattern is used as the | 
|  | * static type for the focus of the template body. */ | 
|  | StaticContext::Ptr patternContext; | 
|  | if(hasExternalFocus) | 
|  | patternContext = context; | 
|  | else | 
|  | patternContext = StaticContext::Ptr(new StaticFocusContext(BuiltinTypes::node, context)); | 
|  |  | 
|  | /* For each template pattern. */ | 
|  | for (auto it = info->templateRules.cbegin(), end = info->templateRules.cend(); it != end; ++it) { | 
|  | const TemplateMode::Ptr &mode = it.value(); | 
|  | const int len = mode->templatePatterns.count(); | 
|  | TemplatePattern::ID currentTemplateID = -1; | 
|  | bool hasDoneItOnce = false; | 
|  |  | 
|  | /* For each template pattern. */ | 
|  | for(int i = 0; i < len; ++i) | 
|  | { | 
|  | /* We can't use references for these two members, since we | 
|  | * assign to them. */ | 
|  | const TemplatePattern::Ptr &pattern = mode->templatePatterns.at(i); | 
|  | Expression::Ptr matchPattern(pattern->matchPattern()); | 
|  |  | 
|  | processTemplateRule(pattern->templateTarget()->body, | 
|  | pattern, mode->name(), TemplateInitial); | 
|  |  | 
|  | matchPattern = matchPattern->typeCheck(patternContext, CommonSequenceTypes::ZeroOrMoreItems); | 
|  | matchPattern = matchPattern->compress(patternContext); | 
|  | pattern->setMatchPattern(matchPattern); | 
|  |  | 
|  | if(currentTemplateID == -1 && hasDoneItOnce) | 
|  | { | 
|  | currentTemplateID = pattern->id(); | 
|  | continue; | 
|  | } | 
|  | else if(currentTemplateID == pattern->id() && hasDoneItOnce) | 
|  | { | 
|  | hasDoneItOnce = false; | 
|  | continue; | 
|  | } | 
|  |  | 
|  | hasDoneItOnce = true; | 
|  | currentTemplateID = pattern->id(); | 
|  | Expression::Ptr body(pattern->templateTarget()->body); | 
|  |  | 
|  | /* Patterns for a new template has started, we must | 
|  | * deal with the body & parameters. */ | 
|  | { | 
|  | /* TODO type is wrong, it has to be the union of all | 
|  | * patterns. */ | 
|  | const StaticContext::Ptr focusContext(new StaticFocusContext(matchPattern->staticType()->itemType(), | 
|  | context)); | 
|  | body = body->typeCheck(focusContext, CommonSequenceTypes::ZeroOrMoreItems); | 
|  |  | 
|  | pattern->templateTarget()->compileParameters(focusContext); | 
|  | } | 
|  |  | 
|  | processTemplateRule(body, pattern, mode->name(), TemplateTypeCheck); | 
|  |  | 
|  | body = body->compress(context); | 
|  |  | 
|  | pattern->templateTarget()->body = body; | 
|  | processTemplateRule(body, pattern, mode->name(), TemplateCompress); | 
|  | } | 
|  |  | 
|  | mode->finalize(); | 
|  | } | 
|  | } | 
|  |  | 
|  | /* Add templates in mode #all to all other modes. | 
|  | * | 
|  | * We do this after the templates has been typechecked and compressed, | 
|  | * since otherwise it will be done N times for the built-in templates, | 
|  | * where N is the count of different templates, instead of once. */ | 
|  | { | 
|  | const QXmlName nameModeAll(QXmlName(StandardNamespaces::InternalXSLT, | 
|  | StandardLocalNames::all)); | 
|  | const TemplateMode::Ptr &modeAll = info->templateRules[nameModeAll]; | 
|  |  | 
|  | Q_ASSERT_X(modeAll, Q_FUNC_INFO, | 
|  | "We should at least have the builtin templates."); | 
|  | for (auto it = info->templateRules.cbegin(), end = info->templateRules.cend(); it != end; ++it) { | 
|  | /* Don't add mode #all to mode #all. */ | 
|  | if(it.key()  == nameModeAll) | 
|  | continue; | 
|  |  | 
|  | it.value()->addMode(modeAll); | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | /* Type check and compress the query body. */ | 
|  | { | 
|  | pDebug() << "----- Initial AST build. -----"; | 
|  | processTreePass(result, QueryBodyInitial); | 
|  | pDebug() << "------------------------------"; | 
|  |  | 
|  | pDebug() << "-----     Type Check     -----"; | 
|  | registerLastPath(result); | 
|  | result->rewrite(result, result->typeCheck(context, requiredType), context); | 
|  | processTreePass(result, QueryBodyTypeCheck); | 
|  | pDebug() << "------------------------------"; | 
|  |  | 
|  | pDebug() << "-----      Compress      -----"; | 
|  | result->rewrite(result, result->compress(context), context); | 
|  | processTreePass(result, QueryBodyCompression); | 
|  | pDebug() << "------------------------------"; | 
|  | } | 
|  |  | 
|  | return result; | 
|  | } | 
|  |  | 
|  | void ExpressionFactory::registerLastPath(const Expression::Ptr &operand) | 
|  | { | 
|  | OperandsIterator it(operand, OperandsIterator::IncludeParent); | 
|  | Expression::Ptr next(it.next()); | 
|  |  | 
|  | while(next) | 
|  | { | 
|  | if(next->is(Expression::IDPath)) | 
|  | { | 
|  | next->as<Path>()->setLast(); | 
|  | next = it.skipOperands(); | 
|  | } | 
|  | else | 
|  | next = it.next(); | 
|  | } | 
|  | } | 
|  |  | 
|  | void ExpressionFactory::processTreePass(const Expression::Ptr &, | 
|  | const CompilationStage) | 
|  | { | 
|  | } | 
|  |  | 
|  | void ExpressionFactory::processTemplateRule(const Expression::Ptr &body, | 
|  | const TemplatePattern::Ptr &pattern, | 
|  | const QXmlName &mode, | 
|  | const TemplateCompilationStage stage) | 
|  | { | 
|  | Q_UNUSED(body); | 
|  | Q_UNUSED(pattern); | 
|  | Q_UNUSED(mode); | 
|  | Q_UNUSED(stage); | 
|  | } | 
|  |  | 
|  | void ExpressionFactory::processNamedTemplate(const QXmlName &name, | 
|  | const Expression::Ptr &tree, | 
|  | const TemplateCompilationStage stage) | 
|  | { | 
|  | Q_UNUSED(name); | 
|  | Q_UNUSED(tree); | 
|  | Q_UNUSED(stage); | 
|  | } | 
|  |  | 
|  | } // namespace QPatternist | 
|  |  | 
|  | QT_END_NAMESPACE | 
|  |  |