Updates referencesource to .NET 4.7
[mono.git] / mcs / class / referencesource / System.Data.SqlXml / System / Xml / Xsl / Xslt / QilGenerator.cs
1 //------------------------------------------------------------------------------
2 // <copyright file="QilGenerator.cs" company="Microsoft">
3 //     Copyright (c) Microsoft Corporation.  All rights reserved.
4 // </copyright>
5 // <owner current="true" primary="true">Microsoft</owner>
6 // <spec>http://www.w3.org/TR/xslt.html</spec>
7 // <spec>http://www.w3.org/TR/xslt20/</spec>
8 //------------------------------------------------------------------------------
9
10 using System.Collections;
11 using System.Collections.Generic;
12 using System.Collections.Specialized;
13 using System.Diagnostics;
14 using System.Reflection;
15 using System.Text;
16 using System.Xml.Xsl.Qil;
17 using System.Xml.Xsl.Runtime;
18 using System.Xml.Xsl.XPath;
19
20 namespace System.Xml.Xsl.Xslt {
21     using Res           = System.Xml.Utils.Res;
22     using ScopeRecord   = CompilerScopeManager<QilIterator>.ScopeRecord;
23     using T             = XmlQueryTypeFactory;
24
25     // Everywhere in this code in case of error in the stylesheet we should call ReportError or ReportWarning
26
27     internal class ReferenceReplacer : QilReplaceVisitor {
28         private QilReference lookFor, replaceBy;
29
30         public ReferenceReplacer(QilFactory f) : base(f) {
31         }
32
33         public QilNode Replace(QilNode expr, QilReference lookFor, QilReference replaceBy) {
34             QilDepthChecker.Check(expr);
35             this.lookFor = lookFor;
36             this.replaceBy = replaceBy;
37             return VisitAssumeReference(expr);
38         }
39
40         protected override QilNode VisitReference(QilNode n) {
41             return (n == lookFor) ? replaceBy : n;
42         }
43     }
44
45     internal partial class QilGenerator : IErrorHelper {
46         private CompilerScopeManager<QilIterator> scope;
47         private OutputScopeManager      outputScope;
48         private HybridDictionary        prefixesInUse;
49
50         private XsltQilFactory          f;
51         private XPathBuilder            xpathBuilder;
52         private XPathParser<QilNode>    xpathParser;
53         private XPathPatternBuilder     ptrnBuilder;
54         private XPathPatternParser      ptrnParser;
55         private ReferenceReplacer       refReplacer;
56         private KeyMatchBuilder         keyMatchBuilder;
57         private InvokeGenerator         invkGen;
58         private MatcherBuilder          matcherBuilder;
59         private QilStrConcatenator      strConcat;
60         private VariableHelper          varHelper;
61
62         private Compiler        compiler;
63         private QilList         functions;
64         private QilFunction     generalKey;
65         private bool            formatNumberDynamicUsed;
66         private QilList         extPars;
67         private QilList         gloVars;
68         private QilList         nsVars;
69
70         private XmlQueryType    elementOrDocumentType;
71         private XmlQueryType    textOrAttributeType;
72         private XslNode         lastScope;
73         private XslVersion      xslVersion;
74
75         private QilName         nameCurrent;
76         private QilName         namePosition;
77         private QilName         nameLast;
78         private QilName         nameNamespaces;
79         private QilName         nameInit;
80
81         private SingletonFocus  singlFocus;
82         private FunctionFocus   funcFocus;
83         private LoopFocus       curLoop;
84
85         private int             formatterCnt;
86
87         public static QilExpression CompileStylesheet(Compiler compiler) {
88             return new QilGenerator(compiler.IsDebug).Compile(compiler);
89         }
90
91         private QilGenerator(bool debug) {
92             scope           = new CompilerScopeManager<QilIterator>();
93             outputScope     = new OutputScopeManager();
94             prefixesInUse   = new HybridDictionary();
95             f               = new XsltQilFactory(new QilFactory(), debug);
96             xpathBuilder    = new XPathBuilder((IXPathEnvironment) this);
97             xpathParser     = new XPathParser<QilNode>();
98             ptrnBuilder     = new XPathPatternBuilder((IXPathEnvironment) this);
99             ptrnParser      = new XPathPatternParser();
100             refReplacer     = new ReferenceReplacer(f.BaseFactory);
101             invkGen         = new InvokeGenerator(f, debug);
102             matcherBuilder  = new MatcherBuilder(f, refReplacer, invkGen);
103             singlFocus      = new SingletonFocus(f);
104             funcFocus       = new FunctionFocus();
105             curLoop         = new LoopFocus(f);
106             strConcat       = new QilStrConcatenator(f);
107             varHelper       = new VariableHelper(f);
108
109             elementOrDocumentType = T.DocumentOrElement;
110             textOrAttributeType   = T.NodeChoice(XmlNodeKindFlags.Text | XmlNodeKindFlags.Attribute);
111
112             nameCurrent     = f.QName("current"   , XmlReservedNs.NsXslDebug);
113             namePosition    = f.QName("position"  , XmlReservedNs.NsXslDebug);
114             nameLast        = f.QName("last"      , XmlReservedNs.NsXslDebug);
115             nameNamespaces  = f.QName("namespaces", XmlReservedNs.NsXslDebug);
116             nameInit        = f.QName("init"      , XmlReservedNs.NsXslDebug);
117
118             formatterCnt    = 0;
119         }
120
121         private bool IsDebug {
122             get { return compiler.IsDebug; }
123         }
124
125         private bool EvaluateFuncCalls  { get { return !IsDebug; } }
126         private bool InferXPathTypes    { get { return !IsDebug; } }
127
128         private QilExpression Compile(Compiler compiler) {
129             Debug.Assert(compiler != null);
130             this.compiler   = compiler;
131             this.functions  = f.FunctionList();
132             this.extPars    = f.GlobalParameterList();
133             this.gloVars    = f.GlobalVariableList();
134             this.nsVars     = f.GlobalVariableList();
135
136             compiler.Scripts.CompileScripts();
137
138             // Refactor huge templates into smaller ones (more JIT friendly)
139             (new XslAstRewriter()).Rewrite(compiler);
140
141             if (!IsDebug) {
142                 (new XslAstAnalyzer()).Analyze(compiler);
143             }
144
145             // Global variables and external params are visible from everywhere, so we have
146             // to prepopulate the scope with all of them before starting compilation
147             CreateGlobalVarPars();
148
149             try {
150                 CompileKeys();
151                 CompileAndSortMatches(compiler.Root.Imports[0]);
152                 PrecompileProtoTemplatesHeaders();
153                 CompileGlobalVariables();
154
155                 foreach (ProtoTemplate tmpl in compiler.AllTemplates) {
156                     CompileProtoTemplate(tmpl);
157                 }
158                 varHelper.CheckEmpty();
159             }
160             catch (XslLoadException e) {
161                 e.SetSourceLineInfo(lastScope.SourceLine);
162                 throw;
163             }
164             catch (Exception e) {
165                 if (!XmlException.IsCatchableException(e)) {
166                     throw;
167                 }
168                 throw new XslLoadException(e, lastScope.SourceLine);
169             }
170
171             CompileInitializationCode();
172             QilNode root = CompileRootExpression(compiler.StartApplyTemplates);
173
174             // Clean default values which we calculate in caller context
175             foreach (ProtoTemplate tmpl in compiler.AllTemplates) {
176                 foreach (QilParameter par in tmpl.Function.Arguments) {
177                     if (!IsDebug || par.Name.Equals(nameNamespaces)) {
178                         par.DefaultValue = null;
179                     }
180                 }
181             }
182
183             // Create list of all early bound objects
184             Dictionary<string, Type> scriptClasses = compiler.Scripts.ScriptClasses;
185             List<EarlyBoundInfo> ebTypes = new List<EarlyBoundInfo>(scriptClasses.Count);
186             foreach (KeyValuePair<string, Type> pair in scriptClasses) {
187                 if (pair.Value != null) {
188                     ebTypes.Add(new EarlyBoundInfo(pair.Key, pair.Value));
189                 }
190             }
191
192             QilExpression qil = f.QilExpression(root, f.BaseFactory); {
193                 qil.EarlyBoundTypes       = ebTypes;
194                 qil.FunctionList          = functions;
195                 qil.GlobalParameterList   = extPars;
196                 qil.GlobalVariableList    = gloVars;
197                 qil.WhitespaceRules       = compiler.WhitespaceRules;
198                 qil.IsDebug               = IsDebug;
199                 qil.DefaultWriterSettings = compiler.Output.Settings;
200             }
201
202             QilDepthChecker.Check(qil);
203
204             return qil;
205         }
206
207         private QilNode InvokeOnCurrentNodeChanged() {
208             Debug.Assert(IsDebug && curLoop.IsFocusSet);
209             QilIterator i;
210             return f.Loop(i = f.Let(f.InvokeOnCurrentNodeChanged(curLoop.GetCurrent())), f.Sequence());
211         }
212
213         [Conditional("DEBUG")]
214         private void CheckSingletonFocus() {
215             Debug.Assert(!curLoop.IsFocusSet && !funcFocus.IsFocusSet, "Must be compiled using singleton focus");
216         }
217
218         private void CompileInitializationCode() {
219             // Initialization code should be executed before any other code (global variables/parameters or root expression)
220             // For this purpose we insert it as THE FIRST global variable $init (global variables are calculated before global parameters)
221             // and put all initalization code in it.
222             // In retail mode global variables are calculated lasely if they don't have side effects. 
223             // To mark $init as variable with side effect we put all code to function and set SideEffect flag on this function.
224             // ILGen expects that all library functions are sideeffect free. To prevent calls to RegisterDecimalFormat() to be optimized out 
225             // we add results returned from these calls and return them as a result of initialization function.
226             QilNode init = f.Int32(0);
227
228             // Register all decimal formats, they are needed for format-number()
229             if (formatNumberDynamicUsed || IsDebug) {
230                 bool defaultDefined = false;
231                 foreach (DecimalFormatDecl format in compiler.DecimalFormats) {
232                     init = f.Add(init, f.InvokeRegisterDecimalFormat(format));
233                     defaultDefined |= (format.Name == DecimalFormatDecl.Default.Name);
234                 }
235                 if (!defaultDefined) {
236                     init = f.Add(init, f.InvokeRegisterDecimalFormat(DecimalFormatDecl.Default));
237                 }
238             }
239
240             // Register all script namespaces
241             foreach (string scriptNs in compiler.Scripts.ScriptClasses.Keys) {
242                 init = f.Add(init, f.InvokeCheckScriptNamespace(scriptNs));
243             }
244
245             if (init.NodeType == QilNodeType.Add) {
246                 QilFunction initFunction = f.Function(f.FormalParameterList(), init, /*sideEffects:*/f.True());
247                 initFunction.DebugName = "Init";
248                 this.functions.Add(initFunction);
249
250                 QilNode initBinding = f.Invoke(initFunction, f.ActualParameterList());
251                 if (IsDebug) {
252                     // In debug mode all variables must have type item*
253                     initBinding = f.TypeAssert(initBinding, T.ItemS);
254                 }
255                 QilIterator initVar = f.Let(initBinding);
256                 initVar.DebugName = nameInit.ToString();
257                 gloVars.Insert(0, initVar);
258             }
259         }
260
261         private QilNode CompileRootExpression(XslNode applyTmpls) {
262             // Compile start apply-templates call
263             CheckSingletonFocus();
264             singlFocus.SetFocus(SingletonFocusType.InitialContextNode);
265             QilNode result = GenerateApply(compiler.Root, applyTmpls);
266             singlFocus.SetFocus(null);
267
268             return f.DocumentCtor(result);
269         }
270
271         private QilList EnterScope(XslNode node) {
272             // This is the only place where lastScope is changed
273             lastScope = node;
274             xslVersion = node.XslVersion;
275             if (this.scope.EnterScope(node.Namespaces)) {
276                 return BuildDebuggerNamespaces();
277             }
278             return null;
279         }
280
281         private void ExitScope() {
282             this.scope.ExitScope();
283         }
284
285         private QilList BuildDebuggerNamespaces() {
286             if (IsDebug) {
287                 QilList nsDecls = f.BaseFactory.Sequence();
288                 foreach (ScopeRecord rec in this.scope) {
289                     nsDecls.Add(f.NamespaceDecl(f.String(rec.ncName), f.String(rec.nsUri)));
290                 }
291                 return nsDecls;
292             }
293             return null;
294         }
295
296         // For each call instruction - call-template, use-attribute-sets, apply-template, apply-imports - we have
297         // to pass the current execution context which may be represented as three additional implicit arguments:
298         // current, position, last.  In most cases the last two ones are never used, so for the purpose
299         // of optimization in non-debug mode we bind them only if they are actually needed.
300
301         // Strictly speaking, a (proto)template function is supplied with the additional position argument if both
302         // the following conditions are true:
303         //   1. At least one template within the given stylesheet contains a "----" position() function invocation
304         //      (needPositionArgs == true).  ---- here means "not within any of for-each instructions".
305         //   2. THIS template contains a ---- position() invocation or a ---- call-template, use-attribute-sets,
306         //      or apply-imports instruction.  Note: apply-template's are not taken into account because in that
307         //      case the call will be actually wrapped in a tuple.
308         //
309         // The same is true for additional last arguments.
310
311         // There are 3 cases when context methods may be called:
312         // 1. In context of for-each expression
313         // 2. In context of template
314         // 3. In context of global variable
315         // We treating this methods differentely when they are called to create implicit arguments.
316         // Implicite argument (position, last) are rare and lead to uneficiant code. So we treating them
317         // specialy to be able eliminate them later, wen we compiled everithing and can detect was they used or not.
318
319         // Returns context node
320         private QilNode GetCurrentNode() {
321             if (curLoop.IsFocusSet) {
322                 return curLoop.GetCurrent();
323             } else if (funcFocus.IsFocusSet) {
324                 return funcFocus.GetCurrent();
325             } else {
326                 return singlFocus.GetCurrent();
327             }
328         }
329
330         // Returns context position
331         private QilNode GetCurrentPosition() {
332             if (curLoop.IsFocusSet) {
333                 return curLoop.GetPosition();
334             } else if (funcFocus.IsFocusSet) {
335                 return funcFocus.GetPosition();
336             } else {
337                 return singlFocus.GetPosition();
338             }
339         }
340
341         // Returns context size
342         private QilNode GetLastPosition() {
343             if (curLoop.IsFocusSet) {
344                 return curLoop.GetLast();
345             } else if (funcFocus.IsFocusSet) {
346                 return funcFocus.GetLast();
347             } else {
348                 return singlFocus.GetLast();
349             }
350         }
351
352         private XmlQueryType ChooseBestType(VarPar var) {
353             if (IsDebug || !InferXPathTypes) {
354                 return T.ItemS;
355             }
356
357             switch (var.Flags & XslFlags.TypeFilter) {
358             case XslFlags.String  : return T.StringX;;
359             case XslFlags.Number  : return T.DoubleX;
360             case XslFlags.Boolean : return T.BooleanX;
361             case XslFlags.Node    : return T.NodeNotRtf;
362             case XslFlags.Nodeset : return T.NodeNotRtfS;
363             case XslFlags.Rtf     : return T.Node;
364             case XslFlags.Node    | XslFlags.Rtf     : return T.Node;
365             case XslFlags.Node    | XslFlags.Nodeset : return T.NodeNotRtfS;
366             case XslFlags.Nodeset | XslFlags.Rtf     : return T.NodeS;
367             case XslFlags.Node    | XslFlags.Nodeset | XslFlags.Rtf : return T.NodeS;
368             default               : return T.ItemS;
369             }
370         }
371
372         // In debugger we need to pass to each (almost) template $namespace parameter with list of namespaces that
373         // are defined on stylesheet and this template. In most cases this will be only xmlns:xsl="..."
374         // To prevent creating these list with each call-template/apply-template we create one global variable for each unique set of namespaces
375         // This function looks through list of existent global variables for suitable ns list and add one if none was found.
376         private QilIterator GetNsVar(QilList nsList) {
377             Debug.Assert(IsDebug, "This is debug only logic");
378             // All global vars at this point are nsList like one we are looking now.
379             foreach (QilIterator var in this.nsVars) {
380                 Debug.Assert(var.XmlType.IsSubtypeOf(T.NamespaceS));
381                 Debug.Assert(var.Binding is QilList);
382                 QilList varList = (QilList)var.Binding;
383                 if (varList.Count != nsList.Count) {
384                     continue;
385                 }
386                 bool found = true;
387                 for (int i = 0; i < nsList.Count; i ++) {
388                     Debug.Assert(nsList[i].NodeType  == QilNodeType.NamespaceDecl);
389                     Debug.Assert(varList[i].NodeType == QilNodeType.NamespaceDecl);
390                     if (
391                         ((QilLiteral)((QilBinary)nsList[i]).Right).Value != ((QilLiteral)((QilBinary)varList[i]).Right).Value ||
392                         ((QilLiteral)((QilBinary)nsList[i]).Left ).Value != ((QilLiteral)((QilBinary)varList[i]).Left ).Value
393                     ) {
394                         found = false;
395                         break;
396                     }
397                 }
398                 if (found) {
399                     return var;  // Found!
400                 }
401             }
402             QilIterator newVar = f.Let(nsList);
403             newVar.DebugName = f.QName("ns" + this.nsVars.Count, XmlReservedNs.NsXslDebug).ToString();
404             this.gloVars.Add(newVar);
405             this.nsVars.Add(newVar);
406             return newVar;
407         }
408
409         private void PrecompileProtoTemplatesHeaders() {
410             // All global variables should be in scoupe here.
411             List<VarPar>                     paramWithCalls  = null;
412             Dictionary<VarPar, Template   >  paramToTemplate = null;
413             Dictionary<VarPar, QilFunction>  paramToFunction = null;
414
415             foreach (ProtoTemplate tmpl in compiler.AllTemplates) {
416                 Debug.Assert(tmpl != null && tmpl.Function == null);
417                 Debug.Assert(tmpl.NodeType == XslNodeType.AttributeSet || tmpl.NodeType == XslNodeType.Template);
418                 QilList args = f.FormalParameterList();
419                 XslFlags flags = !IsDebug ? tmpl.Flags : XslFlags.FullFocus;
420
421                 QilList nsList = EnterScope(tmpl);
422                 if ((flags & XslFlags.Current) != 0) {
423                     args.Add(CreateXslParam(CloneName(nameCurrent), T.NodeNotRtf));
424                 }
425                 if ((flags & XslFlags.Position) != 0) {
426                     args.Add(CreateXslParam(CloneName(namePosition), T.DoubleX));
427                 }
428                 if ((flags & XslFlags.Last) != 0) {
429                     args.Add(CreateXslParam(CloneName(nameLast), T.DoubleX));
430                 }
431                 if (IsDebug && nsList != null) {
432                     // AttributeSet doesn't need this logic because: 1) it doesn't have args; 2) we merge them.
433                     // SimplifiedStylesheet has nsList == null as well.
434                     QilParameter ns = CreateXslParam(CloneName(nameNamespaces), T.NamespaceS);
435                     ns.DefaultValue = GetNsVar(nsList);
436                     args.Add(ns);
437                 }
438
439                 Template template = tmpl as Template;
440                 if (template != null) {
441                     Debug.Assert(tmpl.NodeType == XslNodeType.Template);
442
443                     CheckSingletonFocus();
444                     funcFocus.StartFocus(args, flags);
445                     for (int i = 0; i < tmpl.Content.Count; i++) {
446                         XslNode node = tmpl.Content[i];
447                         if (node.NodeType == XslNodeType.Text) {
448                             // NOTE: We should take care of a bizarre case when xsl:param comes after TextCtor:
449                             // <xsl:template match="/" xml:space="preserve">  <xsl:param name="par"/>
450                             continue;
451                         }
452                         if (node.NodeType == XslNodeType.Param) {
453                             VarPar xslPar = (VarPar)node;
454                             EnterScope(xslPar);
455                             if (scope.IsLocalVariable(xslPar.Name.LocalName, xslPar.Name.NamespaceUri)) {
456                                 ReportError(/*[XT0580]*/Res.Xslt_DupLocalVariable, xslPar.Name.QualifiedName);
457                             }
458                             QilParameter param = CreateXslParam(xslPar.Name, ChooseBestType(xslPar));
459                             if (IsDebug) {
460                                 param.Annotation = xslPar;
461                                 // Actual compilation will happen in CompileProtoTemplate()
462                             } else {
463                                 if ((xslPar.DefValueFlags & XslFlags.HasCalls) == 0) {
464                                     param.DefaultValue = CompileVarParValue(xslPar);
465                                 } else {
466                                     // We can't compile param default value here because it contains xsl:call-template and
467                                     // we will not be able to compile any calls befor we finish with all headers
468                                     // So we compile this default value as a call to helper function. Now we create header for this function
469                                     // and preserve this param in paramWithCall list. Later in this function we finaly compile all preserved
470                                     // parameters and set resulted default values as helper function definition.
471                                     QilList paramFormal = f.FormalParameterList();
472                                     QilList paramActual = f.ActualParameterList();
473                                     for (int j = 0; j < args.Count; j ++) {
474                                         QilParameter formal = f.Parameter(args[j].XmlType); {
475                                             formal.DebugName = ((QilParameter) args[j]).DebugName;
476                                             formal.Name = CloneName(((QilParameter)args[j]).Name);
477                                             SetLineInfo(formal, args[j].SourceLine);
478                                         }
479                                         paramFormal.Add(formal);
480                                         paramActual.Add(args[j]);
481                                     }
482                                     // Param doesn't know what implicit args it needs, so we pass all implicit args that was passed to its template.
483                                     // let's reflect this fact in parans FocusFlags:
484                                     xslPar.Flags |= (template.Flags & XslFlags.FocusFilter);
485                                     QilFunction paramFunc = f.Function(paramFormal,
486                                         f.Boolean((xslPar.DefValueFlags & XslFlags.SideEffects) != 0),
487                                         ChooseBestType(xslPar)
488                                     );
489                                     paramFunc.SourceLine = SourceLineInfo.NoSource;
490                                     paramFunc.DebugName = "<xsl:param name=\"" + xslPar.Name.QualifiedName + "\">";
491                                     param.DefaultValue = f.Invoke(paramFunc, paramActual);
492                                     // store VarPar here to compile it on next pass:
493                                     if (paramWithCalls == null) {
494                                         paramWithCalls  = new List<VarPar>();
495                                         paramToTemplate = new Dictionary<VarPar, Template>();
496                                         paramToFunction = new Dictionary<VarPar, QilFunction>();
497                                     }
498                                     paramWithCalls.Add(xslPar);
499                                     paramToTemplate.Add(xslPar, template );
500                                     paramToFunction.Add(xslPar, paramFunc);
501                                 }
502                             }
503                             SetLineInfo(param, xslPar.SourceLine);
504                             ExitScope();
505                             scope.AddVariable(xslPar.Name, param);
506                             args.Add(param);
507                         } else {
508                             break;
509                         }
510                     }
511                     funcFocus.StopFocus();
512                 }
513                 ExitScope();
514
515                 tmpl.Function = f.Function(args,
516                     f.Boolean((tmpl.Flags & XslFlags.SideEffects) != 0),
517                     tmpl is AttributeSet ? T.AttributeS : T.NodeNotRtfS
518                 );
519                 tmpl.Function.DebugName = tmpl.GetDebugName();
520                 Debug.Assert((template != null) == (tmpl.SourceLine != null), "Templates must have line information, and attribute sets must not");
521                 SetLineInfo(tmpl.Function, tmpl.SourceLine ?? SourceLineInfo.NoSource);
522                 this.functions.Add(tmpl.Function);
523             } // foreach (ProtoTemplate tmpl in compiler.AllTemplates)
524
525             // Finish compiling postponed parameters (those having calls in their default values)
526             if (paramWithCalls != null) {
527                 Debug.Assert(! IsDebug, "In debug mode we don't generate parumWithCalls functions. Otherwise focus flags should be adjusted");
528                 foreach (VarPar par in paramWithCalls) {
529                     Template    tmpl = paramToTemplate[par];
530                     QilFunction func = paramToFunction[par];
531                     CheckSingletonFocus();
532                     funcFocus.StartFocus(func.Arguments, par.Flags);
533                     EnterScope(tmpl);
534                     EnterScope(par);
535                     foreach (QilParameter arg in func.Arguments) {
536                         scope.AddVariable(arg.Name, arg);
537                     }
538                     func.Definition = CompileVarParValue(par);
539                     SetLineInfo(func.Definition, par.SourceLine);
540                     ExitScope();
541                     ExitScope();
542                     funcFocus.StopFocus();
543                     this.functions.Add(func);
544                 }
545             }
546         }
547
548         private QilParameter CreateXslParam(QilName name, XmlQueryType xt) {
549             QilParameter arg = f.Parameter(xt);
550             arg.DebugName = name.ToString();
551             arg.Name = name;
552             return arg;
553         }
554
555         private void CompileProtoTemplate(ProtoTemplate tmpl) {
556             Debug.Assert(tmpl != null && tmpl.Function != null && tmpl.Function.Definition.NodeType == QilNodeType.Unknown);
557
558             EnterScope(tmpl);
559
560             CheckSingletonFocus();
561             funcFocus.StartFocus(tmpl.Function.Arguments, !IsDebug ? tmpl.Flags : XslFlags.FullFocus);
562             foreach (QilParameter arg in tmpl.Function.Arguments) {
563                 if (arg.Name.NamespaceUri != XmlReservedNs.NsXslDebug) {
564                     Debug.Assert(tmpl is Template, "Only templates can have explicit arguments");
565                     if (IsDebug) {
566                         Debug.Assert(arg.DefaultValue == null, "Argument must not be compiled yet");
567                         VarPar xslParam = (VarPar)arg.Annotation;
568                         QilList nsListParam = EnterScope(xslParam);
569                         arg.DefaultValue = CompileVarParValue(xslParam);
570                         ExitScope();
571                         arg.DefaultValue = SetDebugNs(arg.DefaultValue, nsListParam);
572                     } else {
573                         // in !IsDebug we compile argument default value in PrecompileProtoTemplatesHeaders()
574                     }
575                     scope.AddVariable(arg.Name, arg);
576                 }
577             }
578             tmpl.Function.Definition = CompileInstructions(tmpl.Content);
579             // tmpl.Function.Definition = AddCurrentPositionLast(tmpl.Function.Definition); We don't mask Cur,Pos,Last parameters with Cur,Pos,Last wariables any more
580             // tmpl.Function.Definition = SetDebugNs(tmpl.Function.Definition, nsList); We add it as parameter now.
581             funcFocus.StopFocus();
582
583             ExitScope();
584         }
585
586         private QilList InstructionList() {
587             return f.BaseFactory.Sequence();
588         }
589
590         private QilNode CompileInstructions(IList<XslNode> instructions) {
591             return CompileInstructions(instructions, 0, InstructionList());
592         }
593
594         private QilNode CompileInstructions(IList<XslNode> instructions, int from) {
595             return CompileInstructions(instructions, from, InstructionList());
596         }
597
598         private QilNode CompileInstructions(IList<XslNode> instructions, QilList content) {
599             return CompileInstructions(instructions, 0, content);
600         }
601
602         private QilNode CompileInstructions(IList<XslNode> instructions, int from, QilList content) {
603             Debug.Assert(instructions != null);
604             for (int i = from; i < instructions.Count; i++) {
605                 XslNode     node     = instructions[i];
606                 XslNodeType nodeType = node.NodeType;
607                 if (nodeType == XslNodeType.Param) {
608                     continue; // already compiled by CompileProtoTemplate()
609                 }
610                 QilList     nsList   = EnterScope(node);
611                 QilNode     result;
612
613                 switch (nodeType) {
614                 case XslNodeType.ApplyImports:      result = CompileApplyImports    (node); break;
615                 case XslNodeType.ApplyTemplates:    result = CompileApplyTemplates  ((XslNodeEx)node); break;
616                 case XslNodeType.Attribute:         result = CompileAttribute       ((NodeCtor)node); break;
617                 case XslNodeType.CallTemplate:      result = CompileCallTemplate    ((XslNodeEx)node); break;
618                 case XslNodeType.Choose:            result = CompileChoose          (node); break;
619                 case XslNodeType.Comment:           result = CompileComment         (node); break;
620                 case XslNodeType.Copy:              result = CompileCopy            (node); break;
621                 case XslNodeType.CopyOf:            result = CompileCopyOf          (node); break;
622                 case XslNodeType.Element:           result = CompileElement         ((NodeCtor)node); break;
623                 case XslNodeType.Error:             result = CompileError           (node); break;
624                 case XslNodeType.ForEach:           result = CompileForEach         ((XslNodeEx)node); break;
625                 case XslNodeType.If:                result = CompileIf              (node); break;
626                 case XslNodeType.List:              result = CompileList            (node); break;
627                 case XslNodeType.LiteralAttribute:  result = CompileLiteralAttribute(node); break;
628                 case XslNodeType.LiteralElement:    result = CompileLiteralElement  (node); break;
629                 case XslNodeType.Message:           result = CompileMessage         (node); break;
630                 case XslNodeType.Nop:               result = CompileNop             (node); break;
631                 case XslNodeType.Number:            result = CompileNumber          ((Number)node); break;
632 //              case XslNodeType.Otherwise:         wrapped by Choose
633 //              case XslNodeType.Param:             already compiled by CompileProtoTemplate()
634                 case XslNodeType.PI:                result = CompilePI              (node); break;
635 //              case XslNodeType.Sort:              wrapped by ForEach or ApplyTemplates, see CompileSorts()
636 //              case XslNodeType.Template:          global level element
637                 case XslNodeType.Text:              result = CompileText            ((Text)node); break;
638                 case XslNodeType.UseAttributeSet:   result = CompileUseAttributeSet (node); break;
639                 case XslNodeType.ValueOf:           result = CompileValueOf         (node); break;
640                 case XslNodeType.ValueOfDoe:        result = CompileValueOfDoe      (node); break;
641                 case XslNodeType.Variable:          result = CompileVariable        (node); break;
642 //              case XslNodeType.WithParam:         wrapped by CallTemplate or ApplyTemplates, see CompileWithParam()
643                 default:                            Debug.Fail("Unexpected type of AST node: " + nodeType.ToString()); result = null; break;
644                 }
645
646                 ExitScope();
647                 Debug.Assert(result != null, "Result of compilation should not be null");
648                 if (result.NodeType == QilNodeType.Sequence && result.Count == 0) {
649                     continue;
650                 }
651
652                 // Do not create sequence points for literal attributes and use-attribute-sets
653                 if (nodeType != XslNodeType.LiteralAttribute && nodeType != XslNodeType.UseAttributeSet) {
654                     SetLineInfoCheck(result, node.SourceLine);
655                 }
656
657                 result = SetDebugNs(result, nsList);
658                 if (nodeType == XslNodeType.Variable) {
659                     QilIterator var = f.Let(result);
660                     var.DebugName = node.Name.ToString();
661                     scope.AddVariable(node.Name, var);
662                     // Process all remaining instructions in the recursive call
663                     result = f.Loop(var, CompileInstructions(instructions, i + 1));
664                     i = instructions.Count;
665                 }
666
667                 content.Add(result);
668             }
669             if (!IsDebug && content.Count == 1) {
670                 return content[0];
671             }
672             return content;
673         }
674
675         private QilNode CompileList(XslNode node) {
676             return CompileInstructions(node.Content);
677         }
678
679         private QilNode CompileNop(XslNode node) {
680             return f.Nop(f.Sequence());
681         }
682
683         private void AddNsDecl(QilList content, string prefix, string nsUri) {
684             if (this.outputScope.LookupNamespace(prefix) == nsUri) {
685                 return; // This prefix is already bound to required namespace. Nothing to do.
686             }
687             this.outputScope.AddNamespace(prefix, nsUri);
688             content.Add(f.NamespaceDecl(f.String(prefix), f.String(nsUri)));
689         }
690
691         private QilNode CompileLiteralElement(XslNode node) {
692             // IlGen requires that namespace declarations do not conflict with the namespace used by the element
693             // constructor, see XmlILNamespaceAnalyzer.CheckNamespaceInScope() and SQLBUDT 389481, 389482. First
694             // we try to replace all prefixes bound to aliases by result-prefixes of the corresponding
695             // xsl:namespace-alias instructions. If there is at least one conflict, we leave all prefixes
696             // untouched, changing only namespace URIs.
697             bool changePrefixes = true;
698
699         Start:
700             prefixesInUse.Clear();
701
702             QilName qname   = node.Name;
703             string  prefix  = qname.Prefix;
704             string  nsUri   = qname.NamespaceUri;
705
706             compiler.ApplyNsAliases(ref prefix, ref nsUri);
707             if (changePrefixes) {
708                 prefixesInUse.Add(prefix, nsUri);
709             } else {
710                 prefix = qname.Prefix;
711             }
712
713             outputScope.PushScope();
714
715             // Declare all namespaces that should be declared
716             // <spec>http://www.w3.org/TR/xslt.html#literal-result-element</spec>
717             QilList nsList = InstructionList();
718             foreach (ScopeRecord rec in this.scope) {
719                 string recPrefix = rec.ncName;
720                 string recNsUri  = rec.nsUri;
721                 if (recNsUri != XmlReservedNs.NsXslt && !scope.IsExNamespace(recNsUri)) {
722                     compiler.ApplyNsAliases(ref recPrefix, ref recNsUri);
723                     if (changePrefixes) {
724                         if (prefixesInUse.Contains(recPrefix)) {
725                             if ((string)prefixesInUse[recPrefix] != recNsUri) {
726                                 // Found a prefix conflict. Start again from the beginning leaving all prefixes untouched.
727                                 outputScope.PopScope();
728                                 changePrefixes = false;
729                                 goto Start;
730                             }
731                         } else {
732                             prefixesInUse.Add(recPrefix, recNsUri);
733                         }
734                     } else {
735                         recPrefix = rec.ncName;
736                     }
737                     AddNsDecl(nsList, recPrefix, recNsUri);
738                 }
739             }
740
741             QilNode content = CompileInstructions(node.Content, nsList);
742             outputScope.PopScope();
743
744             qname.Prefix = prefix;
745             qname.NamespaceUri = nsUri;
746             return f.ElementCtor(qname, content);
747         }
748
749         private QilNode CompileElement(NodeCtor node) {
750             QilNode qilNs   = CompileStringAvt(node.NsAvt);
751             QilNode qilName = CompileStringAvt(node.NameAvt);
752             QilNode qname;
753
754             if (qilName.NodeType == QilNodeType.LiteralString && (qilNs == null || qilNs.NodeType == QilNodeType.LiteralString)) {
755                 string name = (string)(QilLiteral)qilName;
756                 string prefix, local, nsUri;
757
758                 bool isValid = compiler.ParseQName(name, out prefix, out local, (IErrorHelper)this);
759
760                 if (qilNs == null) {
761                     nsUri = isValid ? ResolvePrefix(/*ignoreDefaultNs:*/false, prefix) : compiler.CreatePhantomNamespace();
762                 } else {
763                     nsUri = (string)(QilLiteral)qilNs;
764                 }
765                 qname = f.QName(local, nsUri, prefix);
766             } else {           // Process AVT
767                 if (qilNs != null) {
768                     qname = f.StrParseQName(qilName, qilNs);
769                 } else {
770                     qname = ResolveQNameDynamic(/*ignoreDefaultNs:*/false, qilName);
771                 }
772             }
773
774             outputScope.PushScope();
775             // ToDo if we don't have AVT we shouldn't do this:
776             this.outputScope.InvalidateAllPrefixes();
777             QilNode content = CompileInstructions(node.Content);
778             outputScope.PopScope();
779
780             return f.ElementCtor(qname, content);
781         }
782
783         private QilNode CompileLiteralAttribute(XslNode node) {
784             QilName qname   = node.Name;
785             string  prefix  = qname.Prefix;
786             string  nsUri   = qname.NamespaceUri;
787             // The default namespace do not apply directly to attributes
788             if (prefix.Length != 0) {
789                 compiler.ApplyNsAliases(ref prefix, ref nsUri);
790             }
791             qname.Prefix = prefix;
792             qname.NamespaceUri = nsUri;
793             return f.AttributeCtor(qname, CompileTextAvt(node.Select));
794         }
795
796         private QilNode CompileAttribute(NodeCtor node) {
797             QilNode qilNs   = CompileStringAvt(node.NsAvt);
798             QilNode qilName = CompileStringAvt(node.NameAvt);
799             QilNode qname;
800             bool    explicitNamespace = false;
801
802             if (qilName.NodeType == QilNodeType.LiteralString && (qilNs == null || qilNs.NodeType == QilNodeType.LiteralString)) {
803                 string name  = (string)(QilLiteral)qilName;
804                 string prefix, local, nsUri;
805
806                 bool isValid = compiler.ParseQName(name, out prefix, out local, (IErrorHelper)this);
807
808                 if (qilNs == null) {
809                     nsUri = isValid ? ResolvePrefix(/*ignoreDefaultNs:*/true, prefix) : compiler.CreatePhantomNamespace();
810                 } else {
811                     nsUri = (string)(QilLiteral)qilNs;
812                     // if both name and ns are non AVT and this ns is already bind to the same prefix we can avoid reseting ns management
813                     explicitNamespace = true;
814                 }
815                 // Check the case <xsl:attribute name="foo:xmlns" namespace=""/>
816                 if (name == "xmlns" || local == "xmlns" && nsUri.Length == 0) {
817                     ReportError(/*[XT_031]*/Res.Xslt_XmlnsAttr, "name", name);
818                 }
819                 qname = f.QName(local, nsUri, prefix);
820             } else {
821                 // Process AVT
822                 if (qilNs != null) {
823                     qname = f.StrParseQName(qilName, qilNs);
824                 } else {
825                     qname = ResolveQNameDynamic(/*ignoreDefaultNs:*/true, qilName);
826                 }
827             }
828             if (explicitNamespace) {
829                 // Optimization: attribute cannot change the default namespace
830                 this.outputScope.InvalidateNonDefaultPrefixes();
831             }
832             return f.AttributeCtor(qname, CompileInstructions(node.Content));
833         }
834
835         private readonly StringBuilder unescapedText = new StringBuilder();
836
837         private QilNode ExtractText(string source, ref int pos) {
838             Debug.Assert(pos < source.Length);
839             int i, start = pos;
840
841             unescapedText.Length = 0;
842             for (i = pos; i < source.Length; i++) {
843                 char ch = source[i];
844
845                 if (ch == '{' || ch == '}') {
846                     if (i + 1 < source.Length && source[i + 1] == ch) {     // "{{" or "}}"
847                         // Double curly brace outside an expression is replaced by a single one
848                         i++;
849                         unescapedText.Append(source, start, i - start);
850                         start = i + 1;
851                     } else if (ch == '{') {                                 // single '{'
852                         // Expression encountered, returning
853                         break;
854                     } else {                                                // single '}'
855                         // Single '}' outside an expression is an error
856                         pos = source.Length;
857                         if (xslVersion != XslVersion.ForwardsCompatible) {
858                             ReportError(/*[XT0370]*/Res.Xslt_SingleRightBraceInAvt, source);
859                             return null;
860                         }
861                         return f.Error(lastScope.SourceLine, Res.Xslt_SingleRightBraceInAvt, source);
862                     }
863                 }
864             }
865
866             Debug.Assert(i == source.Length || source[i] == '{');
867             pos = i;
868             if (unescapedText.Length == 0) {
869                 return i > start ? f.String(source.Substring(start, i - start)) : null;
870             } else {
871                 unescapedText.Append(source, start, i - start);
872                 return f.String(unescapedText.ToString());
873             }
874         }
875
876         private QilNode CompileAvt(string source) {
877             QilList result = f.BaseFactory.Sequence();
878             int pos = 0;
879             while (pos < source.Length) {
880                 QilNode fixedPart = ExtractText(source, ref pos);
881                 if (fixedPart != null) {
882                     result.Add(fixedPart);
883                 }
884                 if (pos < source.Length) { // '{' encountered, parse an expression
885                     pos++;
886                     QilNode exp = CompileXPathExpressionWithinAvt(source, ref pos);
887                     result.Add(f.ConvertToString(exp));
888                 }
889             }
890             if (result.Count == 1) {
891                 return result[0];
892             }
893             return result;
894         }
895
896         static readonly char[] curlyBraces = {'{', '}'};
897
898         private QilNode CompileStringAvt(string avt) {
899             if (avt == null) {
900                 return null;
901             }
902             if (avt.IndexOfAny(curlyBraces) == -1) {
903                 return f.String(avt);
904             }
905             return f.StrConcat(CompileAvt(avt));
906         }
907
908         private QilNode CompileTextAvt(string avt) {
909             Debug.Assert(avt != null);
910             if (avt.IndexOfAny(curlyBraces) == -1) {
911                 return f.TextCtor(f.String(avt));
912             }
913             QilNode avtParts = CompileAvt(avt);
914             if (avtParts.NodeType == QilNodeType.Sequence) {
915                 QilList result = InstructionList();
916                 foreach (QilNode node in avtParts) {
917                     result.Add(f.TextCtor(node));
918                 }
919                 return result;
920             } else {
921                 return f.TextCtor(avtParts);
922             }
923         }
924
925         private QilNode CompileText(Text node) {
926             if (node.Hints == SerializationHints.None)
927                 return f.TextCtor(f.String(node.Select));
928
929             return f.RawTextCtor(f.String(node.Select));
930         }
931
932         private QilNode CompilePI(XslNode node) {
933             QilNode qilName = CompileStringAvt(node.Select);
934             if (qilName.NodeType == QilNodeType.LiteralString) {
935                 string name = (string)(QilLiteral)qilName;
936                 compiler.ValidatePiName(name, (IErrorHelper)this);
937             }
938             return f.PICtor(qilName, CompileInstructions(node.Content));
939         }
940
941         private QilNode CompileComment(XslNode node) {
942             return f.CommentCtor(CompileInstructions(node.Content));
943         }
944
945         private QilNode CompileError(XslNode node) {
946             return f.Error(f.String(node.Select));
947         }
948
949         private QilNode WrapLoopBody(ISourceLineInfo before, QilNode expr, ISourceLineInfo after) {
950             Debug.Assert(curLoop.IsFocusSet);
951             if (IsDebug) {
952                 return f.Sequence(
953                     SetLineInfo(InvokeOnCurrentNodeChanged(), before),
954                     expr,
955                     SetLineInfo(f.Nop(f.Sequence()), after)
956                 );
957             }
958             return expr;
959         }
960
961         private QilNode CompileForEach(XslNodeEx node) {
962             QilNode         result;
963             IList<XslNode>  content = node.Content;
964
965             // Push new loop frame on the stack
966             LoopFocus curLoopSaved = curLoop;
967             QilIterator it = f.For(CompileNodeSetExpression(node.Select));
968             curLoop.SetFocus(it);
969
970             // Compile sort keys and body
971             int varScope = varHelper.StartVariables();
972             curLoop.Sort(CompileSorts(content, ref curLoopSaved));
973             result = CompileInstructions(content);
974             result = WrapLoopBody(node.ElemNameLi, result, node.EndTagLi);
975             result = AddCurrentPositionLast(result);
976             result = curLoop.ConstructLoop(result);
977             result = varHelper.FinishVariables(result, varScope);
978
979             // Pop loop frame
980             curLoop = curLoopSaved;
981             return result;
982         }
983
984         private QilNode CompileApplyTemplates(XslNodeEx node) {
985             QilNode         result;
986             IList<XslNode>  content = node.Content;
987
988             // Calculate select expression
989             int varScope = varHelper.StartVariables();
990
991             QilIterator select = f.Let(CompileNodeSetExpression(node.Select));
992             varHelper.AddVariable(select);
993
994             // Compile with-param's, they must be calculated outside the loop and
995             // if they are neither constant nor reference we need to cache them in Let's
996             for (int i = 0; i < content.Count; i++) {
997                 VarPar withParam = content[i] as VarPar;
998                 if (withParam != null) {
999                     Debug.Assert(withParam.NodeType == XslNodeType.WithParam);
1000                     CompileWithParam(withParam);
1001                     QilNode val = withParam.Value;
1002                     if (IsDebug || !(val is QilIterator || val is QilLiteral)) {
1003                         QilIterator let = f.Let(val);
1004                         let.DebugName = f.QName("with-param " + withParam.Name.QualifiedName, XmlReservedNs.NsXslDebug).ToString();
1005                         varHelper.AddVariable(let);
1006                         withParam.Value = let;
1007                     }
1008                 }
1009             }
1010
1011             // Push new loop frame on the stack
1012             LoopFocus curLoopSaved = curLoop;
1013             QilIterator it = f.For(select);
1014             curLoop.SetFocus(it);
1015
1016             // Compile sort keys and body
1017             curLoop.Sort(CompileSorts(content, ref curLoopSaved));
1018
1019             result = GenerateApply(compiler.Root, node);
1020
1021             result = WrapLoopBody(node.ElemNameLi, result, node.EndTagLi);
1022             result = AddCurrentPositionLast(result);
1023             result = curLoop.ConstructLoop(result);
1024
1025             // Pop loop frame
1026             curLoop = curLoopSaved;
1027
1028             result = varHelper.FinishVariables(result, varScope);
1029             return result;
1030         }
1031
1032         private QilNode CompileApplyImports(XslNode node) {
1033             Debug.Assert(node.NodeType == XslNodeType.ApplyImports);
1034             Debug.Assert(!curLoop.IsFocusSet, "xsl:apply-imports cannot be inside of xsl:for-each");
1035
1036             return GenerateApply((StylesheetLevel)node.Arg, node);
1037         }
1038
1039         private QilNode CompileCallTemplate(XslNodeEx node) {
1040             VerifyXPathQName(node.Name);
1041             int varScope = varHelper.StartVariables();
1042
1043             IList<XslNode> content = node.Content;
1044             foreach (VarPar withParam in content) {
1045                 CompileWithParam(withParam);
1046                 // In debug mode precalculate all with-param's
1047                 if (IsDebug) {
1048                     QilNode val = withParam.Value;
1049                     QilIterator let = f.Let(val);
1050                     let.DebugName = f.QName("with-param " + withParam.Name.QualifiedName, XmlReservedNs.NsXslDebug).ToString();
1051                     varHelper.AddVariable(let);
1052                     withParam.Value = let;
1053                 }
1054             }
1055
1056             QilNode result; {
1057                 Template tmpl;
1058                 if (compiler.NamedTemplates.TryGetValue(node.Name, out tmpl)) {
1059                     Debug.Assert(tmpl.Function != null, "All templates should be already compiled");
1060                     result = invkGen.GenerateInvoke(tmpl.Function, AddRemoveImplicitArgs(node.Content, tmpl.Flags));
1061                 } else {
1062                     if (! compiler.IsPhantomName(node.Name)) {
1063                         compiler.ReportError(/*[XT0710]*/node.SourceLine, Res.Xslt_InvalidCallTemplate, node.Name.QualifiedName);
1064                     }
1065                     result = f.Sequence();
1066                 }
1067             }
1068
1069             // Do not create an additional sequence point if there are no parameters
1070             if (content.Count > 0) {
1071                 result = SetLineInfo(result, node.ElemNameLi);
1072             }
1073             result = varHelper.FinishVariables(result, varScope);
1074             if (IsDebug) {
1075                 return f.Nop(result);
1076             }
1077             return result;
1078         }
1079
1080         private QilNode CompileUseAttributeSet(XslNode node) {
1081             VerifyXPathQName(node.Name);
1082             // 
1083             outputScope.InvalidateAllPrefixes();
1084
1085             AttributeSet attSet;
1086             if (compiler.AttributeSets.TryGetValue(node.Name, out attSet)) {
1087                 Debug.Assert(attSet.Function != null, "All templates should be already compiled");
1088                 return invkGen.GenerateInvoke(attSet.Function, AddRemoveImplicitArgs(node.Content, attSet.Flags));
1089             } else {
1090                 if (! compiler.IsPhantomName(node.Name)) {
1091                     compiler.ReportError(/*[XT0710]*/node.SourceLine, Res.Xslt_NoAttributeSet, node.Name.QualifiedName);
1092                 }
1093                 return f.Sequence();
1094             }
1095         }
1096
1097         private const XmlNodeKindFlags InvalidatingNodes = (XmlNodeKindFlags.Attribute | XmlNodeKindFlags.Namespace);
1098
1099         private QilNode CompileCopy(XslNode copy) {
1100             QilNode node = GetCurrentNode();
1101             f.CheckNodeNotRtf(node);
1102             if ((node.XmlType.NodeKinds & InvalidatingNodes) != XmlNodeKindFlags.None) {
1103                 outputScope.InvalidateAllPrefixes();
1104             }
1105             if (node.XmlType.NodeKinds == XmlNodeKindFlags.Element) {
1106                 // Context node is always an element
1107                 // The namespace nodes of the current node are automatically copied
1108                 QilList content = InstructionList();
1109                 content.Add(f.XPathNamespace(node));
1110                 outputScope.PushScope();
1111                 outputScope.InvalidateAllPrefixes();
1112                 QilNode result = CompileInstructions(copy.Content, content);
1113                 outputScope.PopScope();
1114                 return f.ElementCtor(f.NameOf(node), result);
1115             } else if (node.XmlType.NodeKinds == XmlNodeKindFlags.Document) {
1116                 // Context node is always a document
1117                 // xsl:copy will not create a document node, but will just use the content template
1118                 return CompileInstructions(copy.Content);
1119             } else if ((node.XmlType.NodeKinds & (XmlNodeKindFlags.Element | XmlNodeKindFlags.Document)) == XmlNodeKindFlags.None) {
1120                 // Context node is neither an element, nor a document
1121                 // The content of xsl:copy is not instantiated
1122                 return node;
1123             } else {
1124                 // Static classifying of the context node is not possible
1125                 return f.XsltCopy(node, CompileInstructions(copy.Content));
1126             }
1127         }
1128
1129         private QilNode CompileCopyOf(XslNode node) {
1130             QilNode selectExpr = CompileXPathExpression(node.Select);
1131             if (selectExpr.XmlType.IsNode) {
1132                 if ((selectExpr.XmlType.NodeKinds & InvalidatingNodes) != XmlNodeKindFlags.None) {
1133                     outputScope.InvalidateAllPrefixes();
1134                 }
1135
1136                 if (selectExpr.XmlType.IsNotRtf && (selectExpr.XmlType.NodeKinds & XmlNodeKindFlags.Document) == XmlNodeKindFlags.None) {
1137                     // Expression returns non-document nodes only
1138                     return selectExpr;
1139                 }
1140
1141                 // May be an Rtf or may return Document nodes, so use XsltCopyOf operator
1142                 if (selectExpr.XmlType.IsSingleton) {
1143                     return f.XsltCopyOf(selectExpr);
1144                 } else {
1145                     QilIterator it;
1146                     return f.Loop(it = f.For(selectExpr), f.XsltCopyOf(it));
1147                 }
1148             }
1149             else if (selectExpr.XmlType.IsAtomicValue) {
1150                 // Expression returns non-nodes only
1151                 // When the result is neither a node-set nor a result tree fragment, the result is converted
1152                 // to a string and then inserted into the result tree, as with xsl:value-of.
1153                 return f.TextCtor(f.ConvertToString(selectExpr));
1154             }
1155             else {
1156                 // Static classifying is not possible
1157                 QilIterator it;
1158                 outputScope.InvalidateAllPrefixes();
1159                 return f.Loop(
1160                     it = f.For(selectExpr),
1161                     f.Conditional(f.IsType(it, T.Node),
1162                         f.XsltCopyOf(f.TypeAssert(it, T.Node)),
1163                         f.TextCtor(f.XsltConvert(it, T.StringX))
1164                     )
1165                 );
1166             }
1167         }
1168
1169         private QilNode CompileValueOf(XslNode valueOf) {
1170             return f.TextCtor(f.ConvertToString(CompileXPathExpression(/*select:*/valueOf.Select)));
1171         }
1172
1173         private QilNode CompileValueOfDoe(XslNode valueOf) {
1174             return f.RawTextCtor(f.ConvertToString(CompileXPathExpression(/*select:*/valueOf.Select)));
1175         }
1176
1177         private QilNode CompileWhen(XslNode whenNode, QilNode otherwise) {
1178             return f.Conditional(
1179                 f.ConvertToBoolean(CompileXPathExpression(/*test:*/whenNode.Select)),
1180                 CompileInstructions(whenNode.Content),
1181                 otherwise
1182             );
1183         }
1184
1185         private QilNode CompileIf(XslNode ifNode) {
1186             return CompileWhen(ifNode, InstructionList());
1187         }
1188
1189         private QilNode CompileChoose(XslNode node) {
1190             IList<XslNode> cases = node.Content;
1191             QilNode result = null;
1192
1193             // It's easier to compile xsl:choose from bottom to top
1194             for (int i = cases.Count - 1; 0 <= i; i--) {
1195                 XslNode when = cases[i];
1196                 Debug.Assert(when.NodeType == XslNodeType.If || when.NodeType == XslNodeType.Otherwise);
1197                 QilList nsList = EnterScope(when);
1198                 if (when.NodeType == XslNodeType.Otherwise) {
1199                     Debug.Assert(result == null, "xsl:otherwise must be the last child of xsl:choose");
1200                     result = CompileInstructions(when.Content);
1201                 } else {
1202                     result = CompileWhen(when, /*otherwise:*/result ?? InstructionList());
1203                 }
1204                 ExitScope();
1205                 SetLineInfoCheck(result, when.SourceLine);
1206                 result = SetDebugNs(result, nsList);
1207             }
1208             if (result == null) {
1209                 return f.Sequence();
1210             }
1211             return IsDebug ? f.Sequence(result) : result;
1212         }
1213
1214         private QilNode CompileMessage(XslNode node) {
1215             string  baseUri = lastScope.SourceLine.Uri;
1216             QilNode content = f.RtfCtor(CompileInstructions(node.Content), f.String(baseUri));
1217
1218             //content = f.ConvertToString(content);
1219             content = f.InvokeOuterXml(content);
1220
1221             // If terminate="no", then create QilNodeType.Warning
1222             if (!(bool)node.Arg) {
1223                 return f.Warning(content);
1224             }
1225
1226             // Otherwise create both QilNodeType.Warning and QilNodeType.Error
1227             QilIterator i;
1228             return f.Loop(i = f.Let(content), f.Sequence(f.Warning(i), f.Error(i)));
1229         }
1230
1231         private QilNode CompileVariable(XslNode node) {
1232             Debug.Assert(node.NodeType == XslNodeType.Variable);
1233             if (scope.IsLocalVariable(node.Name.LocalName, node.Name.NamespaceUri)) {
1234                 ReportError(/*[XT_030]*/Res.Xslt_DupLocalVariable, node.Name.QualifiedName);
1235             }
1236             return CompileVarParValue(node);
1237         }
1238
1239         private QilNode CompileVarParValue(XslNode node) {
1240             Debug.Assert(node.NodeType == XslNodeType.Variable || node.NodeType == XslNodeType.Param || node.NodeType == XslNodeType.WithParam);
1241             VerifyXPathQName(node.Name);
1242
1243             string          baseUri = lastScope.SourceLine.Uri;
1244             IList<XslNode>  content = node.Content;
1245             string          select  = node.Select;
1246             QilNode         varValue;
1247
1248             if (select != null) {
1249                 // In case of incorrect stylesheet, variable or parameter may have both a 'select' attribute and non-empty content
1250                 QilList list = InstructionList();
1251                 list.Add(CompileXPathExpression(select));
1252                 varValue = CompileInstructions(content, list);
1253             } else if (content.Count != 0) {
1254                 this.outputScope.PushScope();
1255                 // Rtf will be instantiated in an unknown namespace context, so we should not assume anything here
1256                 this.outputScope.InvalidateAllPrefixes();
1257                 varValue = f.RtfCtor(CompileInstructions(content), f.String(baseUri));
1258                 this.outputScope.PopScope();
1259             } else {
1260                 varValue = f.String(string.Empty);
1261             }
1262             if (IsDebug) {
1263                 // In debug mode every variable/param must be of type 'any'
1264                 varValue = f.TypeAssert(varValue, T.ItemS);
1265             }
1266             Debug.Assert(varValue.SourceLine == null);
1267             return varValue;
1268         }
1269
1270         private void CompileWithParam(VarPar withParam) {
1271             Debug.Assert(withParam.NodeType == XslNodeType.WithParam);
1272             QilList nsList = EnterScope(withParam);
1273             QilNode paramValue = CompileVarParValue(withParam);
1274             ExitScope();
1275             SetLineInfo(paramValue, withParam.SourceLine);
1276             paramValue = SetDebugNs(paramValue, nsList);
1277             withParam.Value = paramValue;
1278         }
1279
1280         // 
1281
1282         private QilNode CompileSorts(IList<XslNode> content, ref LoopFocus parentLoop) {
1283             QilList keyList = f.BaseFactory.SortKeyList();
1284
1285             int i = 0;
1286
1287             while (i < content.Count) {
1288                 Sort sort = content[i] as Sort;
1289                 if (sort != null) {
1290                     CompileSort(sort, keyList, ref parentLoop);
1291                     content.RemoveAt(i);
1292                 } else {
1293                     i++;
1294                 }
1295             }
1296
1297             if (keyList.Count == 0)
1298                 return null;
1299
1300             return keyList;
1301         }
1302
1303         private QilNode CompileLangAttribute(string attValue, bool fwdCompat) {
1304             QilNode result = CompileStringAvt(attValue);
1305
1306             if (result == null) {
1307                 // Do nothing
1308             } else if (result.NodeType == QilNodeType.LiteralString) {
1309                 string lang = (string)(QilLiteral)result;
1310                 int lcid = XsltLibrary.LangToLcidInternal(lang, fwdCompat, (IErrorHelper)this);
1311                 if (lcid == XsltLibrary.InvariantCultureLcid) {
1312                     result = null;
1313                 }
1314             } else {
1315                 // NOTE: We should have the same checks for both compile time and execution time
1316                 QilIterator i;
1317                 result = f.Loop(i = f.Let(result),
1318                     f.Conditional(f.Eq(f.InvokeLangToLcid(i, fwdCompat), f.Int32(XsltLibrary.InvariantCultureLcid)),
1319                         f.String(string.Empty),
1320                         i
1321                     )
1322                 );
1323             }
1324             return result;
1325         }
1326
1327         private QilNode CompileLangAttributeToLcid(string attValue, bool fwdCompat) {
1328             return CompileLangToLcid(CompileStringAvt(attValue), fwdCompat);
1329          }
1330
1331         private QilNode CompileLangToLcid(QilNode lang, bool fwdCompat) {
1332             if (lang == null) {
1333                 return f.Double(XsltLibrary.InvariantCultureLcid);
1334             } else if (lang.NodeType == QilNodeType.LiteralString) {
1335                 return f.Double(XsltLibrary.LangToLcidInternal((string)(QilLiteral)lang, fwdCompat, (IErrorHelper)this));
1336             } else {
1337                 return f.XsltConvert(f.InvokeLangToLcid(lang, fwdCompat), T.DoubleX);
1338             }
1339         }
1340
1341         private void CompileDataTypeAttribute(string attValue, bool fwdCompat, ref QilNode select, out QilNode select2) {
1342             const string DtText   = "text";
1343             const string DtNumber = "number";
1344             QilNode result = CompileStringAvt(attValue);
1345             if (result != null) {
1346                 if (result.NodeType == QilNodeType.LiteralString) {
1347                     string dataType = (string)(QilLiteral)result;
1348                     if (dataType == DtNumber) {
1349                         select  = f.ConvertToNumber(select);
1350                         select2 = null;
1351                         return;
1352                     } else if (dataType == DtText) {
1353                         // fall through to default case
1354                     } else {
1355                         if (!fwdCompat) {
1356                             // check for qname-but-not-ncname
1357                             string prefix, local, nsUri;
1358                             bool isValid = compiler.ParseQName(dataType, out prefix, out local, (IErrorHelper)this);
1359                             nsUri = isValid ? ResolvePrefix(/*ignoreDefaultNs:*/true, prefix) : compiler.CreatePhantomNamespace();
1360
1361                             if (nsUri.Length == 0) {
1362                                 // this is a ncname; we might report Res.Xslt_InvalidAttrValue,
1363                                 // but the following error message is more user friendly
1364                             }
1365                             ReportError(/*[XT_034]*/Res.Xslt_BistateAttribute, "data-type", DtText, DtNumber);
1366                         }
1367                         // fall through to default case
1368                     }
1369                 } else {
1370                     // Precalculate its value outside of for-each loop
1371                     QilIterator dt, qname;
1372
1373                     result = f.Loop(dt = f.Let(result),
1374                         f.Conditional(f.Eq(dt, f.String(DtNumber)), f.False(),
1375                         f.Conditional(f.Eq(dt, f.String(DtText)),   f.True(),
1376                         fwdCompat ? f.True() :
1377                         f.Loop(qname = f.Let(ResolveQNameDynamic(/*ignoreDefaultNs:*/true, dt)),
1378                             f.Error(lastScope.SourceLine,
1379                                 Res.Xslt_BistateAttribute, "data-type", DtText, DtNumber
1380                             )
1381                         )
1382                     )));
1383
1384                     QilIterator text = f.Let(result);
1385                     varHelper.AddVariable(text);
1386
1387                     // Make two sort keys since heterogenous sort keys are not allowed
1388                     select2 = select.DeepClone(f.BaseFactory);
1389                     select  = f.Conditional(text, f.ConvertToString(select), f.String(string.Empty)    );
1390                     select2 = f.Conditional(text, f.Double(0),               f.ConvertToNumber(select2));
1391                     return;
1392                 }
1393             }
1394
1395             // Default case
1396             select  = f.ConvertToString(select);
1397             select2 = null;
1398         }
1399
1400         /// <summary>
1401         /// Compiles AVT with two possible values
1402         /// </summary>
1403         /// <param name="attName"  >NodeCtor name (used for constructing an error message)</param>
1404         /// <param name="attValue" >NodeCtor value</param>
1405         /// <param name="value0"   >First possible value of attribute</param>
1406         /// <param name="value1"   >Second possible value of attribute</param>
1407         /// <param name="fwdCompat">If true, unrecognized value does not report an error</param>
1408         /// <returns>
1409         /// If AVT is null (i.e. the attribute is omitted), null is returned. Otherwise, QilExpression
1410         /// returning "1" if AVT evaluates to value1, or "0" if AVT evaluates to value0 or any other value.
1411         /// If AVT evaluates to neither value0 nor value1 and fwdCompat == false, an error is reported.
1412         /// </returns>
1413         private QilNode CompileOrderAttribute(string attName, string attValue, string value0, string value1, bool fwdCompat) {
1414             QilNode result = CompileStringAvt(attValue);
1415             if (result != null) {
1416                 if (result.NodeType == QilNodeType.LiteralString) {
1417                     string value = (string)(QilLiteral)result;
1418                     if (value == value1) {
1419                         result = f.String("1");
1420                     } else {
1421                         if (value != value0 && !fwdCompat) {
1422                             ReportError(/*[XT_034]*/Res.Xslt_BistateAttribute, attName, value0, value1);
1423                         }
1424                         result = f.String("0");
1425                     }
1426                 } else {
1427                     QilIterator i;
1428                     result = f.Loop(i = f.Let(result),
1429                         f.Conditional(f.Eq(i, f.String(value1)), f.String("1"),
1430                         fwdCompat ? f.String("0") :
1431                         f.Conditional(f.Eq(i, f.String(value0)), f.String("0"),
1432                         f.Error(lastScope.SourceLine,
1433                             Res.Xslt_BistateAttribute, attName, value0, value1
1434                         )
1435                     )));
1436                 }
1437                 Debug.Assert(result.XmlType == T.StringX);
1438             }
1439             return result;
1440         }
1441
1442         private void CompileSort(Sort sort, QilList keyList, ref LoopFocus parentLoop) {
1443             Debug.Assert(sort.NodeType == XslNodeType.Sort);
1444             QilNode select, select2, lang, order, caseOrder;
1445             bool    fwdCompat;
1446
1447             EnterScope(sort);
1448             fwdCompat = sort.ForwardsCompatible;
1449
1450             select = CompileXPathExpression(sort.Select);
1451
1452             if (sort.Lang != null || sort.DataType != null || sort.Order != null || sort.CaseOrder != null) {
1453                 // Calculate these attributes in the context of the parent loop
1454                 LoopFocus curLoopSaved = curLoop;
1455                 curLoop = parentLoop;
1456
1457                 lang = CompileLangAttribute(sort.Lang, fwdCompat);
1458
1459                 CompileDataTypeAttribute(sort.DataType, fwdCompat, ref select, out select2);
1460
1461                 order = CompileOrderAttribute(
1462                     /*attName:  */  "order",
1463                     /*attValue: */  sort.Order,
1464                     /*value0:   */  "ascending",
1465                     /*value1:   */  "descending",
1466                     /*fwdCompat:*/  fwdCompat
1467                 );
1468
1469                 caseOrder = CompileOrderAttribute(
1470                     /*attName:  */  "case-order",
1471                     /*attValue: */  sort.CaseOrder,
1472                     /*value0:   */  "lower-first",
1473                     /*value1:   */  "upper-first",
1474                     /*fwdCompat:*/  fwdCompat
1475                 );
1476
1477                 // Restore loop context
1478                 curLoop = curLoopSaved;
1479             } else {
1480                 select  = f.ConvertToString(select);
1481                 select2 = lang = order = caseOrder = null;
1482             }
1483
1484             strConcat.Reset();
1485             strConcat.Append(XmlReservedNs.NsCollationBase);
1486             strConcat.Append('/');
1487             strConcat.Append(lang);
1488
1489             char separator = '?';
1490             if (order != null) {
1491                 strConcat.Append(separator);
1492                 strConcat.Append("descendingOrder=");
1493                 strConcat.Append(order);
1494                 separator = '&';
1495             }
1496             if (caseOrder != null) {
1497                 strConcat.Append(separator);
1498                 strConcat.Append("upperFirst=");
1499                 strConcat.Append(caseOrder);
1500                 separator = '&';
1501             }
1502
1503             QilNode collation = strConcat.ToQil();
1504
1505             QilSortKey result = f.SortKey(select, collation);
1506             // Line information is not copied
1507             keyList.Add(result);
1508
1509             if (select2 != null) {
1510                 result = f.SortKey(select2, collation.DeepClone(f.BaseFactory));
1511                 // Line information is not copied
1512                 keyList.Add(result);
1513             }
1514
1515             ExitScope();
1516         }
1517
1518         private QilNode MatchPattern(QilNode pattern, QilIterator testNode) {
1519             QilList list;
1520             if (pattern.NodeType == QilNodeType.Error) {
1521                 // Invalid pattern
1522                 return pattern;
1523             } else if (pattern.NodeType == QilNodeType.Sequence) {
1524                 list = (QilList)pattern;
1525                 Debug.Assert(0 < list.Count, "Pattern should have at least one filter");
1526             } else {
1527                 list = f.BaseFactory.Sequence();
1528                 list.Add(pattern);
1529             }
1530
1531             QilNode result = f.False();
1532             for (int i = list.Count - 1; 0 <= i; i --) {
1533                 QilLoop filter = (QilLoop) list[i];
1534                 ptrnBuilder.AssertFilter(filter);
1535                 result = f.Or(
1536                     refReplacer.Replace(filter.Body, filter.Variable, testNode),
1537                     result
1538                 );
1539             }
1540             return result;
1541         }
1542
1543         private QilNode MatchCountPattern(QilNode countPattern, QilIterator testNode) {
1544             /*
1545                 If the 'count' attribute is not specified, then it defaults to the pattern that matches any node
1546                 with the same node kind as the context node and, if the context node has an expanded-QName, with
1547                 the same expanded-QName as the context node.
1548             */
1549             if (countPattern != null) {
1550                 return MatchPattern(countPattern, testNode);
1551             } else {
1552                 QilNode current = GetCurrentNode();
1553                 QilNode result;
1554                 XmlNodeKindFlags nodeKinds = current.XmlType.NodeKinds;
1555
1556                 // If node kind is not known, invoke a runtime function
1557                 if ((nodeKinds & (nodeKinds - 1)) != 0) {
1558                     return f.InvokeIsSameNodeSort(testNode, current);
1559                 }
1560
1561                 // Otherwise generate IsType check along with expanded QName check
1562                 switch (nodeKinds) {
1563                 case XmlNodeKindFlags.Document  :   return f.IsType(testNode, T.Document);
1564                 case XmlNodeKindFlags.Element   :   result = f.IsType(testNode, T.Element);   break;
1565                 case XmlNodeKindFlags.Attribute :   result = f.IsType(testNode, T.Attribute); break;
1566                 case XmlNodeKindFlags.Text      :   return f.IsType(testNode, T.Text);
1567                 case XmlNodeKindFlags.Comment   :   return f.IsType(testNode, T.Comment);
1568                 case XmlNodeKindFlags.PI        :   return f.And(f.IsType(testNode, T.PI)       , f.Eq(f.LocalNameOf(testNode), f.LocalNameOf(current)));
1569                 case XmlNodeKindFlags.Namespace :   return f.And(f.IsType(testNode, T.Namespace), f.Eq(f.LocalNameOf(testNode), f.LocalNameOf(current)));
1570                 default :
1571                     Debug.Fail("Unexpected NodeKind: " + nodeKinds.ToString());
1572                     return f.False();
1573                 }
1574
1575                 // Elements and attributes have both local name and namespace URI
1576                 return f.And(result, f.And(
1577                     f.Eq(f.LocalNameOf(testNode)   , f.LocalNameOf(current)),
1578                     f.Eq(f.NamespaceUriOf(testNode), f.NamespaceUriOf(GetCurrentNode()))
1579                 ));
1580             }
1581         }
1582
1583         private QilNode PlaceMarker(QilNode countPattern, QilNode fromPattern, bool multiple) {
1584             /*
1585                 Quotation from XSLT 2.0 spec:
1586                 * Let $A be the node sequence selected by the expression
1587                     ancestor-or-self::node()[matches-count(.)]          (level = "multiple")
1588                     ancestor-or-self::node()[matches-count(.)][1]       (level = "single")
1589                 * Let $F be the node sequence selected by the expression
1590                     ancestor-or-self::node()[matches-from(.)][1]
1591                 * Let $AF be the value of
1592                     $A intersect ($F/descendant-or-self::node())
1593                 * Return the result of the expression
1594                     for $af in $AF return 1+count($af/preceding-sibling::node()[matches-count(.)])
1595
1596                 NOTE: There are some distinctions between XSLT 1.0 and XSLT 2.0 specs. In our 1.0 implementation we:
1597                 1) Assume that the 'matches-from()' function does not match root nodes by default.
1598                 2) Instead of '$A intersect ($F/descendant-or-self::node())' (which, by the way,
1599                    would filter out attribute and namespace nodes from $A) we calculate
1600                      '$A'           if the 'from' attribute is omitted,
1601                      '$A[. >> $F]'  if the 'from' attribute is present.
1602             */
1603
1604             QilNode     countPattern2, countMatches, fromMatches, A, F, AF;
1605             QilIterator i, j;
1606
1607             countPattern2 = (countPattern != null) ? countPattern.DeepClone(f.BaseFactory) : null;
1608             countMatches = f.Filter(i = f.For(f.AncestorOrSelf(GetCurrentNode())), MatchCountPattern(countPattern, i));
1609             if (multiple) {
1610                 A = f.DocOrderDistinct(countMatches);
1611             } else {
1612                 A = f.Filter(i = f.For(countMatches), f.Eq(f.PositionOf(i), f.Int32(1)));
1613             }
1614
1615             if (fromPattern == null) {
1616                 AF = A;
1617             } else {
1618                 fromMatches = f.Filter(i = f.For(f.AncestorOrSelf(GetCurrentNode())), MatchPattern(fromPattern, i));
1619                 F = f.Filter(i = f.For(fromMatches), f.Eq(f.PositionOf(i), f.Int32(1)));
1620                 AF = f.Loop(i = f.For(F), f.Filter(j = f.For(A), f.Before(i, j)));
1621             }
1622
1623             return f.Loop(j = f.For(AF),
1624                 f.Add(f.Int32(1), f.Length(f.Filter(i = f.For(f.PrecedingSibling(j)), MatchCountPattern(countPattern2, i))))
1625             );
1626         }
1627
1628         private QilNode PlaceMarkerAny(QilNode countPattern, QilNode fromPattern) {
1629             /*
1630                 Quotation from XSLT 2.0 spec:
1631                 * If the context node is a document node, return the empty sequence, ()
1632                 * Let $A be the node sequence selected by the expression
1633                     (preceding::node()|ancestor-or-self::node())[matches-count(.)]
1634                 * Let $F be the node sequence selected by the expression
1635                     (preceding::node()|ancestor::node())[matches-from(.)][last()]
1636                 * Let $AF be the node sequence $A[. is $F or . >> $F].
1637                 * If $AF is empty, return the empty sequence, ()
1638                 * Otherwise return the value of the expression count($AF)
1639
1640                 NOTE: There are some distinctions between XSLT 1.0 and XSLT 2.0 specs. In our 1.0 implementation we:
1641                 1) Assume that the 'matches-from()' function does not match root nodes by default.
1642                 2) Instead of '$A[. is $F or . >> $F]' we calculate
1643                      '$A'           if the 'from' attribute is omitted,
1644                      '$A[. >> $F]'  if the 'from' attribute is present.
1645             */
1646
1647             QilNode     range, fromMatches, F, AF;
1648             QilIterator i, j, k;
1649
1650             if (fromPattern == null) {
1651                 // According to XSLT 2.0 spec, if the 'from' attribute is omitted, matches-from() returns true
1652                 // only for the root node. It means $F is a sequence of length one containing the root node,
1653                 // and $AF = $A. XSLT 1.0 spec rules lead to the same result $AF = $A, so two specs are compliant here.
1654                 range = f.NodeRange(f.Root(GetCurrentNode()), GetCurrentNode());
1655                 AF = f.Filter(i = f.For(range), MatchCountPattern(countPattern, i));
1656             } else {
1657                 fromMatches = f.Filter(i = f.For(f.Preceding(GetCurrentNode())), MatchPattern(fromPattern, i));
1658                 F = f.Filter(i = f.For(fromMatches), f.Eq(f.PositionOf(i), f.Int32(1)));
1659                 AF = f.Loop(i = f.For(F),
1660                     f.Filter(j = f.For(f.Filter(k = f.For(f.NodeRange(i, GetCurrentNode())), MatchCountPattern(countPattern, k))),
1661                         f.Not(f.Is(i, j))
1662                     )
1663                 );
1664             }
1665
1666             return f.Loop(k = f.Let(f.Length(AF)),
1667                 f.Conditional(f.Eq(k, f.Int32(0)), f.Sequence(),
1668                 k
1669             ));
1670         }
1671
1672         // Returns one of XsltLibrary.LetterValue enum values
1673         private QilNode CompileLetterValueAttribute(string attValue, bool fwdCompat) {
1674             const string Default     = "default";
1675             const string Alphabetic  = "alphabetic";
1676             const string Traditional = "traditional";
1677
1678             string letterValue;
1679             QilNode result = CompileStringAvt(attValue);
1680
1681             if (result != null) {
1682                 if (result.NodeType == QilNodeType.LiteralString) {
1683                     letterValue = (string)(QilLiteral)result;
1684                     if (letterValue != Alphabetic && letterValue != Traditional) {
1685                         if (!fwdCompat) {
1686                             ReportError(/*[XT_034]*/Res.Xslt_BistateAttribute, "letter-value", Alphabetic, Traditional);
1687                         }
1688                         else {
1689                             // Use default value
1690                             return f.String(Default);
1691                         }
1692                     }
1693                     return result;
1694                 } else {
1695                     QilIterator i = f.Let(result);
1696                     return f.Loop(i,
1697                         f.Conditional(
1698                             f.Or(f.Eq(i, f.String(Alphabetic)), f.Eq(i, f.String(Traditional))),
1699                             i,
1700                             fwdCompat ? f.String(Default) :
1701                             f.Error(lastScope.SourceLine, Res.Xslt_BistateAttribute, "letter-value", Alphabetic, Traditional)
1702                     ));
1703                 }
1704             }
1705             return f.String(Default);
1706         }
1707
1708         private QilNode CompileGroupingSeparatorAttribute(string attValue, bool fwdCompat) {
1709             QilNode result = CompileStringAvt(attValue);
1710
1711             if (result == null) {
1712                 // NOTE: string.Empty value denotes unspecified attribute
1713                 result = f.String(string.Empty);
1714             } else if (result.NodeType == QilNodeType.LiteralString) {
1715                 string value = (string)(QilLiteral)result;
1716                 if (value.Length != 1) {
1717                     if (!fwdCompat) {
1718                         ReportError(/*[XT_035]*/Res.Xslt_CharAttribute, "grouping-separator");
1719                     }
1720                     // See the comment above
1721                     result = f.String(string.Empty);
1722                 }
1723             } else {
1724                 QilIterator i = f.Let(result);
1725                 result = f.Loop(i,
1726                     f.Conditional(f.Eq(f.StrLength(i), f.Int32(1)), i,
1727                     fwdCompat ? f.String(string.Empty) :
1728                     f.Error(lastScope.SourceLine, Res.Xslt_CharAttribute, "grouping-separator")
1729                 ));
1730             }
1731             return result;
1732         }
1733
1734         private QilNode CompileGroupingSizeAttribute(string attValue, bool fwdCompat) {
1735             QilNode result = CompileStringAvt(attValue);
1736
1737             if (result == null) {
1738                 return f.Double(0);
1739             } else if (result.NodeType == QilNodeType.LiteralString) {
1740                 string groupingSize = (string)(QilLiteral)result;
1741
1742                 // NOTE: It is unclear from the spec what we should do with float numbers here.
1743                 // Let's apply XPath number and round functions as usual, suppressing any conversion errors.
1744                 double dblGroupingSize = XsltFunctions.Round(XPathConvert.StringToDouble(groupingSize));
1745                 if (0 <= dblGroupingSize && dblGroupingSize <= int.MaxValue) {
1746                     return f.Double(dblGroupingSize);
1747                 }
1748                 // NaN goes here as well
1749                 return f.Double(0);
1750             } else {
1751                 // NOTE: We should have the same checks for both compile time and execution time
1752                 QilIterator i = f.Let(f.ConvertToNumber(result));
1753                 return f.Loop(i,
1754                     f.Conditional(f.And(f.Lt(f.Double(0), i), f.Lt(i, f.Double(int.MaxValue))),
1755                         i,
1756                         f.Double(0)
1757                     )
1758                 );
1759             }
1760         }
1761
1762         private QilNode CompileNumber(Number num) {
1763             QilNode value;
1764
1765             if (num.Value != null) {
1766                 // 
1767
1768                 value = f.ConvertToNumber(CompileXPathExpression(num.Value));
1769             } else {
1770                 QilNode countPattern = (num.Count != null) ? CompileNumberPattern(num.Count) : null;
1771                 QilNode fromPattern  = (num.From  != null) ? CompileNumberPattern(num.From ) : null;
1772
1773                 switch (num.Level) {
1774                 case NumberLevel.Single   : value = PlaceMarker(countPattern, fromPattern, false); break;
1775                 case NumberLevel.Multiple : value = PlaceMarker(countPattern, fromPattern, true ); break;
1776                 default:
1777                     Debug.Assert(num.Level == NumberLevel.Any);
1778                     value = PlaceMarkerAny(countPattern, fromPattern);
1779                     break;
1780                 }
1781             }
1782
1783             bool fwdCompat = num.ForwardsCompatible;
1784             return f.TextCtor(f.InvokeNumberFormat(
1785                 value, CompileStringAvt(num.Format),
1786                 CompileLangAttributeToLcid       (num.Lang,              fwdCompat),
1787                 CompileLetterValueAttribute      (num.LetterValue,       fwdCompat),
1788                 CompileGroupingSeparatorAttribute(num.GroupingSeparator, fwdCompat),
1789                 CompileGroupingSizeAttribute     (num.GroupingSize,      fwdCompat)
1790             ));
1791         }
1792
1793         // ------------- CompileAndSortMatchPatterns() -------------
1794
1795         private void CompileAndSortMatches(Stylesheet sheet) {
1796             Debug.Assert(sheet.TemplateMatches.Count == 0);
1797
1798             foreach (Template template in sheet.Templates) {
1799                 if (template.Match != null) {
1800                     EnterScope(template);
1801                     QilNode result = CompileMatchPattern(template.Match);
1802                     if (result.NodeType == QilNodeType.Sequence) {
1803                         QilList filters = (QilList)result;
1804                         for (int idx = 0; idx < filters.Count; idx++) {
1805                             sheet.AddTemplateMatch(template, (QilLoop)filters[idx]);
1806                         }
1807                     } else {
1808                         sheet.AddTemplateMatch(template, (QilLoop)result);
1809                     }
1810                     ExitScope();
1811                 }
1812             }
1813
1814             sheet.SortTemplateMatches();
1815
1816             foreach (Stylesheet import in sheet.Imports) {
1817                 CompileAndSortMatches(import);
1818             }
1819         }
1820
1821         // ------------- CompileKeys() -------------
1822
1823         private void CompileKeys() {
1824             CheckSingletonFocus();
1825             for (int idx = 0; idx < compiler.Keys.Count; idx++) {
1826                 foreach (Key key in compiler.Keys[idx]) {
1827                     EnterScope(key);
1828                     QilParameter context = f.Parameter(T.NodeNotRtf);
1829                     singlFocus.SetFocus(context);
1830                     QilIterator values  = f.For(f.OptimizeBarrier(CompileKeyMatch(key.Match)));
1831                     singlFocus.SetFocus(values);
1832                     QilIterator keys = f.For(CompileKeyUse(key));
1833                     keys = f.For(f.OptimizeBarrier(f.Loop(keys, f.ConvertToString(keys))));
1834
1835                     QilParameter value = f.Parameter(T.StringX);
1836
1837                     QilFunction func = f.Function(f.FormalParameterList(context, value),
1838                         f.Filter(values,
1839                             f.Not(f.IsEmpty(f.Filter(keys, f.Eq(keys, value))))
1840                         ),
1841                         f.False()
1842                     );
1843
1844                     func.DebugName = key.GetDebugName();
1845                     SetLineInfo(func, key.SourceLine);
1846                     key.Function = func;
1847                     this.functions.Add(func);
1848                     ExitScope();
1849                 }
1850             }
1851             singlFocus.SetFocus(null);
1852         }
1853
1854         // ---------------------- Global variables and parameters -----------------------
1855
1856         private void CreateGlobalVarPars() {
1857             foreach (VarPar par in compiler.ExternalPars) {
1858                 CreateGlobalVarPar(par);
1859             }
1860             foreach (VarPar var in compiler.GlobalVars) {
1861                 CreateGlobalVarPar(var);
1862             }
1863         }
1864
1865         private void CreateGlobalVarPar(VarPar varPar) {
1866             Debug.Assert(varPar.NodeType == XslNodeType.Variable || varPar.NodeType == XslNodeType.Param);
1867             XmlQueryType xt = ChooseBestType(varPar);
1868             QilIterator it;
1869             if (varPar.NodeType == XslNodeType.Variable) {
1870                 it = f.Let(f.Unknown(xt));
1871             } else {
1872                 it = f.Parameter(null, varPar.Name, xt);
1873             }
1874             it.DebugName = varPar.Name.ToString();
1875             varPar.Value = it;
1876             SetLineInfo(it, varPar.SourceLine);
1877             scope.AddVariable(varPar.Name, it);
1878         }
1879
1880         private void CompileGlobalVariables() {
1881             CheckSingletonFocus();
1882             singlFocus.SetFocus(SingletonFocusType.InitialDocumentNode);
1883
1884             foreach (VarPar par in compiler.ExternalPars) {
1885                 extPars.Add(CompileGlobalVarPar(par));
1886             }
1887             foreach (VarPar var in compiler.GlobalVars) {
1888                 gloVars.Add(CompileGlobalVarPar(var));
1889             }
1890
1891             singlFocus.SetFocus(null);
1892         }
1893
1894         private QilIterator CompileGlobalVarPar(VarPar varPar) {
1895             Debug.Assert(varPar.NodeType == XslNodeType.Variable || varPar.NodeType == XslNodeType.Param);
1896             QilIterator it = (QilIterator)varPar.Value;
1897
1898             QilList nsList = EnterScope(varPar);
1899             QilNode content = CompileVarParValue(varPar);
1900             SetLineInfo(content, it.SourceLine);
1901             content = AddCurrentPositionLast(content);
1902             content = SetDebugNs(content, nsList);
1903             it.SourceLine = SourceLineInfo.NoSource;
1904             it.Binding = content;
1905             ExitScope();
1906             return it;
1907         }
1908
1909         // ------------- CompileXPathExpression() / CompileMatchPattern() / CompileKeyPattern() -----------
1910
1911         private void ReportErrorInXPath(XslLoadException e) {
1912             XPathCompileException ex = e as XPathCompileException;
1913             string errorText = (ex != null) ? ex.FormatDetailedMessage() : e.Message;
1914             compiler.ReportError(lastScope.SourceLine, Res.Xml_UserException, errorText);
1915         }
1916
1917         private QilNode PhantomXPathExpression() {
1918             return f.TypeAssert(f.Sequence(), T.ItemS);
1919         }
1920
1921         private QilNode PhantomKeyMatch() {
1922             return f.TypeAssert(f.Sequence(), T.NodeNotRtfS);
1923         }
1924
1925         // Calls to CompileXPathExpression() can't be nested in the XSLT. So we can reuse the same instance of xpathBuilder.
1926         // The only thing we need to do before its use is adjustment of IXPathEnvironment to have correct context tuple.
1927         private QilNode CompileXPathExpression(string expr) {
1928             XPathScanner    scanner;
1929             QilNode         result;
1930
1931             SetEnvironmentFlags(/*allowVariables:*/true, /*allowCurrent:*/true, /*allowKey:*/true);
1932             if (expr == null) {
1933                 result = PhantomXPathExpression();
1934             } else {
1935                 try {
1936                     // Note that the constructor may throw an exception, for example, in case of the expression "'"
1937                     scanner = new XPathScanner(expr);
1938                     result = xpathParser.Parse(scanner, xpathBuilder, LexKind.Eof);
1939                 } catch (XslLoadException e) {
1940                     if (xslVersion != XslVersion.ForwardsCompatible) {
1941                         ReportErrorInXPath(/*[XT0300]*/e);
1942                     }
1943                     result = f.Error(f.String(e.Message));
1944                 }
1945             }
1946             if (result is QilIterator) {
1947                 result = f.Nop(result);
1948             }
1949             return result;
1950         }
1951
1952         private QilNode CompileNodeSetExpression(string expr) {
1953             QilNode result = f.TryEnsureNodeSet(CompileXPathExpression(expr));
1954             if (result == null) {
1955                 // The expression is never a node-set
1956                 XPathCompileException e = new XPathCompileException(expr, 0, expr.Length, Res.XPath_NodeSetExpected, null);
1957                 if (xslVersion != XslVersion.ForwardsCompatible) {
1958                     ReportErrorInXPath(/*[XTTE_101]*/e);
1959                 }
1960                 result = f.Error(f.String(e.Message));
1961             }
1962             return result;
1963         }
1964
1965         private QilNode CompileXPathExpressionWithinAvt(string expr, ref int pos) {
1966             Debug.Assert(expr != null);
1967             XPathScanner    scanner;
1968             QilNode         result;
1969             int             startPos = pos;
1970
1971             SetEnvironmentFlags(/*allowVariables:*/true, /*allowCurrent:*/true, /*allowKey:*/true);
1972             try {
1973                 scanner = new XPathScanner(expr, pos);
1974                 result = xpathParser.Parse(scanner, xpathBuilder, LexKind.RBrace);
1975                 pos = scanner.LexStart + 1;
1976             } catch (XslLoadException e) {
1977                 if (xslVersion != XslVersion.ForwardsCompatible) {
1978                     ReportErrorInXPath(/*[XT0350][XT0360]*/e);
1979                 }
1980                 result = f.Error(f.String(e.Message));
1981                 pos = expr.Length;
1982             }
1983             if (result is QilIterator) {
1984                 result = f.Nop(result);
1985             }
1986             return result;
1987         }
1988
1989         private QilNode CompileMatchPattern(string pttrn) {
1990             Debug.Assert(pttrn != null);
1991             XPathScanner    scanner;
1992             QilNode         result;
1993
1994             SetEnvironmentFlags(/*allowVariables:*/false, /*allowCurrent:*/false, /*allowKey:*/true);
1995             try {
1996                 scanner = new XPathScanner(pttrn);
1997                 result = ptrnParser.Parse(scanner, ptrnBuilder);
1998             } catch (XslLoadException e) {
1999                 if (xslVersion != XslVersion.ForwardsCompatible) {
2000                     ReportErrorInXPath(/*[XT0340]*/e);
2001                 }
2002                 result = f.Loop(f.For(ptrnBuilder.FixupNode),
2003                     f.Error(f.String(e.Message))
2004                 );
2005                 XPathPatternBuilder.SetPriority(result, 0.5);
2006             }
2007             return result;
2008         }
2009
2010         private QilNode CompileNumberPattern(string pttrn) {
2011             Debug.Assert(pttrn != null);
2012             XPathScanner    scanner;
2013             QilNode         result;
2014
2015             SetEnvironmentFlags(/*allowVariables:*/true, /*allowCurrent:*/false, /*allowKey:*/true);
2016             try {
2017                 scanner = new XPathScanner(pttrn);
2018                 result = ptrnParser.Parse(scanner, ptrnBuilder);
2019             } catch (XslLoadException e) {
2020                 if (xslVersion != XslVersion.ForwardsCompatible) {
2021                     ReportErrorInXPath(/*[XT0340]*/e);
2022                 }
2023                 result = f.Error(f.String(e.Message));
2024             }
2025             return result;
2026         }
2027
2028         private QilNode CompileKeyMatch(string pttrn) {
2029             XPathScanner    scanner;
2030             QilNode         result;
2031
2032             if (keyMatchBuilder == null) {
2033                 keyMatchBuilder = new KeyMatchBuilder((IXPathEnvironment) this);
2034             }
2035             SetEnvironmentFlags(/*allowVariables:*/false, /*allowCurrent:*/false, /*allowKey:*/false);
2036             if (pttrn == null) {
2037                 result = PhantomKeyMatch();
2038             } else {
2039                 try {
2040                     scanner = new XPathScanner(pttrn);
2041                     result = ptrnParser.Parse(scanner, keyMatchBuilder);
2042                 } catch (XslLoadException e) {
2043                     if (xslVersion != XslVersion.ForwardsCompatible) {
2044                         ReportErrorInXPath(/*[XT0340]*/e);
2045                     }
2046                     result = f.Error(f.String(e.Message));
2047                 }
2048             }
2049             return result;
2050         }
2051
2052         private QilNode CompileKeyUse(Key key) {
2053             string          expr = key.Use;
2054             XPathScanner    scanner;
2055             QilNode         result;
2056
2057             SetEnvironmentFlags(/*allowVariables:*/false, /*allowCurrent:*/true, /*allowKey:*/false);
2058             if (expr == null) {
2059                 result = f.Error(f.String(XslLoadException.CreateMessage(key.SourceLine, Res.Xslt_MissingAttribute, "use")));
2060             } else {
2061                 try {
2062                     scanner = new XPathScanner(expr);
2063                     result = xpathParser.Parse(scanner, xpathBuilder, LexKind.Eof);
2064                 } catch (XslLoadException e) {
2065                     if (xslVersion != XslVersion.ForwardsCompatible) {
2066                         ReportErrorInXPath(/*[XT0300]*/e);
2067                     }
2068                     result = f.Error(f.String(e.Message));
2069                 }
2070             }
2071             if (result is QilIterator) {
2072                 result = f.Nop(result);
2073             }
2074             return result;
2075         }
2076
2077         private QilNode ResolveQNameDynamic(bool ignoreDefaultNs, QilNode qilName) {
2078             f.CheckString(qilName);
2079             QilList nsDecls = f.BaseFactory.Sequence();
2080             if (ignoreDefaultNs) {
2081                 nsDecls.Add(f.NamespaceDecl(f.String(string.Empty), f.String(string.Empty)));
2082             }
2083             foreach (ScopeRecord rec in this.scope) {
2084                 string recPrefix = rec.ncName;
2085                 string recNsUri  = rec.nsUri;
2086
2087                 if (ignoreDefaultNs && recPrefix.Length == 0) {
2088                     // Do not take into account the default namespace
2089                 } else {
2090                     nsDecls.Add(f.NamespaceDecl(f.String(recPrefix), f.String(recNsUri)));
2091                 }
2092             }
2093             return f.StrParseQName(qilName, nsDecls);
2094         }
2095
2096         // ----------------- apply-templates, apply-imports ----------------------------- //
2097
2098         private QilNode GenerateApply(StylesheetLevel sheet, XslNode node) {
2099             Debug.Assert(
2100                 node.NodeType == XslNodeType.ApplyTemplates && sheet is RootLevel ||
2101                 node.NodeType == XslNodeType.ApplyImports   && sheet is Stylesheet
2102             );
2103
2104             if (compiler.Settings.CheckOnly) {
2105                 return f.Sequence();
2106             }
2107             return InvokeApplyFunction(sheet, /*mode:*/node.Name, node.Content);
2108         }
2109
2110         private void SetArg(IList<XslNode> args, int pos, QilName name, QilNode value) {
2111             VarPar varPar;
2112             if (args.Count <= pos || args[pos].Name != name) {
2113                 varPar = AstFactory.WithParam(name);
2114                 args.Insert(pos, varPar); 
2115             } else {
2116                 varPar = (VarPar) args[pos];
2117             }
2118             varPar.Value = value;
2119         }
2120         private IList<XslNode> AddRemoveImplicitArgs(IList<XslNode> args, XslFlags flags) {
2121             //We currently don't reuse the same argument list. So remove is not needed and will not work in this code
2122             if (IsDebug) {
2123                 flags = XslFlags.FullFocus;
2124             }
2125             if ((flags & XslFlags.FocusFilter) != 0) {
2126                 if (args == null || args.IsReadOnly) {
2127                     args = new List<XslNode>(3);
2128                 }
2129                 int pos = 0;
2130                 if ((flags & XslFlags.Current ) != 0) { SetArg(args, pos ++, nameCurrent , GetCurrentNode    ()); } 
2131                 if ((flags & XslFlags.Position) != 0) { SetArg(args, pos ++, namePosition, GetCurrentPosition()); } 
2132                 if ((flags & XslFlags.Last    ) != 0) { SetArg(args, pos ++, nameLast    , GetLastPosition   ()); } 
2133             }
2134             return args;
2135         }
2136
2137         // Fills invokeArgs with values from actualArgs in order given by formalArgs
2138         // Returns true if formalArgs maps 1:1 with actual args.
2139         // Formaly this is n*n algorithm. We can optimize it by calculationg "signature"
2140         // of the function as sum of all hashes of its args names.
2141         private bool FillupInvokeArgs(IList<QilNode> formalArgs, IList<XslNode> actualArgs, QilList invokeArgs) {
2142             if (actualArgs.Count != formalArgs.Count) {
2143                 return false;
2144             }
2145             invokeArgs.Clear();
2146             for (int invArg = 0; invArg < formalArgs.Count; invArg++) {
2147                 QilName formalArgName = ((QilParameter)formalArgs[invArg]).Name;
2148                 XmlQueryType paramType = formalArgs[invArg].XmlType;
2149                 QilNode arg = null; {
2150                     for (int actArg = 0; actArg < actualArgs.Count; actArg++) {
2151                         Debug.Assert(actualArgs[actArg].NodeType == XslNodeType.WithParam, "All Sorts was removed in CompileSorts()");
2152                         VarPar withParam = (VarPar)actualArgs[actArg];
2153                         if (formalArgName.Equals(withParam.Name)) {
2154                             QilNode value = withParam.Value;
2155                             XmlQueryType valueType = value.XmlType;
2156                             if (valueType != paramType) {
2157                                 if (valueType.IsNode && paramType.IsNode && valueType.IsSubtypeOf(paramType)) {
2158                                     // We can pass it
2159                                 } else {
2160                                     // Formal argument has the same name but a different type
2161                                     return false;
2162                                 }
2163                             }
2164                             arg = value;
2165                             break;
2166                         }
2167                     }
2168                 }
2169                 if (arg == null) {
2170                     // Formal argument has not been found among actual arguments
2171                     return false;
2172                 }
2173                 invokeArgs.Add(arg);
2174             }
2175             // All arguments have been found
2176             return true;
2177         }
2178
2179         private QilNode InvokeApplyFunction(StylesheetLevel sheet, QilName mode, IList<XslNode> actualArgs) {
2180             // Here we create function that has one argument for each with-param in apply-templates
2181             // We have actualArgs -- list of xsl:with-param(name, value)
2182             // From it we create:
2183             // invokeArgs -- values to use with QilInvoke
2184             // formalArgs -- list of iterators to use with QilFunction
2185             // actualArgs -- modify it to hold iterators (formalArgs) instead of values to ise in invoke generator inside function budy
2186
2187             XslFlags flags; { 
2188                 if (! sheet.ModeFlags.TryGetValue(mode, out flags)) {
2189                     flags = 0;
2190                 }
2191                 flags |= XslFlags.Current; // Due to recursive nature of Apply(Templates/Imports) we will need current node any way
2192             }
2193             actualArgs = AddRemoveImplicitArgs(actualArgs, flags);
2194
2195             QilList     invokeArgs = f.ActualParameterList();
2196             QilFunction applyFunction = null;
2197
2198             // Look at the list of all functions that have been already built.  If a suitable one is found, reuse it.
2199             List<QilFunction> functionsForMode;
2200             if (!sheet.ApplyFunctions.TryGetValue(mode, out functionsForMode)) {
2201                 functionsForMode = sheet.ApplyFunctions[mode] = new List<QilFunction>();
2202             }
2203
2204             foreach (QilFunction func in functionsForMode) {
2205                 if (FillupInvokeArgs(func.Arguments, actualArgs, /*ref*/invokeArgs)) {
2206                     applyFunction = func;
2207                     break;
2208                 }
2209             }
2210
2211             // If a suitable function has not been found, create it
2212             if (applyFunction == null) {
2213                 invokeArgs.Clear();
2214                 // We wasn't able to find suitable function. Let's build new:
2215                 // 1. Function arguments
2216                 QilList formalArgs = f.FormalParameterList();
2217                 for (int i = 0; i < actualArgs.Count; i++) {
2218                     Debug.Assert(actualArgs[i].NodeType == XslNodeType.WithParam, "All Sorts was removed in CompileSorts()");
2219                     VarPar withParam = (VarPar)actualArgs[i] ;
2220
2221                     // Add actual arg to 'invokeArgs' array. No need to clone it since it must be
2222                     // a literal or a reference.
2223                     invokeArgs.Add(withParam.Value);
2224
2225                     // Create correspondent formal arg
2226                     QilParameter formalArg = f.Parameter(i == 0 ? T.NodeNotRtf : withParam.Value.XmlType);
2227                     formalArg.Name = CloneName(withParam.Name);
2228                     formalArgs.Add(formalArg);
2229
2230                     // Change actual arg value to formalArg for reuse in calling built-in templates rules
2231                     withParam.Value = formalArg;
2232                 }
2233
2234                 // 2. Function header
2235                 applyFunction = f.Function(formalArgs,
2236                     f.Boolean((flags & XslFlags.SideEffects) != 0),
2237                     T.NodeNotRtfS
2238                 );
2239                 string attMode = (mode.LocalName.Length == 0) ? string.Empty : " mode=\"" + mode.QualifiedName + '"';
2240                 applyFunction.DebugName = (sheet is RootLevel ? "<xsl:apply-templates" : "<xsl:apply-imports") + attMode + '>';
2241                 functionsForMode.Add(applyFunction);
2242                 this.functions.Add(applyFunction);
2243
2244                 // 3. Function body
2245                 Debug.Assert(actualArgs[0].Name == nameCurrent, "Caller should always pass $current as a first argument to apply-* calls.");
2246                 QilIterator current = (QilIterator)formalArgs[0];
2247
2248                 // 3.1 Built-in templates:
2249                 // 3.1.1 loop over content of current element
2250                 QilLoop loopOnContent; {
2251                     QilIterator iChild = f.For(f.Content(current));
2252                     QilNode filter = f.Filter(iChild, f.IsType(iChild, T.Content));
2253                     filter.XmlType = T.ContentS;    // not attribute
2254
2255                     LoopFocus curLoopSaved = curLoop;
2256                     curLoop.SetFocus(f.For(filter));
2257
2258                     /* Prepare actual arguments */
2259                     // At XSLT 1.0, if a built-in template rule is invoked with parameters, the parameters are not
2260                     // passed on to any templates invoked by the built-in rule. At XSLT 2.0, these parameters are
2261                     // passed through the built-in template rule unchanged.
2262
2263                     // we can't just modify current/position/last of actualArgs in XSLT 2.0 as we tried before, 
2264                     // becuase flags for apply-import amy now be different then flags for apply-templates, so 
2265                     // we may need to add some space for additional position/last arguments
2266                     QilNode body = InvokeApplyFunction(compiler.Root, mode, /*actualArgs:*/null);
2267                     if (IsDebug) {
2268                         body = f.Sequence(InvokeOnCurrentNodeChanged(), body);
2269                     }
2270                     loopOnContent = curLoop.ConstructLoop(body);
2271                     curLoop = curLoopSaved;
2272                 }
2273
2274                 // 3.1.2 switch on type of current node
2275                 QilTernary builtinTemplates = f.BaseFactory.Conditional(f.IsType(current, elementOrDocumentType),                    
2276                     loopOnContent,
2277                     f.Conditional(f.IsType(current, textOrAttributeType),
2278                         f.TextCtor(f.XPathNodeValue(current)),
2279                         f.Sequence()
2280                     )
2281                 );
2282
2283                 // 3.2 Stylesheet templates
2284                 matcherBuilder.CollectPatterns(sheet, mode);
2285                 applyFunction.Definition = matcherBuilder.BuildMatcher(current, actualArgs, /*otherwise:*/builtinTemplates);
2286             }
2287             return f.Invoke(applyFunction, invokeArgs);
2288         }
2289
2290         // -------------------------------- IErrorHelper --------------------------------
2291
2292         public void ReportError(string res, params string[] args) {
2293             compiler.ReportError(lastScope.SourceLine, res, args);
2294         }
2295
2296         public void ReportWarning(string res, params string[] args) {
2297             compiler.ReportWarning(lastScope.SourceLine, res, args);
2298         }
2299
2300         // ------------------------------------------------------------------------------
2301
2302         [Conditional("DEBUG")]
2303         private void VerifyXPathQName(QilName qname) {
2304             Debug.Assert(
2305                 compiler.IsPhantomName(qname) ||
2306                 qname.NamespaceUri == ResolvePrefix(/*ignoreDefaultNs:*/true, qname.Prefix),
2307                 "QilGenerator must resolve the prefix to the same namespace as XsltLoader"
2308             );
2309         }
2310
2311         private string ResolvePrefix(bool ignoreDefaultNs, string prefix) {
2312             if (ignoreDefaultNs && prefix.Length == 0) {
2313                 return string.Empty;
2314             } else {
2315                 string ns = scope.LookupNamespace(prefix);
2316                 if (ns == null) {
2317                     if (prefix.Length == 0) {
2318                         ns = string.Empty;
2319                     } else {
2320                         ReportError(/*[XT0280]*/Res.Xslt_InvalidPrefix, prefix);
2321                         ns = compiler.CreatePhantomNamespace();
2322                     }
2323                 }
2324                 return ns;
2325             }
2326         }
2327
2328         private void SetLineInfoCheck(QilNode n, ISourceLineInfo lineInfo) {
2329             // Prevent xsl:choose override xsl:when, etc.
2330             if (n.SourceLine == null) {
2331                 SetLineInfo(n, lineInfo);
2332             } else {
2333                 Debug.Assert(!IsDebug, "Attempt to override SourceLineInfo in debug mode");
2334             }
2335         }
2336
2337         private static QilNode SetLineInfo(QilNode n, ISourceLineInfo lineInfo) {
2338             Debug.Assert(n.SourceLine == null);
2339             if (lineInfo != null) {
2340                 SourceLineInfo.Validate(lineInfo);
2341                 if (0 < lineInfo.Start.Line && lineInfo.Start.LessOrEqual(lineInfo.End)) {
2342                     n.SourceLine = lineInfo;
2343                 }
2344             }
2345             return n;
2346         }
2347
2348         private QilNode AddDebugVariable(QilName name, QilNode value, QilNode content) {
2349             QilIterator var = f.Let(value);
2350             var.DebugName = name.ToString();
2351             return f.Loop(var, content);
2352         }
2353
2354         private QilNode SetDebugNs(QilNode n, QilList nsList) {
2355             if (n != null && nsList != null) {
2356                 QilNode nsVar = GetNsVar(nsList);
2357                 Debug.Assert(nsVar.XmlType.IsSubtypeOf(T.NamespaceS));
2358                 if (nsVar.XmlType.Cardinality == XmlQueryCardinality.One) {
2359                     // We want CLR type to be XmlQuerySequence instead of XPathNavigator
2360                     nsVar = f.TypeAssert(nsVar, T.NamespaceS);
2361                 }
2362                 n = AddDebugVariable(CloneName(nameNamespaces), nsVar, n);
2363             }
2364             return n;
2365         }
2366
2367         private QilNode AddCurrentPositionLast(QilNode content) {
2368             if (IsDebug) {
2369                 content = AddDebugVariable(CloneName(nameLast)    , GetLastPosition   (), content);
2370                 content = AddDebugVariable(CloneName(namePosition), GetCurrentPosition(), content);
2371                 content = AddDebugVariable(CloneName(nameCurrent) , GetCurrentNode    (), content);
2372             }
2373             return content;
2374         }
2375
2376         private QilName CloneName(QilName name) {
2377             return (QilName)name.ShallowClone(f.BaseFactory);
2378         }
2379
2380         // This helper internal class is used for compiling sort's and with-param's
2381         private class VariableHelper {
2382             private Stack<QilIterator>  vars = new Stack<QilIterator>();
2383             private XPathQilFactory     f;
2384
2385             public VariableHelper(XPathQilFactory f) {
2386                 this.f = f;
2387             }
2388
2389             public int StartVariables() {
2390                 return vars.Count;
2391             }
2392
2393             public void AddVariable(QilIterator let) {
2394                 Debug.Assert(let.NodeType == QilNodeType.Let);
2395                 vars.Push(let);
2396             }
2397
2398             public QilNode FinishVariables(QilNode node, int varScope) {
2399                 Debug.Assert(0 <= varScope && varScope <= vars.Count);
2400                 for (int i = vars.Count - varScope; i-- != 0; ) {
2401                     node = f.Loop(vars.Pop(), node);
2402                 }
2403                 return node;
2404             }
2405
2406             [Conditional("DEBUG")]
2407             public void CheckEmpty() {
2408                 Debug.Assert(vars.Count == 0, "Accumulated variables left unclaimed");
2409             }
2410         }
2411     }
2412 }