void AbstractFunctionFactory::verifyArity(const FunctionSignature::Ptr &s,
                                          const StaticContext::Ptr &context,
                                          const xsInteger arity,
                                          const SourceLocationReflection *const r) const
{
    /* Same code in both branches, but more specific error messages in order
     * to improve usability. */
    if(s->maximumArguments() != FunctionSignature::UnlimitedArity &&
       arity > s->maximumArguments())
    {
        context->error(QtXmlPatterns::tr("%1 takes at most %n argument(s). "
                                         "%2 is therefore invalid.", 0, s->maximumArguments())
                          .arg(formatFunction(context->namePool(), s))
                          .arg(arity),
                       ReportContext::XPST0017,
                       r);
        return;
    }

    if(arity < s->minimumArguments())
    {
        context->error(QtXmlPatterns::tr("%1 requires at least %n argument(s). "
                                         "%2 is therefore invalid.", 0, s->minimumArguments())
                          .arg(formatFunction(context->namePool(), s))
                          .arg(arity),
                       ReportContext::XPST0017,
                       r);
        return;
    }
}
Example #2
0
Expression::Ptr CastAs::typeCheck(const StaticContext::Ptr &context,
                                  const SequenceType::Ptr &reqType)
{
    checkTargetType(context);
    const SequenceType::Ptr seqt(m_operand->staticType());
    ItemType::Ptr t(seqt->itemType());

    /* Special case xs:QName */
    if(BuiltinTypes::xsQName->xdtTypeMatches(m_targetType->itemType()))
    {
        /* Ok, We're casting to xs:QName. */
        if(m_operand->is(IDStringValue)) /* A valid combination, let's do the cast. */
            return castToQName(context)->typeCheck(context, reqType);
        else if(BuiltinTypes::xsQName->xdtTypeMatches(t))
            return m_operand->typeCheck(context, reqType);
        else if(seqt->cardinality().isEmpty() && m_targetType->cardinality().allowsEmpty())
            return EmptySequence::create(this, context);
        else if(!(seqt->cardinality().isEmpty() && !m_targetType->cardinality().allowsEmpty()))
        {
            context->error(QtXmlPatterns::tr("When casting to %1 or types "
                                             "derived from it, the source "
                                             "value must be of the same type, "
                                             "or it must be a string literal. "
                                             "Type %2 is not allowed.")
                           .arg(formatType(context->namePool(), BuiltinTypes::xsQName))
                           .arg(formatType(context->namePool(), seqt)),
                           ReportContext::XPTY0004, this);
            return Expression::Ptr(this);
        }
    }

    const Expression::Ptr me(SingleContainer::typeCheck(context, reqType));
    /* Type may have changed, such as that atomization has been applied. */
    t = m_operand->staticType()->itemType();

    if(m_targetType->itemType()->xdtTypeMatches(t) &&
       !BuiltinTypes::xsDayTimeDuration->xdtTypeMatches(t) &&
       !BuiltinTypes::xsYearMonthDuration->xdtTypeMatches(t))
    { /* At least casting is superflorous. */
        if(m_operand->staticType()->cardinality().isMatch(m_targetType->cardinality()))
            return m_operand; /* The whole cast expression is redundant. */
        else
        { /* Only cardinality check is needed, rewrite to CardinalityVerifier. */
            return Expression::Ptr(new CardinalityVerifier(m_operand,
                                                           m_targetType->cardinality(),
                                                           ReportContext::FORG0001));
        }
    }

    /* Let the CastingPlatform look up its AtomicCaster. */
    prepareCasting(context, t);

    return me;
}
Example #3
0
Expression::Ptr GenericPredicate::create(const Expression::Ptr &sourceExpression,
                                         const Expression::Ptr &predicateExpression,
                                         const StaticContext::Ptr &context,
                                         const QSourceLocation &location)
{
    Q_ASSERT(sourceExpression);
    Q_ASSERT(predicateExpression);
    Q_ASSERT(context);
    const ItemType::Ptr type(predicateExpression->staticType()->itemType());

    if(predicateExpression->is(IDIntegerValue) &&
       predicateExpression->as<Literal>()->item().as<Numeric>()->toInteger() == 1)
    { /* Handle [1] */
        return createFirstItem(sourceExpression);
    }
    else if(BuiltinTypes::numeric->xdtTypeMatches(type))
    { /* A numeric predicate, other than [1]. */
        /* TODO at somepoint we'll return a specialized expr here, NumericPredicate or so.
         * Dependency analysis is a bit tricky, since the contained expression can depend on
         * some loop component. */
        return Expression::Ptr(new GenericPredicate(sourceExpression, predicateExpression));
    }
    else if(*CommonSequenceTypes::Empty == *type)
    {
        return EmptySequence::create(predicateExpression.data(), context);
    }
    else if(*BuiltinTypes::item == *type ||
            *BuiltinTypes::xsAnyAtomicType == *type)
    {
        /* The type couldn't be narrowed at compile time, so we use
         * a generic predicate. This check is before the CommonSequenceTypes::EBV check,
         * because the latter matches these types as well. */
        return Expression::Ptr(new GenericPredicate(sourceExpression, predicateExpression));
    }
    else if(CommonSequenceTypes::EBV->itemType()->xdtTypeMatches(type))
    {
        return Expression::Ptr(new TruthPredicate(sourceExpression, predicateExpression));
    }
    else
    {
        context->error(QtXmlPatterns::tr("A value of type %1 cannot be a "
                                         "predicate. A predicate must have "
                                         "either a numeric type or an "
                                         "Effective Boolean Value type.")
                       .arg(formatType(context->namePool(),
                                       sourceExpression->staticType())),
                       ReportContext::FORG0006, location);
        return Expression::Ptr(); /* Silence compiler warning. */
    }
}
Example #4
0
Expression::Ptr AvgFN::typeCheck(const StaticContext::Ptr &context,
                                 const SequenceType::Ptr &reqType)
{
    const Expression::Ptr me(FunctionCall::typeCheck(context, reqType));
    ItemType::Ptr t1(m_operands.first()->staticType()->itemType());

    if(*CommonSequenceTypes::Empty == *t1)
        return me;
    else if(*BuiltinTypes::xsAnyAtomicType == *t1 ||
            *BuiltinTypes::numeric == *t1)
        return me;
    else if(BuiltinTypes::xsUntypedAtomic->xdtTypeMatches(t1))
    {
        m_operands.replace(0, Expression::Ptr(new UntypedAtomicConverter(m_operands.first(),
                                                                         BuiltinTypes::xsDouble)));
        t1 = m_operands.first()->staticType()->itemType();
    }
    else if(!BuiltinTypes::numeric->xdtTypeMatches(t1) &&
            !BuiltinTypes::xsDayTimeDuration->xdtTypeMatches(t1) &&
            !BuiltinTypes::xsYearMonthDuration->xdtTypeMatches(t1))
    {
        /* Translator, don't translate the type names. */
        context->error(QtXmlPatterns::tr("The first argument to %1 cannot be "
                                         "of type %2. It must be of type %3, "
                                         "%4, or %5.")
                          .arg(signature())
                          .arg(formatType(context->namePool(), m_operands.first()->staticType()))
                          .arg(formatType(context->namePool(), BuiltinTypes::numeric))
                          .arg(formatType(context->namePool(), BuiltinTypes::xsYearMonthDuration))
                          .arg(formatType(context->namePool(), BuiltinTypes::xsDayTimeDuration)),
                       ReportContext::FORG0006, this);
    }

    if(!m_operands.first()->staticType()->cardinality().allowsMany())
        return m_operands.first();

    /* We use CommonValues::IntegerOne here because it is an arbitrary Expression
     * of type xs:integer */
    Expression::Ptr op2(wrapLiteral(CommonValues::IntegerOne, context, this));
    m_adder = ArithmeticExpression::fetchMathematician(m_operands.first(), m_operands.first(),
                                                       AtomicMathematician::Add, true, context, this);
    m_divider = ArithmeticExpression::fetchMathematician(m_operands.first(), op2,
                                                         AtomicMathematician::Div, true, context, this);
    return me;
}
Example #5
0
QXmlName::NamespaceCode QNameConstructor::namespaceForPrefix(const QXmlName::PrefixCode prefix,
                                                          const StaticContext::Ptr &context,
                                                          const SourceLocationReflection *const r)
{
    Q_ASSERT(context);
    const QXmlName::NamespaceCode ns(context->namespaceBindings()->lookupNamespaceURI(prefix));

    if(ns == NamespaceResolver::NoBinding)
    {
        context->error(QtXmlPatterns::tr("No namespace binding exists for the prefix %1")
                          .arg(formatKeyword(context->namePool()->stringForPrefix(prefix))),
                       ReportContext::XPST0081,
                       r);
        return NamespaceResolver::NoBinding;
    }
    else
        return ns;
}
Example #6
0
Expression::Ptr
ComparingAggregator<oper, result>::typeCheck(const StaticContext::Ptr &context,
                                             const SequenceType::Ptr &reqType)
{
    Q_ASSERT(oper == AtomicComparator::OperatorGreaterThan ||
             oper == AtomicComparator::OperatorLessThan);
    const Expression::Ptr me(FunctionCall::typeCheck(context, reqType));

    ItemType::Ptr t1(m_operands.first()->staticType()->itemType());

    if(*CommonSequenceTypes::Empty == *t1)
        return EmptySequence::create(this, context);
    else if(*BuiltinTypes::xsAnyAtomicType == *t1 ||
            BuiltinTypes::numeric->xdtTypeMatches(t1))
        return me;
    else if(BuiltinTypes::xsUntypedAtomic->xdtTypeMatches(t1))
    {
        m_operands.replace(0, Expression::Ptr(new UntypedAtomicConverter(m_operands.first(),
                           BuiltinTypes::xsDouble)));
        t1 = m_operands.first()->staticType()->itemType();
    }
    else if(!BuiltinTypes::xsString->xdtTypeMatches(t1) &&
            !BuiltinTypes::xsAnyURI->xdtTypeMatches(t1) &&
            !BuiltinTypes::xsDayTimeDuration->xdtTypeMatches(t1) &&
            !BuiltinTypes::xsDate->xdtTypeMatches(t1) &&
            !BuiltinTypes::xsTime->xdtTypeMatches(t1) &&
            !BuiltinTypes::xsDateTime->xdtTypeMatches(t1) &&
            !BuiltinTypes::xsYearMonthDuration->xdtTypeMatches(t1))
    {
        context->error(QtXmlPatterns::tr("The first argument to %1 cannot be of type %2.")
                          .arg(QPatternist::formatFunction(context->namePool(), signature()))
                          .arg(formatType(context->namePool(), m_operands.first()->staticType())),
                       ReportContext::FORG0006, this);
        return me;
    }

    if(!m_operands.first()->staticType()->cardinality().allowsMany())
        return m_operands.first();
    
    // explicit scope needed in RVCT
    ComparingAggregator<oper, result>::prepareComparison(fetchComparator(t1, t1, context));

    return me;
}
Example #7
0
Expression::Ptr DocFN::typeCheck(const StaticContext::Ptr &context,
                                 const SequenceType::Ptr &reqType)
{
    /* See the doxygen documentation for this function for the explanation
     * to why this implementation is here, as opposed to in
     * qsequencegeneratingfns.cpp. */

    Q_ASSERT(context);

    prepareStaticBaseURI(context);

    const Expression::Ptr uriOp(m_operands.first());

    if(!uriOp->isEvaluated())
        return Expression::Ptr(FunctionCall::typeCheck(context, reqType));

    const Item uriItem(uriOp->evaluateSingleton(context->dynamicContext()));

    if(!uriItem)
        return EmptySequence::create(this, context)->typeCheck(context, reqType); // TODO test this

    /* These two lines were previously in a separate function but are now duplicated
     * in DocFN::evaluateSingleton(), as part of a workaround for solaris-cc-64. */
    const QUrl mayRela(AnyURI::toQUrl<ReportContext::FODC0005>(uriItem.stringValue(), context, this));
    const QUrl uri(context->resolveURI(mayRela, staticBaseURI()));

    /* The URI is supplied statically, so, let's try to be clever. */
    Q_ASSERT_X(context->resourceLoader(), Q_FUNC_INFO,
               "No resource loader is set in the StaticContext.");
    m_type = context->resourceLoader()->announceDocument(uri, ResourceLoader::MayUse);

    if(m_type)
    {
        Q_ASSERT(CommonSequenceTypes::ZeroOrOneDocumentNode->matches(m_type));
        return Expression::Ptr(FunctionCall::typeCheck(context, reqType));
    }
    else
    {
        context->error(QtXmlPatterns::tr("It will not be possible to retrieve %1.").arg(formatURI(uri)),
                       ReportContext::FODC0002, this);
        return Expression::Ptr();
    }
}
Example #8
0
Expression::Ptr AddingAggregate::typeCheck(const StaticContext::Ptr &context,
                                           const SequenceType::Ptr &reqType)
{
    const Expression::Ptr me(FunctionCall::typeCheck(context, reqType));
    ItemType::Ptr t1(m_operands.first()->staticType()->itemType());

    if(*CommonSequenceTypes::Empty == *t1)
        return me;
    else if(*BuiltinTypes::xsAnyAtomicType == *t1 ||
            *BuiltinTypes::numeric == *t1)
        return me;
    else if(BuiltinTypes::xsUntypedAtomic->xdtTypeMatches(t1))
    {
        m_operands.replace(0, Expression::Ptr(new UntypedAtomicConverter(m_operands.first(),
                                                                         BuiltinTypes::xsDouble)));
        t1 = m_operands.first()->staticType()->itemType();
    }
    else if(!BuiltinTypes::numeric->xdtTypeMatches(t1) &&
            !BuiltinTypes::xsDayTimeDuration->xdtTypeMatches(t1) &&
            !BuiltinTypes::xsYearMonthDuration->xdtTypeMatches(t1))
    {
        /* Translator, don't translate the type names. */
        context->error(QtXmlPatterns::tr("The first argument to %1 cannot be "
                                         "of type %2. It must be a numeric "
                                         "type, xs:yearMonthDuration or "
                                         "xs:dayTimeDuration.")
                       .arg(formatFunction(context->namePool(), signature()))
                       .arg(formatType(context->namePool(),
                                       m_operands.first()->staticType())),
                       ReportContext::FORG0006, this);
    }

    if(!m_operands.first()->staticType()->cardinality().allowsMany())
        return m_operands.first();

    /* We know fetchMathematician won't attempt a rewrite of the operand, so this is safe. */
    m_mather = ArithmeticExpression::fetchMathematician(m_operands.first(), m_operands.first(),
                                                        AtomicMathematician::Add, true, context,
                                                        this,
                                                        ReportContext::FORG0006);
    return me;
}
Example #9
0
Expression::Ptr SumFN::typeCheck(const StaticContext::Ptr &context,
                                 const SequenceType::Ptr &reqType)
{
    const Expression::Ptr me(AddingAggregate::typeCheck(context, reqType));

    if(*CommonSequenceTypes::Empty == *m_operands.first()->staticType()->itemType())
    {
        if(m_operands.count() == 1)
            return wrapLiteral(CommonValues::IntegerZero, context, this);
        else
            return m_operands.at(1);
    }

    if(m_operands.count() == 1)
        return me;

    const ItemType::Ptr t(m_operands.at(1)->staticType()->itemType());

    if(!BuiltinTypes::numeric->xdtTypeMatches(t) &&
       !BuiltinTypes::xsAnyAtomicType->xdtTypeMatches(t) &&
       *CommonSequenceTypes::Empty != *t &&
       !BuiltinTypes::xsDayTimeDuration->xdtTypeMatches(t) &&
       !BuiltinTypes::xsYearMonthDuration->xdtTypeMatches(t))
    {
        context->error(QtXmlPatterns::tr("The second argument to %1 cannot be "
                                         "of type %2. It must be of type %3, "
                                         "%4, or %5.")
                          .arg(formatFunction(context->namePool(), signature()))
                          .arg(formatType(context->namePool(), m_operands.at(1)->staticType()))
                          .arg(formatType(context->namePool(), BuiltinTypes::numeric))
                          .arg(formatType(context->namePool(), BuiltinTypes::xsYearMonthDuration))
                          .arg(formatType(context->namePool(), BuiltinTypes::xsDayTimeDuration)),
                       ReportContext::FORG0006, this);
        return me;
    }

    return me;
}
Expression::Ptr CardinalityVerifier::verifyCardinality(const Expression::Ptr &operand,
                                                       const Cardinality &requiredCard,
                                                       const StaticContext::Ptr &context,
                                                       const ReportContext::ErrorCode code)
{
    const Cardinality opCard(operand->staticType()->cardinality());

    if(requiredCard.isMatch(opCard))
        return operand;
    else if(requiredCard.canMatch(opCard))
        return Expression::Ptr(new CardinalityVerifier(operand, requiredCard, code));
    else if(context->compatModeEnabled() &&
            !opCard.isEmpty())
    {
        return GenericPredicate::createFirstItem(operand);
    }
    else
    {
        /* Sequences within this cardinality can never match. */
        context->error(wrongCardinality(requiredCard, opCard), code, operand.data());
        return operand;
    }
}
Example #11
0
Expression::Ptr TypeChecker::verifyType(const Expression::Ptr &operand,
                                        const SequenceType::Ptr &reqSeqType,
                                        const StaticContext::Ptr &context,
                                        const ReportContext::ErrorCode code,
                                        const Options options)
{
    const ItemType::Ptr reqType(reqSeqType->itemType());
    const Expression::Properties props(operand->properties());

    /* If operand requires a focus, do the necessary type checking for that. */
    if(props.testFlag(Expression::RequiresFocus) && options.testFlag(CheckFocus))
    {
        const ItemType::Ptr contextType(context->contextItemType());
        if(contextType)
        {
            if(props.testFlag(Expression::RequiresContextItem))
            {
                Q_ASSERT_X(operand->expectedContextItemType(), Q_FUNC_INFO,
                           "When the Expression sets the RequiresContextItem property, it must "
                           "return a type in expectedContextItemType()");
                const ItemType::Ptr expectedContextType(operand->expectedContextItemType());

                /* Allow the empty sequence. We don't want to trigger XPTY0020 on ()/... . */
                if(!expectedContextType->xdtTypeMatches(contextType) && contextType != CommonSequenceTypes::Empty)
                {
                    context->error(wrongType(context->namePool(), operand->expectedContextItemType(), contextType),
                                   ReportContext::XPTY0020, operand.data());
                    return operand;
                }
            }
        }
        else
        {
            context->error(QtXmlPatterns::tr("The focus is undefined."), ReportContext::XPDY0002, operand.data());
            return operand;
        }
    }

    SequenceType::Ptr operandSeqType(operand->staticType());
    ItemType::Ptr operandType(operandSeqType->itemType());

    /* This returns the operand if the types are identical or if operandType
     * is a subtype of reqType. */
    if(reqType->xdtTypeMatches(operandType) || *operandType == *CommonSequenceTypes::Empty)
        return operand;

    /* Since we haven't exited yet, it means that the operandType is a super type
     * of reqType, and that there hence is a path down to it through the
     * type hierachy -- but that doesn't necessarily mean that a up-cast(down the
     * hierarchy) would succeed. */

    Expression::Ptr result(operand);

    if(reqType->isAtomicType())
    {
        const Expression::ID opID = operand->id();
        if((opID == Expression::IDArgumentReference ||
                (opID == Expression::IDCardinalityVerifier && operand->operands().first()->is(Expression::IDArgumentReference)))
                && *BuiltinTypes::item == *operandType)
            return Expression::Ptr(new ArgumentConverter(result, reqType));

        if(!operandType->isAtomicType())
        {
            result = Expression::Ptr(new Atomizer(result));
            /* The atomizer might know more about the type. */
            operandType = result->staticType()->itemType();
        }

        if(reqType->xdtTypeMatches(operandType))
        {
            /* Atomization was sufficient. Either the expected type is xs:anyAtomicType
             * or the type the Atomizer knows it returns, matches the required type. */
            return result;
        }

        const bool compatModeEnabled = context->compatModeEnabled();

        if((options.testFlag(AutomaticallyConvert) && BuiltinTypes::xsUntypedAtomic->xdtTypeMatches(operandType)) ||
                (compatModeEnabled && BuiltinTypes::xsString->xdtTypeMatches(reqType)))
        {
            if(*reqType == *BuiltinTypes::numeric)
            {
                result = typeCheck(new UntypedAtomicConverter(result, BuiltinTypes::xsDouble, code),
                                   context, reqSeqType);
            }
            else
                result = typeCheck(new UntypedAtomicConverter(result, reqType, code), context, reqSeqType);

            /* The UntypedAtomicConverter might know more about the type, so reload. */
            operandType = result->staticType()->itemType();
        }
        else if(compatModeEnabled && *reqType == *BuiltinTypes::xsDouble)
        {
            const FunctionFactory::Ptr functions(context->functionSignatures());
            Expression::List numberArgs;
            numberArgs.append(operand);

            result = functions->createFunctionCall(QXmlName(StandardNamespaces::fn, StandardLocalNames::number),
                                                   numberArgs,
                                                   context,
                                                   operand.data())->typeCheck(context, reqSeqType);
            operandType = result->staticType()->itemType();
            context->wrapExpressionWith(operand.data(), result);
        }

        if(reqType->xdtTypeMatches(operandType))
            return result;

        /* Test if promotion will solve it; the xdtTypeMatches didn't
         * do that. */
        if(options.testFlag(AutomaticallyConvert) && promotionPossible(operandType, reqType, context))
        {
            if(options.testFlag(GeneratePromotion))
                return Expression::Ptr(new UntypedAtomicConverter(result, reqType));
            else
                return result;
        }

        if(operandType->xdtTypeMatches(reqType))
        {
            /* For example, operandType is numeric, and reqType is xs:integer. */
            return Expression::Ptr(new ItemVerifier(result, reqType, code));
        }
        else
        {
            context->error(wrongType(context->namePool(), reqType, operandType), code, operand.data());
            return result;
        }
    }
    else if(reqType->isNodeType())
    {

        ReportContext::ErrorCode myCode;

        if(*reqType == *CommonSequenceTypes::EBV->itemType())
            myCode = ReportContext::FORG0006;
        else
            myCode = code;

        /* empty-sequence() is considered valid because it's ok to do
         * for example nilled( () ). That is, to pass an empty sequence to a
         * function requiring for example node()?. */
        if(*operandType == *CommonSequenceTypes::Empty)
            return result;
        else if(!operandType->xdtTypeMatches(reqType))
        {
            context->error(wrongType(context->namePool(), reqType, operandType), myCode, operand.data());
            return result;
        }

        /* Operand must be an item. Thus, the sequence can contain both
         * nodes and atomic values: we have to verify. */
        return Expression::Ptr(new ItemVerifier(result, reqType, myCode));
    }
    else
    {
        Q_ASSERT(*reqType == *CommonSequenceTypes::Empty);

        /* element() doesn't match empty-sequence(), but element()* does. */
        if(!reqType->xdtTypeMatches(operandType) &&
                !operandSeqType->cardinality().allowsEmpty())
        {
            context->error(wrongType(context->namePool(), reqType, operandType),
                           code, operand.data());
            return result;
        }
    }

    /* This line should be reached if required type is
     * EBVType, and the operand is compatible. */
    return result;
}
Example #12
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;
}
Example #13
0
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;
}