File: System\Xml\Xsl\Xslt\QilGeneratorEnv.cs
Project: ndp\fx\src\XmlUtils\System.Data.SqlXml.csproj (System.Data.SqlXml)
//------------------------------------------------------------------------------
// <copyright file="QilGeneratorEnv.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.XPath;
using System.Xml.Schema;
using System.Xml.Xsl.Qil;
using System.Xml.Xsl.Runtime;
using System.Xml.Xsl.XPath;
 
namespace System.Xml.Xsl.Xslt {
    using FunctionInfo  = XPathBuilder.FunctionInfo<QilGenerator.FuncId>;
    using Res           = System.Xml.Utils.Res;
    using T             = XmlQueryTypeFactory;
 
    internal partial class QilGenerator : IXPathEnvironment {
 
        // Everywhere in this code in case of error in the stylesheet we should throw XslLoadException.
        // This helper IErrorHelper implementation is used to wrap XmlException's into XslLoadException's.
 
        private struct ThrowErrorHelper : IErrorHelper {
            public void ReportError(string res, params string[] args) {
                Debug.Assert(args == null || args.Length == 0, "Error message must already be composed in res");
                throw new XslLoadException(Res.Xml_UserException, res);
            }
 
            public void ReportWarning(string res, params string[] args) {
                Debug.Fail("Should never get here");
            }
        }
 
        // -------------------------------- IXPathEnvironment --------------------------------
        // IXPathEnvironment represents static context (namespaces, focus) and most naturaly implemented by QilGenerator itself
        //                        $var current() key()
        // */@select or */@test     +     +       +
        // template/@match          -     -       +
        // key/@match               -     -       -
        // key/@use                 -     +       -
        // number/@count            +     -       +
        // number/@from             +     -       +
 
        private bool allowVariables = true;
        private bool allowCurrent   = true;
        private bool allowKey       = true;
 
        private void SetEnvironmentFlags(bool allowVariables, bool allowCurrent, bool allowKey) {
            this.allowVariables = allowVariables;
            this.allowCurrent   = allowCurrent  ;
            this.allowKey       = allowKey      ;
        }
 
        XPathQilFactory IXPathEnvironment.Factory { get { return f; } }
 
        // IXPathEnvironment interface
        QilNode IFocus.GetCurrent()  { return this.GetCurrentNode();     }
        QilNode IFocus.GetPosition() { return this.GetCurrentPosition(); }
        QilNode IFocus.GetLast()     { return this.GetLastPosition();    }
 
        string IXPathEnvironment.ResolvePrefix(string prefix) {
            return ResolvePrefixThrow(true, prefix);
        }
 
        QilNode IXPathEnvironment.ResolveVariable(string prefix, string name) {
            if (!allowVariables) {
                throw new XslLoadException(Res.Xslt_VariablesNotAllowed);
            }
            string ns = ResolvePrefixThrow(/*ignoreDefaultNs:*/true, prefix);
            Debug.Assert(ns != null);
 
            // Look up in params and variables of the current scope and all outer ones
            QilNode var = this.scope.LookupVariable(name, ns);
 
            if (var == null) {
                throw new XslLoadException(Res.Xslt_InvalidVariable, Compiler.ConstructQName(prefix, name));
            }
 
            // All Node* parameters are guaranteed to be in document order with no duplicates, so TypeAssert
            // this so that optimizer can use this information to avoid redundant sorts and duplicate removal.
            XmlQueryType varType = var.XmlType;
            if (var.NodeType == QilNodeType.Parameter && varType.IsNode && varType.IsNotRtf && varType.MaybeMany && !varType.IsDod) {
                var = f.TypeAssert(var, XmlQueryTypeFactory.NodeSDod);
            }
 
            return var;
        }
 
        // NOTE: DO NOT call QilNode.Clone() while executing this method since fixup nodes cannot be cloned
        QilNode IXPathEnvironment.ResolveFunction(string prefix, string name, IList<QilNode> args, IFocus env) {
            Debug.Assert(!args.IsReadOnly, "Writable collection expected");
            if (prefix.Length == 0) {
                FunctionInfo func;
                if (FunctionTable.TryGetValue(name, out func)) {
                    func.CastArguments(args, name, f);
 
                    switch (func.id) {
                    case FuncId.Current             :
                        if (!allowCurrent) {
                            throw new XslLoadException(Res.Xslt_CurrentNotAllowed);
                        }
                        // NOTE: This is the only place where the current node (and not the context node) must be used
                        return ((IXPathEnvironment) this).GetCurrent();
                    case FuncId.Key                 :
                        if (!allowKey) {
                            throw new XslLoadException(Res.Xslt_KeyNotAllowed);
                        }
                        return CompileFnKey(args[0], args[1], env);
                    case FuncId.Document            : return CompileFnDocument(args[0], args.Count > 1 ? args[1] : null);
                    case FuncId.FormatNumber        : return CompileFormatNumber(args[0], args[1], args.Count > 2 ? args[2] : null);
                    case FuncId.UnparsedEntityUri   : return CompileUnparsedEntityUri(args[0]);
                    case FuncId.GenerateId          : return CompileGenerateId(args.Count > 0 ? args[0] : env.GetCurrent());
                    case FuncId.SystemProperty      : return CompileSystemProperty(args[0]);
                    case FuncId.ElementAvailable    : return CompileElementAvailable(args[0]);
                    case FuncId.FunctionAvailable   : return CompileFunctionAvailable(args[0]);
                    default:
                        Debug.Fail(func.id + " is present in the function table, but absent from the switch");
                        return null;
                    }
                } else {
                    throw new XslLoadException(Res.Xslt_UnknownXsltFunction, Compiler.ConstructQName(prefix, name));
                }
            } else {
                string ns = ResolvePrefixThrow(/*ignoreDefaultNs:*/true, prefix);
                Debug.Assert(ns != null);
                if (ns == XmlReservedNs.NsMsxsl) {
                    if (name == "node-set") {
                        FunctionInfo.CheckArity(/*minArg:*/1, /*maxArg:*/1, name, args.Count);
                        return CompileMsNodeSet(args[0]);
                    } else if (name == "string-compare") {
                        FunctionInfo.CheckArity(/*minArg:*/2, /*maxArg:*/4, name, args.Count);
                        return f.InvokeMsStringCompare(
                            /*x:      */f.ConvertToString(args[0]),
                            /*y:      */f.ConvertToString(args[1]),
                            /*lang:   */2 < args.Count ? f.ConvertToString(args[2]) : f.String(string.Empty),
                            /*options:*/3 < args.Count ? f.ConvertToString(args[3]) : f.String(string.Empty)
                        );
                    } else if (name == "utc") {
                        FunctionInfo.CheckArity(/*minArg:*/1, /*maxArg:*/1, name, args.Count);
                        return f.InvokeMsUtc(/*datetime:*/f.ConvertToString(args[0]));
                    } else if (name == "format-date" || name == "format-time") {
                        FunctionInfo.CheckArity(/*minArg:*/1, /*maxArg:*/3, name, args.Count);
                        bool fwdCompat = (xslVersion == XslVersion.ForwardsCompatible);
                        return f.InvokeMsFormatDateTime(
                            /*datetime:*/f.ConvertToString(args[0]),
                            /*format:  */1 < args.Count ? f.ConvertToString(args[1]) : f.String(string.Empty),
                            /*lang:    */2 < args.Count ? f.ConvertToString(args[2]) : f.String(string.Empty),
                            /*isDate:  */f.Boolean(name == "format-date")
                        );
                    } else if (name == "local-name") {
                        FunctionInfo.CheckArity(/*minArg:*/1, /*maxArg:*/1, name, args.Count);
                        return f.InvokeMsLocalName(f.ConvertToString(args[0]));
                    } else if (name == "namespace-uri") {
                        FunctionInfo.CheckArity(/*minArg:*/1, /*maxArg:*/1, name, args.Count);
                        return f.InvokeMsNamespaceUri(f.ConvertToString(args[0]), env.GetCurrent());
                    } else if (name == "number") {
                        FunctionInfo.CheckArity(/*minArg:*/1, /*maxArg:*/1, name, args.Count);
                        return f.InvokeMsNumber(args[0]);
                    }
                }
 
                if (ns == XmlReservedNs.NsExsltCommon) {
                    if (name == "node-set") {
                        FunctionInfo.CheckArity(/*minArg:*/1, /*maxArg:*/1, name, args.Count);
                        return CompileMsNodeSet(args[0]);
                    } else if (name == "object-type") {
                        FunctionInfo.CheckArity(/*minArg:*/1, /*maxArg:*/1, name, args.Count);
                        return EXslObjectType(args[0]);
                    }
                }
 
                // NOTE: If you add any function here, add it to IsFunctionAvailable as well
 
                // Ensure that all node-set parameters are DocOrderDistinct
                for (int i = 0; i < args.Count; i++)
                    args[i] = f.SafeDocOrderDistinct(args[i]);
 
                if (compiler.Settings.EnableScript) {
                    XmlExtensionFunction scrFunc = compiler.Scripts.ResolveFunction(name, ns, args.Count, (IErrorHelper)this);
                    if (scrFunc != null) {
                        return GenerateScriptCall(f.QName(name, ns, prefix), scrFunc, args);
                    }
                } else {
                    if (compiler.Scripts.ScriptClasses.ContainsKey(ns)) {
                        ReportWarning(Res.Xslt_ScriptsProhibited);
                        return f.Error(lastScope.SourceLine, Res.Xslt_ScriptsProhibited);
                    }
                }
 
                return f.XsltInvokeLateBound(f.QName(name, ns, prefix), args);
            }
        }
 
        private QilNode GenerateScriptCall(QilName name, XmlExtensionFunction scrFunc, IList<QilNode> args) {
            XmlQueryType xmlTypeFormalArg;
 
            for (int i = 0; i < args.Count; i++) {
                xmlTypeFormalArg = scrFunc.GetXmlArgumentType(i);
                switch (xmlTypeFormalArg.TypeCode) {
                    case XmlTypeCode.Boolean:       args[i] = f.ConvertToBoolean(args[i]); break;
                    case XmlTypeCode.Double:        args[i] = f.ConvertToNumber(args[i]); break;
                    case XmlTypeCode.String:        args[i] = f.ConvertToString(args[i]); break;
                    case XmlTypeCode.Node:          args[i] = xmlTypeFormalArg.IsSingleton ? f.ConvertToNode(args[i]) : f.ConvertToNodeSet(args[i]); break;
                    case XmlTypeCode.Item:          break;
                    default: Debug.Fail("This XmlTypeCode should never be inferred from a Clr type: " + xmlTypeFormalArg.TypeCode); break;
                }
            }
 
            return f.XsltInvokeEarlyBound(name, scrFunc.Method, scrFunc.XmlReturnType, args);
        }
 
        private string ResolvePrefixThrow(bool ignoreDefaultNs, string prefix) {
            if (ignoreDefaultNs && prefix.Length == 0) {
                return string.Empty;
            } else {
                string ns = scope.LookupNamespace(prefix);
                if (ns == null) {
                    if (prefix.Length != 0) {
                        throw new XslLoadException(Res.Xslt_InvalidPrefix, prefix);
                    }
                    ns = string.Empty;
                }
                return ns;
            }
        }
 
        //------------------------------------------------
        // XSLT Functions
        //------------------------------------------------
 
        public enum FuncId {
            Current,
            Document,
            Key,
            FormatNumber,
            UnparsedEntityUri,
            GenerateId,
            SystemProperty,
            ElementAvailable,
            FunctionAvailable,
        }
 
        private static readonly XmlTypeCode[] argFnDocument     = {XmlTypeCode.Item,   XmlTypeCode.Node};
        private static readonly XmlTypeCode[] argFnKey          = {XmlTypeCode.String, XmlTypeCode.Item};
        private static readonly XmlTypeCode[] argFnFormatNumber = {XmlTypeCode.Double, XmlTypeCode.String, XmlTypeCode.String};
 
        public  static Dictionary<string, FunctionInfo> FunctionTable = CreateFunctionTable();
        private static Dictionary<string, FunctionInfo> CreateFunctionTable() {
            Dictionary<string, FunctionInfo> table = new Dictionary<string, FunctionInfo>(16);
            table.Add("current"            , new FunctionInfo(FuncId.Current          , 0, 0, null));
            table.Add("document"           , new FunctionInfo(FuncId.Document         , 1, 2, argFnDocument));
            table.Add("key"                , new FunctionInfo(FuncId.Key              , 2, 2, argFnKey));
            table.Add("format-number"      , new FunctionInfo(FuncId.FormatNumber     , 2, 3, argFnFormatNumber));
            table.Add("unparsed-entity-uri", new FunctionInfo(FuncId.UnparsedEntityUri, 1, 1, XPathBuilder.argString));
            table.Add("generate-id"        , new FunctionInfo(FuncId.GenerateId       , 0, 1, XPathBuilder.argNodeSet));
            table.Add("system-property"    , new FunctionInfo(FuncId.SystemProperty   , 1, 1, XPathBuilder.argString));
            table.Add("element-available"  , new FunctionInfo(FuncId.ElementAvailable , 1, 1, XPathBuilder.argString));
            table.Add("function-available" , new FunctionInfo(FuncId.FunctionAvailable, 1, 1, XPathBuilder.argString));
            return table;
        }
 
        public static bool IsFunctionAvailable(string localName, string nsUri) {
            if (XPathBuilder.IsFunctionAvailable(localName, nsUri)) {
                return true;
            }
            if (nsUri.Length == 0) {
                return FunctionTable.ContainsKey(localName) && localName != "unparsed-entity-uri";
            }
            if (nsUri == XmlReservedNs.NsMsxsl) {
                return (
                    localName == "node-set"       ||
                    localName == "format-date"    ||
                    localName == "format-time"    ||
                    localName == "local-name"     ||
                    localName == "namespace-uri"  ||
                    localName == "number"         ||
                    localName == "string-compare" ||
                    localName == "utc"
                );
            }
            if (nsUri == XmlReservedNs.NsExsltCommon) {
                return localName == "node-set" || localName == "object-type";
            }
            return false;
        }
 
        public static bool IsElementAvailable(XmlQualifiedName name) {
            if (name.Namespace == XmlReservedNs.NsXslt) {
                string localName = name.Name;
                return (
                    localName == "apply-imports"            ||
                    localName == "apply-templates"          ||
                    localName == "attribute"                ||
                    localName == "call-template"            ||
                    localName == "choose"                   ||
                    localName == "comment"                  ||
                    localName == "copy"                     ||
                    localName == "copy-of"                  ||
                    localName == "element"                  ||
                    localName == "fallback"                 ||
                    localName == "for-each"                 ||
                    localName == "if"                       ||
                    localName == "message"                  ||
                    localName == "number"                   ||
                    localName == "processing-instruction"   ||
                    localName == "text"                     ||
                    localName == "value-of"                 ||
                    localName == "variable"
                );
            }
            // NOTE: msxsl:script is not an "instruction", so we return false for it
            return false;
        }
 
        private QilNode CompileFnKey(QilNode name, QilNode keys, IFocus env) {
            QilNode result;
            QilIterator i, n, k;
            if (keys.XmlType.IsNode) {
                if (keys.XmlType.IsSingleton) {
                    result = CompileSingleKey(name, f.ConvertToString(keys), env);
                } else {
                    result = f.Loop(i = f.For(keys), CompileSingleKey(name, f.ConvertToString(i), env));
                }
            }
            else if (keys.XmlType.IsAtomicValue) {
                result = CompileSingleKey(name, f.ConvertToString(keys), env);
            }
            else {
                result = f.Loop(n = f.Let(name), f.Loop(k = f.Let(keys),
                    f.Conditional(f.Not(f.IsType(k, T.AnyAtomicType)),
                        f.Loop(i = f.For(f.TypeAssert(k, T.NodeS)), CompileSingleKey(n, f.ConvertToString(i), env)),
                        CompileSingleKey(n, f.XsltConvert(k, T.StringX), env)
                    )
                ));
            }
            return f.DocOrderDistinct(result);
        }
 
        private QilNode CompileSingleKey(QilNode name, QilNode key, IFocus env) {
            Debug.Assert(name.XmlType == T.StringX && key.XmlType == T.StringX);
            QilNode     result;
 
            if (name.NodeType == QilNodeType.LiteralString) {
                string keyName = (string)(QilLiteral)name;
                string prefix, local, nsUri;
 
                compiler.ParseQName(keyName, out prefix, out local, new ThrowErrorHelper());
                nsUri = ResolvePrefixThrow(/*ignoreDefaultNs:*/true, prefix);
                QilName qname = f.QName(local, nsUri, prefix);
 
                if (!compiler.Keys.Contains(qname)) {
                    throw new XslLoadException(Res.Xslt_UndefinedKey, keyName);
                }
                result = CompileSingleKey(compiler.Keys[qname], key, env);
            } else {
                if (generalKey == null) {
                    generalKey = CreateGeneralKeyFunction();
                }
                QilIterator i = f.Let(name);
                QilNode resolvedName = ResolveQNameDynamic(/*ignoreDefaultNs:*/true, i);
                result = f.Invoke(generalKey, f.ActualParameterList(i, resolvedName, key, env.GetCurrent()));
                result = f.Loop(i, result);
            }
            return result;
        }
 
        private QilNode CompileSingleKey(List<Key> defList, QilNode key, IFocus env) {
            Debug.Assert(defList != null && defList.Count > 0);
            if (defList.Count == 1) {
                return f.Invoke(defList[0].Function, f.ActualParameterList(env.GetCurrent(), key));
            }
 
            QilIterator i  = f.Let(key);
            QilNode result = f.Sequence();
            foreach (Key keyDef in defList) {
                result.Add(f.Invoke(keyDef.Function, f.ActualParameterList(env.GetCurrent(), i)));
            }
            return f.Loop(i, result);
        }
 
        private QilNode CompileSingleKey(List<Key> defList, QilIterator key, QilIterator context) {
            Debug.Assert(defList != null && defList.Count > 0);
            QilList result = f.BaseFactory.Sequence();
            QilNode keyRef = null;
 
            foreach (Key keyDef in defList) {
                keyRef = f.Invoke(keyDef.Function, f.ActualParameterList(context, key));
                result.Add(keyRef);
            }
            return defList.Count == 1 ? keyRef : result;
        }
 
        private QilFunction CreateGeneralKeyFunction() {
            QilIterator name          = f.Parameter(T.StringX);
            QilIterator resolvedName  = f.Parameter(T.QNameX);
            QilIterator key           = f.Parameter(T.StringX);
            QilIterator context       = f.Parameter(T.NodeNotRtf);
 
            QilNode fdef = f.Error(Res.Xslt_UndefinedKey, name);
            for (int idx = 0; idx < compiler.Keys.Count; idx++) {
                fdef = f.Conditional(f.Eq(resolvedName, compiler.Keys[idx][0].Name.DeepClone(f.BaseFactory)),
                    CompileSingleKey(compiler.Keys[idx], key, context),
                    fdef
                );
            }
 
            QilFunction result = f.Function(f.FormalParameterList(name, resolvedName, key, context), fdef, f.False());
            result.DebugName = "key";
            this.functions.Add(result);
            return result;
        }
 
        private QilNode CompileFnDocument(QilNode uris, QilNode baseNode) {
            QilNode result;
            QilIterator i, j, u;
 
            if (! compiler.Settings.EnableDocumentFunction) {
                ReportWarning(Res.Xslt_DocumentFuncProhibited);
                return f.Error(lastScope.SourceLine, Res.Xslt_DocumentFuncProhibited);
            }
            if (uris.XmlType.IsNode) {
                result = f.DocOrderDistinct(f.Loop(i = f.For(uris),
                    CompileSingleDocument(f.ConvertToString(i), baseNode ?? i)
                ));
            } else if (uris.XmlType.IsAtomicValue) {
                result = CompileSingleDocument(f.ConvertToString(uris), baseNode);
            } else {
                u = f.Let(uris);
                j = (baseNode != null) ? f.Let(baseNode) : null;
                result = f.Conditional(f.Not(f.IsType(u, T.AnyAtomicType)),
                    f.DocOrderDistinct(f.Loop(i = f.For(f.TypeAssert(u, T.NodeS)),
                        CompileSingleDocument(f.ConvertToString(i), j ?? i)
                    )),
                    CompileSingleDocument(f.XsltConvert(u, T.StringX), j)
                );
                result = (baseNode != null) ? f.Loop(j, result) : result;
                result = f.Loop(u, result);
            }
            return result;
        }
 
        private QilNode CompileSingleDocument(QilNode uri, QilNode baseNode) {
            f.CheckString(uri);
            QilNode baseUri;
 
            if (baseNode == null) {
                baseUri = f.String(lastScope.SourceLine.Uri);
            } else {
                f.CheckNodeSet(baseNode);
                if (baseNode.XmlType.IsSingleton) {
                    baseUri = f.InvokeBaseUri(baseNode);
                } else {
                    // According to errata E14, it is an error if the second argument node-set is empty
                    // and the URI reference is relative. We pass an empty string as a baseUri to indicate
                    // that case.
                    QilIterator i;
                    baseUri = f.StrConcat(f.Loop(i = f.FirstNode(baseNode), f.InvokeBaseUri(i)));
                }
            }
 
            f.CheckString(baseUri);
            return f.DataSource(uri, baseUri);
        }
 
        private QilNode CompileFormatNumber(QilNode value, QilNode formatPicture, QilNode formatName) {
            f.CheckDouble(value);
            f.CheckString(formatPicture);
            XmlQualifiedName resolvedName;
 
            if (formatName == null) {
                resolvedName = new XmlQualifiedName();
                // formatName must be non-null in the f.InvokeFormatNumberDynamic() call below
                formatName = f.String(string.Empty);
            } else {
                f.CheckString(formatName);
                if (formatName.NodeType == QilNodeType.LiteralString) {
                    resolvedName = ResolveQNameThrow(/*ignoreDefaultNs:*/true, formatName);
                } else {
                    resolvedName = null;
                }
            }
 
            if (resolvedName != null) {
                DecimalFormatDecl format;
                if (compiler.DecimalFormats.Contains(resolvedName)) {
                    format = compiler.DecimalFormats[resolvedName];
                } else {
                    if (resolvedName != DecimalFormatDecl.Default.Name) {
                        throw new XslLoadException(Res.Xslt_NoDecimalFormat, (string)(QilLiteral)formatName);
                    }
                    format = DecimalFormatDecl.Default;
                }
 
                // If both formatPicture and formatName are literal strings, there is no need to reparse
                // formatPicture on every execution of this format-number(). Instead, we create a DecimalFormatter
                // object on the first execution, save its index into a global variable, and reuse that object
                // on all subsequent executions.
                if (formatPicture.NodeType == QilNodeType.LiteralString) {
                    QilIterator fmtIdx = f.Let(f.InvokeRegisterDecimalFormatter(formatPicture, format));
                    fmtIdx.DebugName = f.QName("formatter" + this.formatterCnt++, XmlReservedNs.NsXslDebug).ToString();
                    this.gloVars.Add(fmtIdx);
                    return f.InvokeFormatNumberStatic(value, fmtIdx);
                }
 
                formatNumberDynamicUsed = true;
                QilNode name = f.QName(resolvedName.Name, resolvedName.Namespace);
                return f.InvokeFormatNumberDynamic(value, formatPicture, name, formatName);
            } else {
                formatNumberDynamicUsed = true;
                QilIterator i = f.Let(formatName);
                QilNode name = ResolveQNameDynamic(/*ignoreDefaultNs:*/true, i);
                return f.Loop(i, f.InvokeFormatNumberDynamic(value, formatPicture, name, i));
            }
        }
 
        private QilNode CompileUnparsedEntityUri(QilNode n) {
            f.CheckString(n);
            return f.Error(lastScope.SourceLine, Res.Xslt_UnsupportedXsltFunction, "unparsed-entity-uri");
        }
 
        private QilNode CompileGenerateId(QilNode n) {
            f.CheckNodeSet(n);
            if (n.XmlType.IsSingleton) {
                return f.XsltGenerateId(n);
            } else {
                QilIterator i;
                return f.StrConcat(f.Loop(i = f.FirstNode(n), f.XsltGenerateId(i)));
            }
        }
 
        private XmlQualifiedName ResolveQNameThrow(bool ignoreDefaultNs, QilNode qilName) {
            string name = (string)(QilLiteral)qilName;
            string prefix, local, nsUri;
 
            compiler.ParseQName(name, out prefix, out local, new ThrowErrorHelper());
            nsUri = ResolvePrefixThrow(/*ignoreDefaultNs:*/ignoreDefaultNs, prefix);
 
            return new XmlQualifiedName(local, nsUri);
        }
 
        private QilNode CompileSystemProperty(QilNode name) {
            f.CheckString(name);
            if (name.NodeType == QilNodeType.LiteralString) {
                XmlQualifiedName qname = ResolveQNameThrow(/*ignoreDefaultNs:*/true, name);
                if (EvaluateFuncCalls) {
                    XPathItem propValue = XsltFunctions.SystemProperty(qname);
                    if (propValue.ValueType == XsltConvert.StringType) {
                        return f.String(propValue.Value);
                    } else {
                        Debug.Assert(propValue.ValueType == XsltConvert.DoubleType);
                        return f.Double((double)propValue.ValueAsDouble);
                    }
                }
                name = f.QName(qname.Name, qname.Namespace);
            } else {
                name = ResolveQNameDynamic(/*ignoreDefaultNs:*/true, name);
            }
            return f.InvokeSystemProperty(name);
        }
 
        private QilNode CompileElementAvailable(QilNode name) {
            f.CheckString(name);
            if (name.NodeType == QilNodeType.LiteralString) {
                XmlQualifiedName qname = ResolveQNameThrow(/*ignoreDefaultNs:*/false, name);
                if (EvaluateFuncCalls) {
                    return f.Boolean(IsElementAvailable(qname));
                }
                name = f.QName(qname.Name, qname.Namespace);
            } else {
                name = ResolveQNameDynamic(/*ignoreDefaultNs:*/false, name);
            }
            return f.InvokeElementAvailable(name);
        }
 
        private QilNode CompileFunctionAvailable(QilNode name) {
            f.CheckString(name);
            if (name.NodeType == QilNodeType.LiteralString) {
                XmlQualifiedName qname = ResolveQNameThrow(/*ignoreDefaultNs:*/true, name);
                if (EvaluateFuncCalls) {
                    // Script blocks and extension objects cannot implement neither null nor XSLT namespace
                    if (qname.Namespace.Length == 0 || qname.Namespace == XmlReservedNs.NsXslt) {
                        return f.Boolean(QilGenerator.IsFunctionAvailable(qname.Name, qname.Namespace));
                    }
                    // We might precalculate the result for script namespaces as well
                }
                name = f.QName(qname.Name, qname.Namespace);
            } else {
                name = ResolveQNameDynamic(/*ignoreDefaultNs:*/true, name);
            }
            return f.InvokeFunctionAvailable(name);
        }
 
        private QilNode CompileMsNodeSet(QilNode n) {
            if (n.XmlType.IsNode && n.XmlType.IsNotRtf) {
                return n;
            }
 
            return f.XsltConvert(n, T.NodeSDod);
        }
 
        //------------------------------------------------
        // EXSLT Functions
        //------------------------------------------------
 
        private QilNode EXslObjectType(QilNode n) {
            if (EvaluateFuncCalls) {
                switch (n.XmlType.TypeCode) {
                case XmlTypeCode.Boolean :  return f.String("boolean");
                case XmlTypeCode.Double  :  return f.String("number");
                case XmlTypeCode.String  :  return f.String("string");
                default:
                    if (n.XmlType.IsNode && n.XmlType.IsNotRtf) {
                        return f.String("node-set");
                    }
                    break;
                }
            }
            return f.InvokeEXslObjectType(n);
        }
    }
}