The Labs \ Source Viewer \ SSCLI \ System.Xml.Xsl \ XmlILCommand

  1. //------------------------------------------------------------------------------
  2. // <copyright file="XmlIlGenerator.cs" company="Microsoft">
  3. //
  4. // Copyright (c) 2006 Microsoft Corporation. All rights reserved.
  5. //
  6. // The use and distribution terms for this software are contained in the file
  7. // named license.txt, which can be found in the root of this distribution.
  8. // By using this software in any fashion, you are agreeing to be bound by the
  9. // terms of this license.
  10. //
  11. // You must not remove this notice, or any other, from this software.
  12. //
  13. // </copyright>
  14. // <spec>http://webdata/xml/specs/querylowlevel.xml</spec>
  15. //------------------------------------------------------------------------------
  16. using System;
  17. using System.Xml;
  18. using System.Xml.XPath;
  19. using System.Xml.Schema;
  20. using System.Collections;
  21. using System.Collections.Generic;
  22. using System.Diagnostics;
  23. using System.Text;
  24. using System.IO;
  25. using System.Reflection;
  26. using System.Reflection.Emit;
  27. using System.Xml.Xsl.Qil;
  28. using System.Xml.Xsl.IlGen;
  29. using System.Xml.Xsl.Runtime;
  30. namespace System.Xml.Xsl
  31. {
  32.    
  33.     internal delegate void ExecuteDelegate(XmlQueryRuntime runtime);
  34.    
  35.    
  36.     /// <summary>
  37.     /// This internal class is the entry point for creating Msil assemblies from QilExpression.
  38.     /// </summary>
  39.     /// <remarks>
  40.     /// Generate will return an AssemblyBuilder with the following setup:
  41.     /// Assembly Name = "MS.Internal.Xml.CompiledQuery"
  42.     /// Module Dll Name = "MS.Internal.Xml.CompiledQuery.dll"
  43.     /// public class MS.Internal.Xml.CompiledQuery.Test {
  44.     /// public static void Execute(XmlQueryRuntime runtime);
  45.     /// public static void Root(XmlQueryRuntime runtime);
  46.     /// private static ... UserMethod1(XmlQueryRuntime runtime, ...);
  47.     /// ...
  48.     /// private static ... UserMethodN(XmlQueryRuntime runtime, ...);
  49.     /// }
  50.     ///
  51.     /// XmlILGenerator incorporates a number of different technologies in order to generate efficient code that avoids caching
  52.     /// large result sets in memory:
  53.     ///
  54.     /// 1. Code Iterators - Query results are computed using a set of composable, interlocking iterators that alone perform a
  55.     /// simple task, but together execute complex queries. The iterators are actually little blocks of code
  56.     /// that are connected to each other using a series of jumps. Because each iterator is not instantiated
  57.     /// as a separate object, the number of objects and number of function calls is kept to a minimum during
  58.     /// execution. Also, large result sets are often computed incrementally, with each iterator performing one step in a
  59.     /// pipeline of sequence items.
  60.     ///
  61.     /// 2. Analyzers - During code generation, QilToMsil traverses the semantic tree representation of the query (QIL) several times.
  62.     /// As visits to each node in the tree start and end, various Analyzers are invoked. These Analyzers incrementally
  63.     /// collect and store information that is later used to generate faster and smaller code.
  64.     /// </remarks>
  65.     internal class XmlILGenerator
  66.     {
  67.         private QilExpression qil;
  68.         private GenerateHelper helper;
  69.         private XmlILOptimizerVisitor optVisitor;
  70.         private XmlILVisitor xmlIlVisitor;
  71.         private XmlILModule module;
  72.        
  73.         /// <summary>
  74.         /// Always output debug information in debug mode.
  75.         /// </summary>
  76.         public XmlILGenerator()
  77.         {
  78.         }
  79.        
  80.         /// <summary>
  81.         /// Given the logical query plan (QilExpression) generate a physical query plan (MSIL) that can be executed.
  82.         /// </summary>
  83.         public XmlCommand Generate(QilExpression query, AssemblyName asmName)
  84.         {
  85.             MethodInfo methRoot;
  86.             MethodInfo methExec;
  87.             bool useLRE;
  88.             bool emitSymbols;
  89.             ExecuteDelegate delExec;
  90.             XmlILMethodAttributes methAttrs;
  91.            
  92.             this.qil = query;
  93.            
  94.             useLRE = !this.qil.IsDebug && (asmName == null);
  95.             emitSymbols = this.qil.IsDebug;
  96.            
  97.             // In debug code, ensure that input QIL is correct
  98.             QilValidationVisitor.Validate(this.qil);
  99.            
  100.             // Trace Qil before optimization
  101.             XmlILTrace.WriteQil(this.qil, "qilbefore.xml");
  102.            
  103.             // Trace optimizations
  104.             XmlILTrace.TraceOptimizations(this.qil, "qilopt.xml");
  105.            
  106.             if (XmlILTrace.IsEnabled) {
  107.                 // Dump assembly to disk; can't do this when using LRE
  108.                 useLRE = false;
  109.             }
  110.            
  111.             // Optimize and annotate the Qil graph
  112.             this.optVisitor = new XmlILOptimizerVisitor(this.qil, !this.qil.IsDebug);
  113.             this.qil = this.optVisitor.Optimize();
  114.            
  115.             // In debug code, ensure that output QIL is correct
  116.             QilValidationVisitor.Validate(this.qil);
  117.            
  118.             // Trace Qil after optimization
  119.             XmlILTrace.WriteQil(this.qil, "qilafter.xml");
  120.            
  121.             // Create module in which methods will be generated
  122.             this.module = new XmlILModule(useLRE, emitSymbols, asmName);
  123.            
  124.             // Create a code generation helper for the module; enable optimizations if IsDebug is false
  125.             this.helper = new GenerateHelper(this.module, this.qil.IsDebug);
  126.            
  127.             // Create helper methods
  128.             CreateHelperFunctions();
  129.            
  130.             // Create metadata for the root expression
  131.             // public void Root()
  132.             Debug.Assert(this.qil.Root != null);
  133.             methAttrs = (this.qil.Root.SourceLine == null) ? XmlILMethodAttributes.NonUser : XmlILMethodAttributes.None;
  134.             methRoot = this.module.DefineMethod("Root", typeof(void), new Type[] {}, new string[] {}, methAttrs);
  135.            
  136.             // Create metadata for each QilExpression function that has at least one caller
  137.             CreateFunctionMetadata(this.qil.FunctionList);
  138.            
  139.             // Create metadata for each QilExpression global variable and parameter
  140.             CreateGlobalValueMetadata(this.qil.GlobalVariableList);
  141.             CreateGlobalValueMetadata(this.qil.GlobalParameterList);
  142.            
  143.             // Create Execute method
  144.             methExec = CreateExecuteFunction(methRoot);
  145.            
  146.             // Visit the QilExpression graph
  147.             this.xmlIlVisitor = new XmlILVisitor();
  148.             this.xmlIlVisitor.Visit(this.qil, this.helper, methRoot);
  149.            
  150.             this.module.BakeMethods();
  151.            
  152.             // Create delegate over "Execute" method
  153.             delExec = (ExecuteDelegate)this.module.CreateDelegate("Execute", typeof(ExecuteDelegate));
  154.            
  155.             return new XmlILCommand(delExec, this.qil, this.helper.StaticData);
  156.         }
  157.        
  158.         /// <summary>
  159.         /// Create MethodBuilder metadata for the specified QilExpression function. Annotate ndFunc with the
  160.         /// MethodBuilder. Also, each QilExpression argument type should be converted to a corresponding Clr type.
  161.         /// Each argument QilExpression node should be annotated with the resulting ParameterBuilder.
  162.         /// </summary>
  163.         private void CreateFunctionMetadata(IList<QilNode> funcList)
  164.         {
  165.             MethodInfo methInfo;
  166.             Type[] paramTypes;
  167.             string[] paramNames;
  168.             Type typReturn;
  169.             XmlILMethodAttributes methAttrs;
  170.            
  171.             foreach (QilFunction ndFunc in funcList) {
  172.                 paramTypes = new Type[ndFunc.Arguments.Count];
  173.                 paramNames = new string[ndFunc.Arguments.Count];
  174.                
  175.                 // Loop through all other parameters and save their types in the array
  176.                 for (int arg = 0; arg < ndFunc.Arguments.Count; arg++) {
  177.                     QilParameter ndParam = (QilParameter)ndFunc.Arguments[arg];
  178.                     Debug.Assert(ndParam.NodeType == QilNodeType.Parameter);
  179.                    
  180.                     // Get the type of each argument as a Clr type
  181.                     paramTypes[arg] = XmlILTypeHelper.GetStorageType(ndParam.XmlType);
  182.                    
  183.                     // Get the name of each argument
  184.                     if (ndParam.DebugName != null)
  185.                         paramNames[arg] = ndParam.DebugName;
  186.                 }
  187.                
  188.                 // Get the type of the return value
  189.                 if (XmlILConstructInfo.Read(ndFunc).PushToWriterLast) {
  190.                     // Push mode functions do not have a return value
  191.                     typReturn = typeof(void);
  192.                 }
  193.                 else {
  194.                     // Pull mode functions have a return value
  195.                     typReturn = XmlILTypeHelper.GetStorageType(ndFunc.XmlType);
  196.                 }
  197.                
  198.                 // Create the method metadata
  199.                 methAttrs = ndFunc.SourceLine == null ? XmlILMethodAttributes.NonUser : XmlILMethodAttributes.None;
  200.                 methInfo = this.module.DefineMethod(ndFunc.DebugName, typReturn, paramTypes, paramNames, methAttrs);
  201.                
  202.                 for (int arg = 0; arg < ndFunc.Arguments.Count; arg++) {
  203.                     // Set location of parameter on Let node annotation
  204.                     XmlILAnnotation.Write(ndFunc.Arguments[arg]).ArgumentPosition = arg;
  205.                 }
  206.                
  207.                 // Annotate function with the MethodInfo
  208.                 XmlILAnnotation.Write(ndFunc).FunctionBinding = methInfo;
  209.             }
  210.         }
  211.        
  212.         /// <summary>
  213.         /// Generate metadata for a method that calculates a global value.
  214.         /// </summary>
  215.         private void CreateGlobalValueMetadata(IList<QilNode> globalList)
  216.         {
  217.             MethodInfo methInfo;
  218.             Type typReturn;
  219.             XmlILMethodAttributes methAttrs;
  220.            
  221.             foreach (QilReference ndRef in globalList) {
  222.                 // public T GlobalValue()
  223.                 typReturn = XmlILTypeHelper.GetStorageType(ndRef.XmlType);
  224.                 methAttrs = ndRef.SourceLine == null ? XmlILMethodAttributes.NonUser : XmlILMethodAttributes.None;
  225.                 methInfo = this.module.DefineMethod(ndRef.DebugName.ToString(), typReturn, new Type[] {}, new string[] {}, methAttrs);
  226.                
  227.                 // Annotate function with MethodBuilder
  228.                 XmlILAnnotation.Write(ndRef).FunctionBinding = methInfo;
  229.             }
  230.         }
  231.        
  232.         /// <summary>
  233.         /// Create and generate the "Execute" method, which is the entry point to the query.
  234.         /// </summary>
  235.         private MethodInfo CreateExecuteFunction(MethodInfo methRoot)
  236.         {
  237.             MethodInfo methExec;
  238.            
  239.             // public static void Execute(XmlQueryRuntime);
  240.             methExec = this.module.DefineMethod("Execute", typeof(void), new Type[] {}, new string[] {}, XmlILMethodAttributes.NonUser);
  241.            
  242.             this.helper.MethodBegin(methExec, null, false);
  243.            
  244.             // Force some or all global values to be evaluated at start of query
  245.             EvaluateGlobalValues(this.qil.GlobalVariableList);
  246.             EvaluateGlobalValues(this.qil.GlobalParameterList);
  247.            
  248.             // Root(runtime);
  249.             this.helper.LoadQueryRuntime();
  250.             this.helper.Call(methRoot);
  251.            
  252.             this.helper.MethodEnd();
  253.            
  254.             return methExec;
  255.         }
  256.        
  257.         /// <summary>
  258.         /// Create and generate various helper methods, which are called by the generated code.
  259.         /// </summary>
  260.         private void CreateHelperFunctions()
  261.         {
  262.             MethodInfo meth;
  263.             Label lblClone;
  264.            
  265.             // public static XPathNavigator SyncToNavigator(XPathNavigator, XPathNavigator);
  266.             meth = this.module.DefineMethod("SyncToNavigator", typeof(XPathNavigator), new Type[] {typeof(XPathNavigator), typeof(XPathNavigator)}, new string[] {null, null}, XmlILMethodAttributes.NonUser | XmlILMethodAttributes.Raw);
  267.            
  268.             this.helper.MethodBegin(meth, null, false);
  269.            
  270.             // if (navigatorThis != null && navigatorThis.MoveTo(navigatorThat))
  271.             // return navigatorThis;
  272.             lblClone = this.helper.DefineLabel();
  273.             this.helper.Emit(OpCodes.Ldarg_0);
  274.             this.helper.Emit(OpCodes.Brfalse, lblClone);
  275.             this.helper.Emit(OpCodes.Ldarg_0);
  276.             this.helper.Emit(OpCodes.Ldarg_1);
  277.             this.helper.Call(XmlILMethods.NavMoveTo);
  278.             this.helper.Emit(OpCodes.Brfalse, lblClone);
  279.             this.helper.Emit(OpCodes.Ldarg_0);
  280.             this.helper.Emit(OpCodes.Ret);
  281.            
  282.             // LabelClone:
  283.             // return navigatorThat.Clone();
  284.             this.helper.MarkLabel(lblClone);
  285.             this.helper.Emit(OpCodes.Ldarg_1);
  286.             this.helper.Call(XmlILMethods.NavClone);
  287.            
  288.             this.helper.MethodEnd();
  289.         }
  290.        
  291.         /// <summary>
  292.         /// Generate code to force evaluation of some or all global variables and/or parameters.
  293.         /// </summary>
  294.         private void EvaluateGlobalValues(IList<QilNode> iterList)
  295.         {
  296.             MethodInfo methInfo;
  297.            
  298.             foreach (QilIterator ndIter in iterList) {
  299.                 // Evaluate global if generating debug code, or if global might have side effects
  300.                 if (this.qil.IsDebug || OptimizerPatterns.Read(ndIter).MatchesPattern(OptimizerPatternName.MaybeSideEffects)) {
  301.                     // Get MethodInfo that evaluates the global value and discard its return value
  302.                     methInfo = XmlILAnnotation.Write(ndIter).FunctionBinding;
  303.                     Debug.Assert(methInfo != null, "MethodInfo for global value should have been created previously.");
  304.                    
  305.                     this.helper.LoadQueryRuntime();
  306.                     this.helper.Call(methInfo);
  307.                     this.helper.Emit(OpCodes.Pop);
  308.                 }
  309.             }
  310.         }
  311.     }
  312.    
  313.    
  314.     /// <summary>
  315.     /// This is a simple implementation of MessageEventArgs.
  316.     /// </summary>
  317.     internal class XmlILQueryEventArgs : XsltMessageEncounteredEventArgs
  318.     {
  319.         private string message;
  320.        
  321.         public XmlILQueryEventArgs(string message)
  322.         {
  323.             this.message = message;
  324.         }
  325.        
  326.         public override string Message {
  327.             get { return this.message; }
  328.         }
  329.     }
  330.    
  331.    
  332.     /// <summary>
  333.     /// This is the executable command generated by the XmlILGenerator.
  334.     /// </summary>
  335.     internal class XmlILCommand : XmlCommand
  336.     {
  337.         private XsltMessageEncounteredEventHandler onMessage;
  338.         private ExecuteDelegate delExec;
  339.         private XmlWriterSettings defaultWriterSettings;
  340.         private IList<WhitespaceRule> wsRules;
  341.         private string[] globalNames;
  342.         private string[] names;
  343.         private StringPair[][] prefixMappingsList;
  344.         private Int32Pair[] filters;
  345.         private XmlQueryType[] types;
  346.         private XmlCollation[] collations;
  347.         private EarlyBoundInfo[] earlyInfo;
  348.        
  349.         /// <summary>
  350.         /// Constructor.
  351.         /// </summary>
  352.         public XmlILCommand(ExecuteDelegate delExec, QilExpression qil, StaticDataManager staticData)
  353.         {
  354.             Debug.Assert(qil != null);
  355.             this.delExec = delExec;
  356.             this.defaultWriterSettings = qil.DefaultWriterSettings;
  357.             this.wsRules = qil.WhitespaceRules;
  358.             this.names = staticData.Names;
  359.             this.prefixMappingsList = staticData.PrefixMappingsList;
  360.             this.filters = staticData.NameFilters;
  361.             this.types = staticData.XmlTypes;
  362.             this.collations = staticData.Collations;
  363.             this.globalNames = staticData.GlobalNames;
  364.             this.earlyInfo = staticData.EarlyBound;
  365.         }
  366.        
  367.         /// <summary>
  368.         /// Return the rules used for whitespace stripping/preservation.
  369.         /// </summary>
  370.         public IList<WhitespaceRule> WhitespaceRules {
  371.             get { return this.wsRules; }
  372.         }
  373.        
  374.         /// <summary>
  375.         /// Return names of all global variables and parameters used by this query.
  376.         /// </summary>
  377.         public string[] GlobalNames {
  378.             get { return this.globalNames; }
  379.         }
  380.        
  381.         /// <summary>
  382.         /// Return array of names used by this query.
  383.         /// </summary>
  384.         public string[] Names {
  385.             get { return this.names; }
  386.         }
  387.        
  388.         /// <summary>
  389.         /// Return array of prefix mappings used by this query.
  390.         /// </summary>
  391.         public StringPair[][] PrefixMappingsList {
  392.             get { return this.prefixMappingsList; }
  393.         }
  394.        
  395.         /// <summary>
  396.         /// Return array of name filter specifications used by this query.
  397.         /// </summary>
  398.         public Int32Pair[] Filters {
  399.             get { return this.filters; }
  400.         }
  401.        
  402.         /// <summary>
  403.         /// Return array of types used by this query.
  404.         /// </summary>
  405.         public XmlQueryType[] Types {
  406.             get { return this.types; }
  407.         }
  408.        
  409.         /// <summary>
  410.         /// Return array of collations used by this query.
  411.         /// </summary>
  412.         public XmlCollation[] Collations {
  413.             get { return this.collations; }
  414.         }
  415.        
  416.         /// <summary>
  417.         /// Return array of early bound object information used by this query.
  418.         /// </summary>
  419.         public EarlyBoundInfo[] EarlyBound {
  420.             get { return this.earlyInfo; }
  421.         }
  422.        
  423.         /// <summary>
  424.         /// Fire the OnMessageEvent, passing the specified text as the message.
  425.         /// </summary>
  426.         public void FireOnMessageEvent(string message)
  427.         {
  428.             if (this.onMessage != null)
  429.                 this.onMessage(this, new XmlILQueryEventArgs(message));
  430.             else
  431.                 Console.WriteLine(message);
  432.         }
  433.        
  434.         /// <summary>
  435.         /// Default document as XPathNavigator.
  436.         /// </summary>
  437.         public override void Execute(IXPathNavigable contextDocument, XmlResolver dataSources, XsltArgumentList argumentList, XmlWriter results)
  438.         {
  439.             if (results == null)
  440.                 throw new ArgumentNullException("results");
  441.            
  442.             if (contextDocument != null)
  443.                 Execute(contextDocument.CreateNavigator(), dataSources, argumentList, results, false);
  444.             else
  445.                 Execute(null, dataSources, argumentList, results, false);
  446.         }
  447.        
  448.         /// <summary>
  449.         /// Default document as XPathNavigator.
  450.         /// </summary>
  451.         public override void Execute(IXPathNavigable contextDocument, XmlResolver dataSources, XsltArgumentList argumentList, TextWriter results)
  452.         {
  453.             if (results == null)
  454.                 throw new ArgumentNullException("results");
  455.            
  456.             Execute(contextDocument, dataSources, argumentList, XmlWriter.Create(results, this.defaultWriterSettings));
  457.         }
  458.        
  459.         /// <summary>
  460.         /// Default document as XPathNavigator.
  461.         /// </summary>
  462.         public override void Execute(IXPathNavigable contextDocument, XmlResolver dataSources, XsltArgumentList argumentList, Stream results)
  463.         {
  464.             if (results == null)
  465.                 throw new ArgumentNullException("results");
  466.            
  467.             Execute(contextDocument, dataSources, argumentList, XmlWriter.Create(results, this.defaultWriterSettings));
  468.         }
  469.        
  470.         /// <summary>
  471.         /// Executes the query by accessing datasources via the XmlResolver and using run-time parameters
  472.         /// as provided by the XsltArgumentList. The default document is mapped into the XmlResolver with the
  473.         /// provided name. The results are output to the provided XmlWriter.
  474.         /// </summary>
  475.         public void Execute(string contextDocumentUri, XmlResolver dataSources, XsltArgumentList argumentList, XmlWriter results)
  476.         {
  477.             if (results == null)
  478.                 throw new ArgumentNullException("results");
  479.            
  480.             Execute(contextDocumentUri, dataSources, argumentList, results, false);
  481.         }
  482.        
  483.         /// <summary>
  484.         /// Executes the query by accessing datasources via the XmlResolver and using
  485.         /// run-time parameters as provided by the XsltArgumentList. The default document
  486.         /// is mapped into the XmlResolver with the provided name. The results are returned
  487.         /// as an IList.
  488.         /// </summary>
  489.         public IList Evaluate(string contextDocumentUri, XmlResolver dataSources, XsltArgumentList argumentList)
  490.         {
  491.             XmlCachedSequenceWriter seqwrt = new XmlCachedSequenceWriter();
  492.             Execute(contextDocumentUri, dataSources, argumentList, seqwrt);
  493.             return seqwrt.ResultSequence;
  494.         }
  495.        
  496.         /// <summary>
  497.         /// Executes the query by accessing datasources via the XmlResolver and using run-time parameters
  498.         /// as provided by the XsltArgumentList. The default document is mapped into the XmlResolver with the
  499.         /// provided name. The results are output to the provided XmlWriter.
  500.         /// </summary>
  501.         public override void Execute(XmlReader contextDocument, XmlResolver dataSources, XsltArgumentList argumentList, XmlWriter results)
  502.         {
  503.             if (results == null)
  504.                 throw new ArgumentNullException("results");
  505.            
  506.             Execute(contextDocument, dataSources, argumentList, results, false);
  507.         }
  508.        
  509.         /// <summary>
  510.         /// Executes the query by accessing datasources via the XmlResolver and using run-time parameters
  511.         /// as provided by the XsltArgumentList. The default document is mapped into the XmlResolver with the
  512.         /// provided name. The results are output to the provided TextWriter.
  513.         /// </summary>
  514.         public override void Execute(XmlReader contextDocument, XmlResolver dataSources, XsltArgumentList argumentList, TextWriter results)
  515.         {
  516.             if (results == null)
  517.                 throw new ArgumentNullException("results");
  518.            
  519.             Execute(contextDocument, dataSources, argumentList, XmlWriter.Create(results, this.defaultWriterSettings), true);
  520.         }
  521.        
  522.         /// <summary>
  523.         /// Executes the query by accessing datasources via the XmlResolver and using run-time parameters
  524.         /// as provided by the XsltArgumentList. The default document is mapped into the XmlResolver with the
  525.         /// provided name. The results are output to the provided Stream.
  526.         /// </summary>
  527.         public override void Execute(XmlReader contextDocument, XmlResolver dataSources, XsltArgumentList argumentList, Stream results)
  528.         {
  529.             if (results == null)
  530.                 throw new ArgumentNullException("results");
  531.            
  532.             Execute(contextDocument, dataSources, argumentList, XmlWriter.Create(results, this.defaultWriterSettings), true);
  533.         }
  534.         /// <summary>
  535.         /// Executes the query by accessing datasources via the XmlResolver and using
  536.         /// run-time parameters as provided by the XsltArgumentList. The default document
  537.         /// is mapped into the XmlResolver with the provided name. The results are returned
  538.         /// as an IList.
  539.         /// </summary>
  540.         public override IList Evaluate(XmlReader contextDocument, XmlResolver dataSources, XsltArgumentList argumentList)
  541.         {
  542.             XmlCachedSequenceWriter seqwrt = new XmlCachedSequenceWriter();
  543.             Execute(contextDocument, dataSources, argumentList, seqwrt);
  544.             return seqwrt.ResultSequence;
  545.         }
  546.        
  547.         /// <summary>
  548.         /// Execute the dynamic assembly generated by the XmlILGenerator.
  549.         /// </summary>
  550.         private void Execute(object defaultDocument, XmlResolver dataSources, XsltArgumentList argumentList, XmlWriter writer, bool closeWriter)
  551.         {
  552.             try {
  553.                 // Try to extract a RawWriter
  554.                 XmlWellFormedWriter wellFormedWriter = writer as XmlWellFormedWriter;
  555.                
  556.                 if (wellFormedWriter != null && wellFormedWriter.WriteState == WriteState.Start && wellFormedWriter.Settings.ConformanceLevel != ConformanceLevel.Document) {
  557.                    
  558.                     // Extracted RawWriter from WellFormedWriter
  559.                     Execute(defaultDocument, dataSources, argumentList, new XmlMergeSequenceWriter(wellFormedWriter.RawWriter));
  560.                 }
  561.                 else {
  562.                     // Wrap Writer in RawWriter
  563.                     Execute(defaultDocument, dataSources, argumentList, new XmlMergeSequenceWriter(new XmlRawWriterWrapper(writer)));
  564.                 }
  565.             }
  566.             finally {
  567.                 // Close Writers that are created by XmlILGenerator; flush external writers
  568.                 if (closeWriter)
  569.                     writer.Close();
  570.                 else
  571.                     writer.Flush();
  572.             }
  573.         }
  574.        
  575.         /// <summary>
  576.         /// Execute the dynamic assembly generated by the XmlILGenerator.
  577.         /// </summary>
  578.         private void Execute(object defaultDocument, XmlResolver dataSources, XsltArgumentList argumentList, XmlSequenceWriter results)
  579.         {
  580.             Debug.Assert(results != null);
  581.            
  582.             // Set up message sink
  583.             this.onMessage = argumentList != null ? argumentList.xsltMessageEncountered : null;
  584.            
  585.             // Ensure that dataSources is always non-null
  586.             if (dataSources == null)
  587.                 dataSources = XmlNullResolver.Singleton;
  588.            
  589.             this.delExec(new XmlQueryRuntime(this, defaultDocument, dataSources, argumentList, results));
  590.         }
  591.     }
  592. }

Developer Fusion