StaticContext::Ptr CurrentItemStore::newStaticContext(const StaticContext::Ptr &context) { /* It might be we are generated despite there is no focus. In that case * an error will reported in case current() is used, but in any case we cannot * crash, so use item() in case we have no focus. * * Such a case is when we're inside a named template, and it's invoked * without focus. */ const ItemType::Ptr t(context->contextItemType()); return StaticContext::Ptr(new StaticCurrentContext(t ? t : BuiltinTypes::item, context)); }
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 ContextItem::typeCheck(const StaticContext::Ptr &context, const SequenceType::Ptr &reqType) { m_itemType = context->contextItemType(); return EmptyContainer::typeCheck(context, reqType); }
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 ContextItem::compress(const StaticContext::Ptr &context) { m_itemType = context->contextItemType(); return EmptyContainer::compress(context); }