1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148
|
//------------------------------------------------------------------------------
// <copyright file="InvokeGenerator.cs" company="Microsoft">
// Copyright (c) Microsoft Corporation. All rights reserved.
// </copyright>
// <owner current="true" primary="true">Microsoft</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;
}
}
}
|