Example #1
0
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";

            QMutableHashIterator<QXmlName, Template::Ptr> it(info->namedTemplates);

            while(it.hasNext())
            {
                it.next();
                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. */
        {
            QHashIterator<QXmlName, TemplateMode::Ptr> it(info->templateRules);

            /* 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. */
            while(it.hasNext())
            {
                it.next();
                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.");
            QHashIterator<QXmlName, TemplateMode::Ptr> it(info->templateRules);

            while(it.hasNext())
            {
                it.next();

                /* 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;
}
Expression::Ptr ExpressionFactory::createExpression(const QString &expr,
                                                    const StaticContext::Ptr &context,
                                                    const LanguageAccent lang,
                                                    const SequenceType::Ptr &requiredType,
                                                    const QUrl &queryURI)
{
    pDebug() << Q_FUNC_INFO << queryURI;
    Q_ASSERT(context);
    Q_ASSERT(requiredType);
    Q_ASSERT(queryURI.isValid());

    OptimizationPasses::Coordinator::init();

    ParserContext::Ptr info(new ParserContext(context, lang,
                                              Tokenizer::Ptr(new XQueryTokenizer(expr, queryURI))));

    const int bisonRetval = 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));
    }

    /* 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 UserFunction::List::const_iterator end(info->userFunctions.constEnd());
    UserFunction::List::const_iterator it(info->userFunctions.constBegin());
    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(context, (*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(context));
        (*it)->setBody(comp);
        processTreePass(comp, UserFunctionCompression);
        pDebug() << "------------------------------";
    }

    const VariableDeclaration::Stack::const_iterator vend(info->variables.constEnd());
    VariableDeclaration::Stack::const_iterator vit(info->variables.constBegin());
    for(; vit != vend; ++vit)
    {
        Q_ASSERT(*vit);
        /* If it's already used, it will be typeChecked later on. */
        if((*vit)->isUsed() || (*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() << "------------------------------";
    }

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