private QilNode InvokeApplyFunction(StylesheetLevel sheet, QilName mode, IList<XslNode> actualArgs)
{
// Here we create function that has one argument for each with-param in apply-templates
// We have actualArgs -- list of xsl:with-param(name, value)
// From it we create:
// invokeArgs -- values to use with QilInvoke
// formalArgs -- list of iterators to use with QilFunction
// actualArgs -- modify it to hold iterators (formalArgs) instead of values to ise in invoke generator inside function budy
XslFlags flags;
{
if (!sheet.ModeFlags.TryGetValue(mode, out flags))
{
flags = 0;
}
flags |= XslFlags.Current; // Due to recursive nature of Apply(Templates/Imports) we will need current node any way
}
actualArgs = AddRemoveImplicitArgs(actualArgs, flags);
QilList invokeArgs = _f.ActualParameterList();
QilFunction applyFunction = null;
// Look at the list of all functions that have been already built. If a suitable one is found, reuse it.
List<QilFunction> functionsForMode;
if (!sheet.ApplyFunctions.TryGetValue(mode, out functionsForMode))
{
functionsForMode = sheet.ApplyFunctions[mode] = new List<QilFunction>();
}
foreach (QilFunction func in functionsForMode)
{
if (FillupInvokeArgs(func.Arguments, actualArgs, /*ref*/invokeArgs))
{
applyFunction = func;
break;
}
}
// If a suitable function has not been found, create it
if (applyFunction == null)
{
invokeArgs.Clear();
// We wasn't able to find suitable function. Let's build new:
// 1. Function arguments
QilList formalArgs = _f.FormalParameterList();
for (int i = 0; i < actualArgs.Count; i++)
{
Debug.Assert(actualArgs[i].NodeType == XslNodeType.WithParam, "All Sorts was removed in CompileSorts()");
VarPar withParam = (VarPar)actualArgs[i];
// Add actual arg to 'invokeArgs' array. No need to clone it since it must be
// a literal or a reference.
invokeArgs.Add(withParam.Value);
// Create correspondent formal arg
QilParameter formalArg = _f.Parameter(i == 0 ? T.NodeNotRtf : withParam.Value.XmlType);
formalArg.Name = CloneName(withParam.Name);
formalArgs.Add(formalArg);
// Change actual arg value to formalArg for reuse in calling built-in templates rules
withParam.Value = formalArg;
}
// 2. Function header
applyFunction = _f.Function(formalArgs,
_f.Boolean((flags & XslFlags.SideEffects) != 0),
T.NodeNotRtfS
);
string attMode = (mode.LocalName.Length == 0) ? string.Empty : " mode=\"" + mode.QualifiedName + '"';
applyFunction.DebugName = (sheet is RootLevel ? "<xsl:apply-templates" : "<xsl:apply-imports") + attMode + '>';
functionsForMode.Add(applyFunction);
_functions.Add(applyFunction);
// 3. Function body
Debug.Assert(actualArgs[0].Name == _nameCurrent, "Caller should always pass $current as a first argument to apply-* calls.");
QilIterator current = (QilIterator)formalArgs[0];
// 3.1 Built-in templates:
// 3.1.1 loop over content of current element
QilLoop loopOnContent;
{
QilIterator iChild = _f.For(_f.Content(current));
QilNode filter = _f.Filter(iChild, _f.IsType(iChild, T.Content));
filter.XmlType = T.ContentS; // not attribute
LoopFocus curLoopSaved = _curLoop;
_curLoop.SetFocus(_f.For(filter));
/* Prepare actual arguments */
// At XSLT 1.0, if a built-in template rule is invoked with parameters, the parameters are not
// passed on to any templates invoked by the built-in rule. At XSLT 2.0, these parameters are
// passed through the built-in template rule unchanged.
// we can't just modify current/position/last of actualArgs in XSLT 2.0 as we tried before,
// becuase flags for apply-import amy now be different then flags for apply-templates, so
// we may need to add some space for additional position/last arguments
QilNode body = InvokeApplyFunction(_compiler.Root, mode, /*actualArgs:*/null);
if (IsDebug)
{
body = _f.Sequence(InvokeOnCurrentNodeChanged(), body);
}
loopOnContent = _curLoop.ConstructLoop(body);
_curLoop = curLoopSaved;
}
// 3.1.2 switch on type of current node
QilTernary builtinTemplates = _f.BaseFactory.Conditional(_f.IsType(current, _elementOrDocumentType),
loopOnContent,
_f.Conditional(_f.IsType(current, _textOrAttributeType),
_f.TextCtor(_f.XPathNodeValue(current)),
_f.Sequence()
)
);
// 3.2 Stylesheet templates
_matcherBuilder.CollectPatterns(sheet, mode);
applyFunction.Definition = _matcherBuilder.BuildMatcher(current, actualArgs, /*otherwise:*/builtinTemplates);
}
return _f.Invoke(applyFunction, invokeArgs);
}