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; } }
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; }
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. */ } }
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; }
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; }
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; }
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(); } }
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; }
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; } }
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; }
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; }