| File: System\Xml\Xsl\Xslt\InvokeGenerator.cs | Web Access |
| Project: src\runtime\src\libraries\System.Private.Xml\src\System.Private.Xml.csproj (System.Private.Xml) |
// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. using System.Collections.Generic; using System.Diagnostics; using System.Xml.Xsl.Qil; using T = System.Xml.Xsl.XmlQueryTypeFactory; namespace System.Xml.Xsl.Xslt { /** InvokeGenerator is one of the trickiest peaces here. ARGS: QilFunction func -- Functions which should be invoked. Arguments of this function (formalArgs) are Let nodes annotated with names and default values. Problem 1 is that default values can contain references to previous args of this function. Problem 2 is that default values shouldn't contain fix-up nodes. ArrayList actualArgs -- Array of QilNodes annotated 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 sealed class InvokeGenerator : QilCloneVisitor { private readonly bool _debug; private readonly Stack<QilIterator> _iterStack; private QilList? _formalArgs; private QilList? _invokeArgs; private int _curArg; // this.Clone() depends on this value private readonly XsltQilFactory _fac; public InvokeGenerator(XsltQilFactory f, bool debug) : base(f.BaseFactory) { _debug = debug; _fac = f; _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 static 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; } } }