149 lines
6.8 KiB
C#
149 lines
6.8 KiB
C#
//------------------------------------------------------------------------------
|
|
// <copyright file="InvokeGenerator.cs" company="Microsoft">
|
|
// Copyright (c) Microsoft Corporation. All rights reserved.
|
|
// </copyright>
|
|
// <owner current="true" primary="true">[....]</owner>
|
|
//------------------------------------------------------------------------------
|
|
|
|
using System.Collections.Generic;
|
|
using System.Diagnostics;
|
|
using System.Xml.Xsl.Qil;
|
|
|
|
namespace System.Xml.Xsl.Xslt {
|
|
using T = XmlQueryTypeFactory;
|
|
|
|
/**
|
|
InvokeGenerator is one of the trikest peaces here.
|
|
ARGS:
|
|
QilFunction func -- Functions which should be invoked. Arguments of this function (formalArgs) are Let nodes
|
|
anotated with names and default valies.
|
|
Problem 1 is that default values can contain references to previouse args of this function.
|
|
Problem 2 is that default values shouldn't contain fixup nodes.
|
|
ArrayList actualArgs -- Array of QilNodes anotated with names. When name of formalArg match name actualArg last one
|
|
is used as invokeArg, otherwise formalArg's default value is cloned and used.
|
|
**/
|
|
|
|
internal class InvokeGenerator : QilCloneVisitor {
|
|
private bool debug;
|
|
private Stack<QilIterator> iterStack;
|
|
|
|
private QilList formalArgs;
|
|
private QilList invokeArgs;
|
|
private int curArg; // this.Clone() depends on this value
|
|
|
|
private XsltQilFactory fac;
|
|
|
|
public InvokeGenerator(XsltQilFactory f, bool debug) : base(f.BaseFactory) {
|
|
this.debug = debug;
|
|
this.fac = f;
|
|
this.iterStack = new Stack<QilIterator>();
|
|
}
|
|
|
|
public QilNode GenerateInvoke(QilFunction func, IList<XslNode> actualArgs) {
|
|
iterStack.Clear();
|
|
formalArgs = func.Arguments;
|
|
invokeArgs = fac.ActualParameterList();
|
|
|
|
// curArg is an instance variable used in Clone() method
|
|
for (curArg = 0; curArg < formalArgs.Count; curArg ++) {
|
|
// Find actual value for a given formal arg
|
|
QilParameter formalArg = (QilParameter)formalArgs[curArg];
|
|
QilNode invokeArg = FindActualArg(formalArg, actualArgs);
|
|
|
|
// If actual value was not specified, use the default value and copy its debug comment
|
|
if (invokeArg == null) {
|
|
if (debug) {
|
|
if (formalArg.Name.NamespaceUri == XmlReservedNs.NsXslDebug) {
|
|
Debug.Assert(formalArg.Name.LocalName == "namespaces", "Cur,Pos,Last don't have default values and should be always added to by caller in AddImplicitArgs()");
|
|
Debug.Assert(formalArg.DefaultValue != null, "PrecompileProtoTemplatesHeaders() set it");
|
|
invokeArg = Clone(formalArg.DefaultValue);
|
|
} else {
|
|
invokeArg = fac.DefaultValueMarker();
|
|
}
|
|
} else {
|
|
Debug.Assert(formalArg.Name.NamespaceUri != XmlReservedNs.NsXslDebug, "Cur,Pos,Last don't have default values and should be always added to by caller in AddImplicitArgs(). We don't have $namespaces in !debug.");
|
|
invokeArg = Clone(formalArg.DefaultValue);
|
|
}
|
|
}
|
|
|
|
XmlQueryType formalType = formalArg.XmlType;
|
|
XmlQueryType invokeType = invokeArg.XmlType;
|
|
|
|
// Possible arg types: anyType, node-set, string, boolean, and number
|
|
fac.CheckXsltType(formalArg);
|
|
fac.CheckXsltType(invokeArg);
|
|
|
|
if (!invokeType.IsSubtypeOf(formalType)) {
|
|
// This may occur only if inferred type of invokeArg is XslFlags.None
|
|
Debug.Assert(invokeType == T.ItemS, "Actual argument type is not a subtype of formal argument type");
|
|
invokeArg = fac.TypeAssert(invokeArg, formalType);
|
|
}
|
|
|
|
invokeArgs.Add(invokeArg);
|
|
}
|
|
|
|
// Create Invoke node and wrap it with previous parameter declarations
|
|
QilNode invoke = fac.Invoke(func, invokeArgs);
|
|
while (iterStack.Count != 0)
|
|
invoke = fac.Loop(iterStack.Pop(), invoke);
|
|
|
|
return invoke;
|
|
}
|
|
|
|
private QilNode FindActualArg(QilParameter formalArg, IList<XslNode> actualArgs) {
|
|
QilName argName = formalArg.Name;
|
|
Debug.Assert(argName != null);
|
|
foreach (XslNode actualArg in actualArgs) {
|
|
if (actualArg.Name.Equals(argName)) {
|
|
return ((VarPar)actualArg).Value;
|
|
}
|
|
}
|
|
return null;
|
|
}
|
|
|
|
// ------------------------------------ QilCloneVisitor -------------------------------------
|
|
|
|
protected override QilNode VisitReference(QilNode n) {
|
|
QilNode replacement = FindClonedReference(n);
|
|
|
|
// If the reference is internal for the subtree being cloned, return it as is
|
|
if (replacement != null) {
|
|
return replacement;
|
|
}
|
|
|
|
// Replacement was not found, thus the reference is external for the subtree being cloned.
|
|
// The case when it refers to one of previous arguments (xsl:param can refer to previous
|
|
// xsl:param's) must be taken care of.
|
|
for (int prevArg = 0; prevArg < curArg; prevArg++) {
|
|
Debug.Assert(formalArgs[prevArg] != null, "formalArg must be in the list");
|
|
Debug.Assert(invokeArgs[prevArg] != null, "This arg should be compiled already");
|
|
|
|
// Is this a reference to prevArg?
|
|
if (n == formalArgs[prevArg]) {
|
|
// If prevArg is a literal, just clone it
|
|
if (invokeArgs[prevArg] is QilLiteral) {
|
|
return invokeArgs[prevArg].ShallowClone(fac.BaseFactory);
|
|
}
|
|
|
|
// If prevArg is not an iterator, cache it in an iterator, and return it
|
|
if (!(invokeArgs[prevArg] is QilIterator)) {
|
|
QilIterator var = fac.BaseFactory.Let(invokeArgs[prevArg]);
|
|
iterStack.Push(var);
|
|
invokeArgs[prevArg] = var;
|
|
}
|
|
Debug.Assert(invokeArgs[prevArg] is QilIterator);
|
|
return invokeArgs[prevArg];
|
|
}
|
|
}
|
|
|
|
// This is a truly external reference, return it as is
|
|
return n;
|
|
}
|
|
|
|
protected override QilNode VisitFunction(QilFunction n) {
|
|
// No need to change function references
|
|
return n;
|
|
}
|
|
}
|
|
}
|