XmlIlGenerator.cs source code in C# .NET

Source code for the .NET framework in C#

                        

Code:

/ Net / Net / 3.5.50727.3053 / DEVDIV / depot / DevDiv / releases / whidbey / netfxsp / ndp / fx / src / XmlUtils / System / Xml / Xsl / XmlIlGenerator.cs / 5 / XmlIlGenerator.cs

                            //------------------------------------------------------------------------------ 
// 
//     Copyright (c) Microsoft Corporation.  All rights reserved.
// 
// [....] 
// [....]
//----------------------------------------------------------------------------- 
 
using System.Collections;
using System.Collections.Generic; 
using System.Diagnostics;
using System.IO;
using System.Reflection;
using System.Reflection.Emit; 
using System.Security;
using System.Xml.XPath; 
using System.Xml.Xsl.IlGen; 
using System.Xml.Xsl.Qil;
using System.Xml.Xsl.Runtime; 

namespace System.Xml.Xsl {

    internal delegate void ExecuteDelegate(XmlQueryRuntime runtime); 

 
    ///  
    /// This internal class is the entry point for creating Msil assemblies from QilExpression.
    ///  
    /// 
    /// Generate will return an AssemblyBuilder with the following setup:
    /// Assembly Name = "MS.Internal.Xml.CompiledQuery"
    /// Module Dll Name = "MS.Internal.Xml.CompiledQuery.dll" 
    /// public class MS.Internal.Xml.CompiledQuery.Test {
    ///     public static void Execute(XmlQueryRuntime runtime); 
    ///     public static void Root(XmlQueryRuntime runtime); 
    ///     private static ... UserMethod1(XmlQueryRuntime runtime, ...);
    ///     ... 
    ///     private static ... UserMethodN(XmlQueryRuntime runtime, ...);
    /// }
    ///
    /// XmlILGenerator incorporates a number of different technologies in order to generate efficient code that avoids caching 
    /// large result sets in memory:
    /// 
    /// 1. Code Iterators - Query results are computed using a set of composable, interlocking iterators that alone perform a 
    /// simple task, but together execute complex queries.  The iterators are actually little blocks of code
    /// that are connected to each other using a series of jumps.  Because each iterator is not instantiated 
    /// as a separate object, the number of objects and number of function calls is kept to a minimum during
    /// execution.  Also, large result sets are often computed incrementally, with each iterator performing one step in a
    /// pipeline of sequence items.
    /// 
    /// 2. Analyzers - During code generation, QilToMsil traverses the semantic tree representation of the query (QIL) several times.
    /// As visits to each node in the tree start and end, various Analyzers are invoked.  These Analyzers incrementally 
    /// collect and store information that is later used to generate faster and smaller code. 
    /// 
    internal class XmlILGenerator { 
        private QilExpression qil;
        private GenerateHelper helper;
        private XmlILOptimizerVisitor optVisitor;
        private XmlILVisitor xmlIlVisitor; 
        private XmlILModule module;
 
        ///  
        /// Always output debug information in debug mode.
        ///  
        public XmlILGenerator() {
        }

        ///  
        /// Given the logical query plan (QilExpression) generate a physical query plan (MSIL) that can be executed.
        ///  
        public XmlILCommand Generate(QilExpression query, TypeBuilder typeBldr) { 
            MethodInfo methRoot, methExec;
            bool useLRE, emitSymbols; 
            XmlQueryStaticData staticData;
            ExecuteDelegate delExec;
            XmlILMethodAttributes methAttrs;
 
            this.qil = query;
 
            useLRE = !this.qil.IsDebug && (typeBldr == null); 
            emitSymbols = this.qil.IsDebug;
 
            // In debug code, ensure that input QIL is correct
            QilValidationVisitor.Validate(this.qil);

            // Trace Qil before optimization 
            XmlILTrace.WriteQil(this.qil, "qilbefore.xml");
 
            // Trace optimizations 
            XmlILTrace.TraceOptimizations(this.qil, "qilopt.xml");
 
            if (XmlILTrace.IsEnabled) {
                // Dump assembly to disk; can't do this when using LRE
                useLRE = false;
            } 

            // Optimize and annotate the Qil graph 
            this.optVisitor = new XmlILOptimizerVisitor(this.qil, !this.qil.IsDebug); 
            this.qil = this.optVisitor.Optimize();
 
            // In debug code, ensure that output QIL is correct
            QilValidationVisitor.Validate(this.qil);

            // Trace Qil after optimization 
            XmlILTrace.WriteQil(this.qil, "qilafter.xml");
 
            try { 
                XmlILModule.CreateModulePermissionSet.Assert();
 
                // Create module in which methods will be generated
                if (typeBldr != null)
                    this.module = new XmlILModule(typeBldr);
                else 
                    this.module = new XmlILModule(useLRE, emitSymbols);
 
                // Create a code generation helper for the module; enable optimizations if IsDebug is false 
                this.helper = new GenerateHelper(this.module, this.qil.IsDebug);
 
                // Create helper methods
                CreateHelperFunctions();

                // Create metadata for the Execute function, which is the entry point to the query 
                // public static void Execute(XmlQueryRuntime);
                methExec = this.module.DefineMethod("Execute", typeof(void), new Type[] {}, new string[] {}, XmlILMethodAttributes.NonUser); 
 
                // Create metadata for the root expression
                // public void Root() 
                Debug.Assert(this.qil.Root != null);
                methAttrs = (this.qil.Root.SourceLine == null) ? XmlILMethodAttributes.NonUser : XmlILMethodAttributes.None;
                methRoot = this.module.DefineMethod("Root", typeof(void), new Type[] {}, new string[] {}, methAttrs);
 
                // Declare all early bound function objects
                foreach (EarlyBoundInfo info in this.qil.EarlyBoundTypes) 
                    this.helper.StaticData.DeclareEarlyBound(info.NamespaceUri, info.EarlyBoundType); 

                // Create metadata for each QilExpression function that has at least one caller 
                CreateFunctionMetadata(this.qil.FunctionList);

                // Create metadata for each QilExpression global variable and parameter
                CreateGlobalValueMetadata(this.qil.GlobalVariableList); 
                CreateGlobalValueMetadata(this.qil.GlobalParameterList);
 
                // Generate Execute method 
                GenerateExecuteFunction(methExec, methRoot);
 
                // Visit the QilExpression graph
                this.xmlIlVisitor = new XmlILVisitor();
                this.xmlIlVisitor.Visit(this.qil, this.helper, methRoot);
 
                // Collect all static information required by the runtime
                staticData = new XmlQueryStaticData( 
                                    this.qil.DefaultWriterSettings, 
                                    this.qil.WhitespaceRules,
                                    this.helper.StaticData); 

                // Create static constructor that initializes XmlQueryStaticData instance at runtime
                if (typeBldr != null)
                    CreateTypeInitializer(staticData); 

 
                // Finish up creation of the type 
                this.module.BakeMethods();
 
                // Create delegate over "Execute" method
                delExec = (ExecuteDelegate) this.module.CreateDelegate("Execute", typeof(ExecuteDelegate));
                return new XmlILCommand(delExec, staticData);
            } 
            finally {
                CodeAccessPermission.RevertAssert(); 
            } 
        }
 
        /// 
        /// Create MethodBuilder metadata for the specified QilExpression function.  Annotate ndFunc with the
        /// MethodBuilder.  Also, each QilExpression argument type should be converted to a corresponding Clr type.
        /// Each argument QilExpression node should be annotated with the resulting ParameterBuilder. 
        /// 
        private void CreateFunctionMetadata(IList funcList) { 
            MethodInfo methInfo; 
            Type[] paramTypes;
            string[] paramNames; 
            Type typReturn;
            XmlILMethodAttributes methAttrs;

            foreach (QilFunction ndFunc in funcList) { 
                paramTypes = new Type[ndFunc.Arguments.Count];
                paramNames = new string[ndFunc.Arguments.Count]; 
 
                // Loop through all other parameters and save their types in the array
                for (int arg = 0; arg < ndFunc.Arguments.Count; arg ++) { 
                    QilParameter ndParam = (QilParameter) ndFunc.Arguments[arg];
                    Debug.Assert(ndParam.NodeType == QilNodeType.Parameter);

                    // Get the type of each argument as a Clr type 
                    paramTypes[arg] = XmlILTypeHelper.GetStorageType(ndParam.XmlType);
 
                    // Get the name of each argument 
                    if (ndParam.DebugName != null)
                        paramNames[arg] = ndParam.DebugName; 
                }

                // Get the type of the return value
                if (XmlILConstructInfo.Read(ndFunc).PushToWriterLast) { 
                    // Push mode functions do not have a return value
                    typReturn = typeof(void); 
                } 
                else {
                    // Pull mode functions have a return value 
                    typReturn = XmlILTypeHelper.GetStorageType(ndFunc.XmlType);
                }

                // Create the method metadata 
                methAttrs = ndFunc.SourceLine == null ? XmlILMethodAttributes.NonUser : XmlILMethodAttributes.None;
                methInfo = this.module.DefineMethod(ndFunc.DebugName, typReturn, paramTypes, paramNames, methAttrs); 
 
                for (int arg = 0; arg < ndFunc.Arguments.Count; arg ++) {
                    // Set location of parameter on Let node annotation 
                    XmlILAnnotation.Write(ndFunc.Arguments[arg]).ArgumentPosition = arg;
                }

                // Annotate function with the MethodInfo 
                XmlILAnnotation.Write(ndFunc).FunctionBinding = methInfo;
            } 
        } 

        ///  
        /// Generate metadata for a method that calculates a global value.
        /// 
        private void CreateGlobalValueMetadata(IList globalList) {
            MethodInfo methInfo; 
            Type typReturn;
            XmlILMethodAttributes methAttrs; 
 
            foreach (QilReference ndRef in globalList) {
                // public T GlobalValue() 
                typReturn = XmlILTypeHelper.GetStorageType(ndRef.XmlType);
                methAttrs = ndRef.SourceLine == null ? XmlILMethodAttributes.NonUser : XmlILMethodAttributes.None;
                methInfo = this.module.DefineMethod(ndRef.DebugName.ToString(), typReturn, new Type[] {}, new string[] {}, methAttrs);
 
                // Annotate function with MethodBuilder
                XmlILAnnotation.Write(ndRef).FunctionBinding = methInfo; 
            } 
        }
 
        /// 
        /// Generate the "Execute" method, which is the entry point to the query.
        /// 
        private MethodInfo GenerateExecuteFunction(MethodInfo methExec, MethodInfo methRoot) { 
            this.helper.MethodBegin(methExec, null, false);
 
            // Force some or all global values to be evaluated at start of query 
            EvaluateGlobalValues(this.qil.GlobalVariableList);
            EvaluateGlobalValues(this.qil.GlobalParameterList); 

            // Root(runtime);
            this.helper.LoadQueryRuntime();
            this.helper.Call(methRoot); 

            this.helper.MethodEnd(); 
 
            return methExec;
        } 

        /// 
        /// Create and generate various helper methods, which are called by the generated code.
        ///  
        private void CreateHelperFunctions() {
            MethodInfo meth; 
            Label lblClone; 

            // public static XPathNavigator SyncToNavigator(XPathNavigator, XPathNavigator); 
            meth = this.module.DefineMethod(
                            "SyncToNavigator",
                            typeof(XPathNavigator),
                            new Type[] {typeof(XPathNavigator), typeof(XPathNavigator)}, 
                            new string[] {null, null},
                            XmlILMethodAttributes.NonUser | XmlILMethodAttributes.Raw); 
 
            this.helper.MethodBegin(meth, null, false);
 
            // if (navigatorThis != null && navigatorThis.MoveTo(navigatorThat))
            //     return navigatorThis;
            lblClone = this.helper.DefineLabel();
            this.helper.Emit(OpCodes.Ldarg_0); 
            this.helper.Emit(OpCodes.Brfalse, lblClone);
            this.helper.Emit(OpCodes.Ldarg_0); 
            this.helper.Emit(OpCodes.Ldarg_1); 
            this.helper.Call(XmlILMethods.NavMoveTo);
            this.helper.Emit(OpCodes.Brfalse, lblClone); 
            this.helper.Emit(OpCodes.Ldarg_0);
            this.helper.Emit(OpCodes.Ret);

            // LabelClone: 
            // return navigatorThat.Clone();
            this.helper.MarkLabel(lblClone); 
            this.helper.Emit(OpCodes.Ldarg_1); 
            this.helper.Call(XmlILMethods.NavClone);
 
            this.helper.MethodEnd();
        }

        ///  
        /// Generate code to force evaluation of some or all global variables and/or parameters.
        ///  
        private void EvaluateGlobalValues(IList iterList) { 
            MethodInfo methInfo;
 
            foreach (QilIterator ndIter in iterList) {
                // Evaluate global if generating debug code, or if global might have side effects
                if (this.qil.IsDebug || OptimizerPatterns.Read(ndIter).MatchesPattern(OptimizerPatternName.MaybeSideEffects)) {
                    // Get MethodInfo that evaluates the global value and discard its return value 
                    methInfo = XmlILAnnotation.Write(ndIter).FunctionBinding;
                    Debug.Assert(methInfo != null, "MethodInfo for global value should have been created previously."); 
 
                    this.helper.LoadQueryRuntime();
                    this.helper.Call(methInfo); 
                    this.helper.Emit(OpCodes.Pop);
                }
            }
        } 

        ///  
        /// Create static constructor that initializes XmlQueryStaticData instance at runtime. 
        /// 
        public void CreateTypeInitializer(XmlQueryStaticData staticData) { 
            byte[] data;
            Type[] ebTypes;
            FieldInfo fldInitData, fldData, fldTypes;
            ConstructorInfo cctor; 

            staticData.GetObjectData(out data, out ebTypes); 
            fldInitData = this.module.DefineInitializedData("__" + XmlQueryStaticData.DataFieldName, data); 
            fldData = this.module.DefineField(XmlQueryStaticData.DataFieldName, typeof(object));
            fldTypes = this.module.DefineField(XmlQueryStaticData.TypesFieldName, typeof(Type[])); 

            cctor = this.module.DefineTypeInitializer();
            this.helper.MethodBegin(cctor, null, false);
 
            // s_data = new byte[s_initData.Length] { s_initData };
            this.helper.LoadInteger(data.Length); 
            this.helper.Emit(OpCodes.Newarr, typeof(byte)); 
            this.helper.Emit(OpCodes.Dup);
            this.helper.Emit(OpCodes.Ldtoken, fldInitData); 
            this.helper.Call(XmlILMethods.InitializeArray);
            this.helper.Emit(OpCodes.Stsfld, fldData);

            if (ebTypes != null) { 
                // Type[] types = new Type[s_ebTypes.Length];
                LocalBuilder locTypes = this.helper.DeclareLocal("$$$types", typeof(Type[])); 
                this.helper.LoadInteger(ebTypes.Length); 
                this.helper.Emit(OpCodes.Newarr, typeof(Type));
                this.helper.Emit(OpCodes.Stloc, locTypes); 

                for (int idx = 0; idx < ebTypes.Length; idx++) {
                    // types[idx] = ebTypes[idx];
                    this.helper.Emit(OpCodes.Ldloc, locTypes); 
                    this.helper.LoadInteger(idx);
                    this.helper.LoadType(ebTypes[idx]); 
                    this.helper.Emit(OpCodes.Stelem_Ref); 
                }
 
                // s_types = types;
                this.helper.Emit(OpCodes.Ldloc, locTypes);
                this.helper.Emit(OpCodes.Stsfld, fldTypes);
            } 

            this.helper.MethodEnd(); 
        } 
    }
 

    /// 
    /// This is the executable command generated by the XmlILGenerator.
    ///  
    internal class XmlILCommand : XmlCommand {
        private ExecuteDelegate delExec; 
        private XmlQueryStaticData staticData; 

        ///  
        /// Constructor.
        /// 
        public XmlILCommand(ExecuteDelegate delExec, XmlQueryStaticData staticData) {
            Debug.Assert(delExec != null && staticData != null); 
            this.delExec = delExec;
            this.staticData = staticData; 
        } 

        ///  
        /// Return execute delegate.
        /// 
        public ExecuteDelegate ExecuteDelegate {
            get { return delExec; } 
        }
 
        ///  
        /// Return query static data required by the runtime.
        ///  
        public XmlQueryStaticData StaticData {
            get { return staticData; }
        }
 
        /// 
        /// Default document as XPathNavigator. 
        ///  
        public override void Execute(IXPathNavigable contextDocument, XmlResolver dataSources, XsltArgumentList argumentList, XmlWriter results) {
            if (results == null) 
                throw new ArgumentNullException("results");

            if (contextDocument != null)
                Execute(contextDocument.CreateNavigator(), dataSources, argumentList, results, false); 
            else
                Execute(null, dataSources, argumentList, results, false); 
        } 

        ///  
        /// Default document as XPathNavigator.
        /// 
        public override void Execute(IXPathNavigable contextDocument, XmlResolver dataSources, XsltArgumentList argumentList, TextWriter results) {
            if (results == null) 
                throw new ArgumentNullException("results");
 
            Execute(contextDocument, dataSources, argumentList, XmlWriter.Create(results, this.staticData.DefaultWriterSettings)); 
        }
 
        /// 
        /// Default document as XPathNavigator.
        /// 
        public override void Execute(IXPathNavigable contextDocument, XmlResolver dataSources, XsltArgumentList argumentList, Stream results) { 
            if (results == null)
                throw new ArgumentNullException("results"); 
 
            Execute(contextDocument, dataSources, argumentList, XmlWriter.Create(results, this.staticData.DefaultWriterSettings));
        } 

        /// 
        /// Executes the query by accessing datasources via the XmlResolver and using run-time parameters
        /// as provided by the XsltArgumentList. The default document is mapped into the XmlResolver with the 
        /// provided name. The results are output to the provided XmlWriter.
        ///  
        public void Execute(string contextDocumentUri, XmlResolver dataSources, XsltArgumentList argumentList, XmlWriter results) { 
            if (results == null)
                throw new ArgumentNullException("results"); 

            Execute(contextDocumentUri, dataSources, argumentList, results, false);
        }
 
        /// 
        /// Executes the query by accessing datasources via the XmlResolver and using 
        /// run-time parameters as provided by the XsltArgumentList. The default document 
        /// is mapped into the XmlResolver with the provided name. The results are returned
        /// as an IList. 
        /// 
        public IList Evaluate(string contextDocumentUri, XmlResolver dataSources, XsltArgumentList argumentList) {
            XmlCachedSequenceWriter seqwrt = new XmlCachedSequenceWriter();
            Execute(contextDocumentUri, dataSources, argumentList, seqwrt); 
            return seqwrt.ResultSequence;
        } 
 
        /// 
        /// Executes the query by accessing datasources via the XmlResolver and using run-time parameters 
        /// as provided by the XsltArgumentList. The default document is mapped into the XmlResolver with the
        /// provided name. The results are output to the provided XmlWriter.
        /// 
        public override void Execute(XmlReader contextDocument, XmlResolver dataSources, XsltArgumentList argumentList, XmlWriter results) { 
            if (results == null)
                throw new ArgumentNullException("results"); 
 
            Execute(contextDocument, dataSources, argumentList, results, false);
        } 

        /// 
        /// Executes the query by accessing datasources via the XmlResolver and using run-time parameters
        /// as provided by the XsltArgumentList. The default document is mapped into the XmlResolver with the 
        /// provided name. The results are output to the provided TextWriter.
        ///  
        public override void Execute(XmlReader contextDocument, XmlResolver dataSources, XsltArgumentList argumentList, TextWriter results) { 
            if (results == null)
                throw new ArgumentNullException("results"); 

            Execute(contextDocument, dataSources, argumentList, XmlWriter.Create(results, this.staticData.DefaultWriterSettings), true);
        }
 
        /// 
        /// Executes the query by accessing datasources via the XmlResolver and using run-time parameters 
        /// as provided by the XsltArgumentList. The default document is mapped into the XmlResolver with the 
        /// provided name. The results are output to the provided Stream.
        ///  
        public override void Execute(XmlReader contextDocument, XmlResolver dataSources, XsltArgumentList argumentList, Stream results) {
            if (results == null)
                throw new ArgumentNullException("results");
 
            Execute(contextDocument, dataSources, argumentList, XmlWriter.Create(results, this.staticData.DefaultWriterSettings), true);
        } 
 
        /// 
        /// Executes the query by accessing datasources via the XmlResolver and using 
        /// run-time parameters as provided by the XsltArgumentList. The default document
        /// is mapped into the XmlResolver with the provided name. The results are returned
        /// as an IList.
        ///  
        public override IList Evaluate(XmlReader contextDocument, XmlResolver dataSources, XsltArgumentList argumentList) {
            XmlCachedSequenceWriter seqwrt = new XmlCachedSequenceWriter(); 
            Execute(contextDocument, dataSources, argumentList, seqwrt); 
            return seqwrt.ResultSequence;
        } 

        /// 
        /// Execute the dynamic assembly generated by the XmlILGenerator.
        ///  
        private void Execute(object defaultDocument, XmlResolver dataSources, XsltArgumentList argumentList, XmlWriter writer, bool closeWriter) {
            try { 
                // Try to extract a RawWriter 
                XmlWellFormedWriter wellFormedWriter = writer as XmlWellFormedWriter;
 
                if (wellFormedWriter != null &&
                    wellFormedWriter.WriteState == WriteState.Start &&
                    wellFormedWriter.Settings.ConformanceLevel != ConformanceLevel.Document) {
 
                    // Extracted RawWriter from WellFormedWriter
                    Execute(defaultDocument, dataSources, argumentList, new XmlMergeSequenceWriter(wellFormedWriter.RawWriter)); 
                } 
                else {
                    // Wrap Writer in RawWriter 
                    Execute(defaultDocument, dataSources, argumentList, new XmlMergeSequenceWriter(new XmlRawWriterWrapper(writer)));
                }
            }
            finally { 
                // Close Writers that are created by XmlILGenerator; flush external writers
                if (closeWriter) 
                    writer.Close(); 
                else
                    writer.Flush(); 
            }
        }

        ///  
        /// Execute the dynamic assembly generated by the XmlILGenerator.
        ///  
        private void Execute(object defaultDocument, XmlResolver dataSources, XsltArgumentList argumentList, XmlSequenceWriter results) { 
            Debug.Assert(results != null);
 
            // Ensure that dataSources is always non-null
            if (dataSources == null)
                dataSources = XmlNullResolver.Singleton;
 
            this.delExec(new XmlQueryRuntime(this.staticData, defaultDocument, dataSources, argumentList, results));
        } 
    } 
}

// File provided for Reference Use Only by Microsoft Corporation (c) 2007.
//------------------------------------------------------------------------------ 
// 
//     Copyright (c) Microsoft Corporation.  All rights reserved.
// 
// [....] 
// [....]
//----------------------------------------------------------------------------- 
 
using System.Collections;
using System.Collections.Generic; 
using System.Diagnostics;
using System.IO;
using System.Reflection;
using System.Reflection.Emit; 
using System.Security;
using System.Xml.XPath; 
using System.Xml.Xsl.IlGen; 
using System.Xml.Xsl.Qil;
using System.Xml.Xsl.Runtime; 

namespace System.Xml.Xsl {

    internal delegate void ExecuteDelegate(XmlQueryRuntime runtime); 

 
    ///  
    /// This internal class is the entry point for creating Msil assemblies from QilExpression.
    ///  
    /// 
    /// Generate will return an AssemblyBuilder with the following setup:
    /// Assembly Name = "MS.Internal.Xml.CompiledQuery"
    /// Module Dll Name = "MS.Internal.Xml.CompiledQuery.dll" 
    /// public class MS.Internal.Xml.CompiledQuery.Test {
    ///     public static void Execute(XmlQueryRuntime runtime); 
    ///     public static void Root(XmlQueryRuntime runtime); 
    ///     private static ... UserMethod1(XmlQueryRuntime runtime, ...);
    ///     ... 
    ///     private static ... UserMethodN(XmlQueryRuntime runtime, ...);
    /// }
    ///
    /// XmlILGenerator incorporates a number of different technologies in order to generate efficient code that avoids caching 
    /// large result sets in memory:
    /// 
    /// 1. Code Iterators - Query results are computed using a set of composable, interlocking iterators that alone perform a 
    /// simple task, but together execute complex queries.  The iterators are actually little blocks of code
    /// that are connected to each other using a series of jumps.  Because each iterator is not instantiated 
    /// as a separate object, the number of objects and number of function calls is kept to a minimum during
    /// execution.  Also, large result sets are often computed incrementally, with each iterator performing one step in a
    /// pipeline of sequence items.
    /// 
    /// 2. Analyzers - During code generation, QilToMsil traverses the semantic tree representation of the query (QIL) several times.
    /// As visits to each node in the tree start and end, various Analyzers are invoked.  These Analyzers incrementally 
    /// collect and store information that is later used to generate faster and smaller code. 
    /// 
    internal class XmlILGenerator { 
        private QilExpression qil;
        private GenerateHelper helper;
        private XmlILOptimizerVisitor optVisitor;
        private XmlILVisitor xmlIlVisitor; 
        private XmlILModule module;
 
        ///  
        /// Always output debug information in debug mode.
        ///  
        public XmlILGenerator() {
        }

        ///  
        /// Given the logical query plan (QilExpression) generate a physical query plan (MSIL) that can be executed.
        ///  
        public XmlILCommand Generate(QilExpression query, TypeBuilder typeBldr) { 
            MethodInfo methRoot, methExec;
            bool useLRE, emitSymbols; 
            XmlQueryStaticData staticData;
            ExecuteDelegate delExec;
            XmlILMethodAttributes methAttrs;
 
            this.qil = query;
 
            useLRE = !this.qil.IsDebug && (typeBldr == null); 
            emitSymbols = this.qil.IsDebug;
 
            // In debug code, ensure that input QIL is correct
            QilValidationVisitor.Validate(this.qil);

            // Trace Qil before optimization 
            XmlILTrace.WriteQil(this.qil, "qilbefore.xml");
 
            // Trace optimizations 
            XmlILTrace.TraceOptimizations(this.qil, "qilopt.xml");
 
            if (XmlILTrace.IsEnabled) {
                // Dump assembly to disk; can't do this when using LRE
                useLRE = false;
            } 

            // Optimize and annotate the Qil graph 
            this.optVisitor = new XmlILOptimizerVisitor(this.qil, !this.qil.IsDebug); 
            this.qil = this.optVisitor.Optimize();
 
            // In debug code, ensure that output QIL is correct
            QilValidationVisitor.Validate(this.qil);

            // Trace Qil after optimization 
            XmlILTrace.WriteQil(this.qil, "qilafter.xml");
 
            try { 
                XmlILModule.CreateModulePermissionSet.Assert();
 
                // Create module in which methods will be generated
                if (typeBldr != null)
                    this.module = new XmlILModule(typeBldr);
                else 
                    this.module = new XmlILModule(useLRE, emitSymbols);
 
                // Create a code generation helper for the module; enable optimizations if IsDebug is false 
                this.helper = new GenerateHelper(this.module, this.qil.IsDebug);
 
                // Create helper methods
                CreateHelperFunctions();

                // Create metadata for the Execute function, which is the entry point to the query 
                // public static void Execute(XmlQueryRuntime);
                methExec = this.module.DefineMethod("Execute", typeof(void), new Type[] {}, new string[] {}, XmlILMethodAttributes.NonUser); 
 
                // Create metadata for the root expression
                // public void Root() 
                Debug.Assert(this.qil.Root != null);
                methAttrs = (this.qil.Root.SourceLine == null) ? XmlILMethodAttributes.NonUser : XmlILMethodAttributes.None;
                methRoot = this.module.DefineMethod("Root", typeof(void), new Type[] {}, new string[] {}, methAttrs);
 
                // Declare all early bound function objects
                foreach (EarlyBoundInfo info in this.qil.EarlyBoundTypes) 
                    this.helper.StaticData.DeclareEarlyBound(info.NamespaceUri, info.EarlyBoundType); 

                // Create metadata for each QilExpression function that has at least one caller 
                CreateFunctionMetadata(this.qil.FunctionList);

                // Create metadata for each QilExpression global variable and parameter
                CreateGlobalValueMetadata(this.qil.GlobalVariableList); 
                CreateGlobalValueMetadata(this.qil.GlobalParameterList);
 
                // Generate Execute method 
                GenerateExecuteFunction(methExec, methRoot);
 
                // Visit the QilExpression graph
                this.xmlIlVisitor = new XmlILVisitor();
                this.xmlIlVisitor.Visit(this.qil, this.helper, methRoot);
 
                // Collect all static information required by the runtime
                staticData = new XmlQueryStaticData( 
                                    this.qil.DefaultWriterSettings, 
                                    this.qil.WhitespaceRules,
                                    this.helper.StaticData); 

                // Create static constructor that initializes XmlQueryStaticData instance at runtime
                if (typeBldr != null)
                    CreateTypeInitializer(staticData); 

 
                // Finish up creation of the type 
                this.module.BakeMethods();
 
                // Create delegate over "Execute" method
                delExec = (ExecuteDelegate) this.module.CreateDelegate("Execute", typeof(ExecuteDelegate));
                return new XmlILCommand(delExec, staticData);
            } 
            finally {
                CodeAccessPermission.RevertAssert(); 
            } 
        }
 
        /// 
        /// Create MethodBuilder metadata for the specified QilExpression function.  Annotate ndFunc with the
        /// MethodBuilder.  Also, each QilExpression argument type should be converted to a corresponding Clr type.
        /// Each argument QilExpression node should be annotated with the resulting ParameterBuilder. 
        /// 
        private void CreateFunctionMetadata(IList funcList) { 
            MethodInfo methInfo; 
            Type[] paramTypes;
            string[] paramNames; 
            Type typReturn;
            XmlILMethodAttributes methAttrs;

            foreach (QilFunction ndFunc in funcList) { 
                paramTypes = new Type[ndFunc.Arguments.Count];
                paramNames = new string[ndFunc.Arguments.Count]; 
 
                // Loop through all other parameters and save their types in the array
                for (int arg = 0; arg < ndFunc.Arguments.Count; arg ++) { 
                    QilParameter ndParam = (QilParameter) ndFunc.Arguments[arg];
                    Debug.Assert(ndParam.NodeType == QilNodeType.Parameter);

                    // Get the type of each argument as a Clr type 
                    paramTypes[arg] = XmlILTypeHelper.GetStorageType(ndParam.XmlType);
 
                    // Get the name of each argument 
                    if (ndParam.DebugName != null)
                        paramNames[arg] = ndParam.DebugName; 
                }

                // Get the type of the return value
                if (XmlILConstructInfo.Read(ndFunc).PushToWriterLast) { 
                    // Push mode functions do not have a return value
                    typReturn = typeof(void); 
                } 
                else {
                    // Pull mode functions have a return value 
                    typReturn = XmlILTypeHelper.GetStorageType(ndFunc.XmlType);
                }

                // Create the method metadata 
                methAttrs = ndFunc.SourceLine == null ? XmlILMethodAttributes.NonUser : XmlILMethodAttributes.None;
                methInfo = this.module.DefineMethod(ndFunc.DebugName, typReturn, paramTypes, paramNames, methAttrs); 
 
                for (int arg = 0; arg < ndFunc.Arguments.Count; arg ++) {
                    // Set location of parameter on Let node annotation 
                    XmlILAnnotation.Write(ndFunc.Arguments[arg]).ArgumentPosition = arg;
                }

                // Annotate function with the MethodInfo 
                XmlILAnnotation.Write(ndFunc).FunctionBinding = methInfo;
            } 
        } 

        ///  
        /// Generate metadata for a method that calculates a global value.
        /// 
        private void CreateGlobalValueMetadata(IList globalList) {
            MethodInfo methInfo; 
            Type typReturn;
            XmlILMethodAttributes methAttrs; 
 
            foreach (QilReference ndRef in globalList) {
                // public T GlobalValue() 
                typReturn = XmlILTypeHelper.GetStorageType(ndRef.XmlType);
                methAttrs = ndRef.SourceLine == null ? XmlILMethodAttributes.NonUser : XmlILMethodAttributes.None;
                methInfo = this.module.DefineMethod(ndRef.DebugName.ToString(), typReturn, new Type[] {}, new string[] {}, methAttrs);
 
                // Annotate function with MethodBuilder
                XmlILAnnotation.Write(ndRef).FunctionBinding = methInfo; 
            } 
        }
 
        /// 
        /// Generate the "Execute" method, which is the entry point to the query.
        /// 
        private MethodInfo GenerateExecuteFunction(MethodInfo methExec, MethodInfo methRoot) { 
            this.helper.MethodBegin(methExec, null, false);
 
            // Force some or all global values to be evaluated at start of query 
            EvaluateGlobalValues(this.qil.GlobalVariableList);
            EvaluateGlobalValues(this.qil.GlobalParameterList); 

            // Root(runtime);
            this.helper.LoadQueryRuntime();
            this.helper.Call(methRoot); 

            this.helper.MethodEnd(); 
 
            return methExec;
        } 

        /// 
        /// Create and generate various helper methods, which are called by the generated code.
        ///  
        private void CreateHelperFunctions() {
            MethodInfo meth; 
            Label lblClone; 

            // public static XPathNavigator SyncToNavigator(XPathNavigator, XPathNavigator); 
            meth = this.module.DefineMethod(
                            "SyncToNavigator",
                            typeof(XPathNavigator),
                            new Type[] {typeof(XPathNavigator), typeof(XPathNavigator)}, 
                            new string[] {null, null},
                            XmlILMethodAttributes.NonUser | XmlILMethodAttributes.Raw); 
 
            this.helper.MethodBegin(meth, null, false);
 
            // if (navigatorThis != null && navigatorThis.MoveTo(navigatorThat))
            //     return navigatorThis;
            lblClone = this.helper.DefineLabel();
            this.helper.Emit(OpCodes.Ldarg_0); 
            this.helper.Emit(OpCodes.Brfalse, lblClone);
            this.helper.Emit(OpCodes.Ldarg_0); 
            this.helper.Emit(OpCodes.Ldarg_1); 
            this.helper.Call(XmlILMethods.NavMoveTo);
            this.helper.Emit(OpCodes.Brfalse, lblClone); 
            this.helper.Emit(OpCodes.Ldarg_0);
            this.helper.Emit(OpCodes.Ret);

            // LabelClone: 
            // return navigatorThat.Clone();
            this.helper.MarkLabel(lblClone); 
            this.helper.Emit(OpCodes.Ldarg_1); 
            this.helper.Call(XmlILMethods.NavClone);
 
            this.helper.MethodEnd();
        }

        ///  
        /// Generate code to force evaluation of some or all global variables and/or parameters.
        ///  
        private void EvaluateGlobalValues(IList iterList) { 
            MethodInfo methInfo;
 
            foreach (QilIterator ndIter in iterList) {
                // Evaluate global if generating debug code, or if global might have side effects
                if (this.qil.IsDebug || OptimizerPatterns.Read(ndIter).MatchesPattern(OptimizerPatternName.MaybeSideEffects)) {
                    // Get MethodInfo that evaluates the global value and discard its return value 
                    methInfo = XmlILAnnotation.Write(ndIter).FunctionBinding;
                    Debug.Assert(methInfo != null, "MethodInfo for global value should have been created previously."); 
 
                    this.helper.LoadQueryRuntime();
                    this.helper.Call(methInfo); 
                    this.helper.Emit(OpCodes.Pop);
                }
            }
        } 

        ///  
        /// Create static constructor that initializes XmlQueryStaticData instance at runtime. 
        /// 
        public void CreateTypeInitializer(XmlQueryStaticData staticData) { 
            byte[] data;
            Type[] ebTypes;
            FieldInfo fldInitData, fldData, fldTypes;
            ConstructorInfo cctor; 

            staticData.GetObjectData(out data, out ebTypes); 
            fldInitData = this.module.DefineInitializedData("__" + XmlQueryStaticData.DataFieldName, data); 
            fldData = this.module.DefineField(XmlQueryStaticData.DataFieldName, typeof(object));
            fldTypes = this.module.DefineField(XmlQueryStaticData.TypesFieldName, typeof(Type[])); 

            cctor = this.module.DefineTypeInitializer();
            this.helper.MethodBegin(cctor, null, false);
 
            // s_data = new byte[s_initData.Length] { s_initData };
            this.helper.LoadInteger(data.Length); 
            this.helper.Emit(OpCodes.Newarr, typeof(byte)); 
            this.helper.Emit(OpCodes.Dup);
            this.helper.Emit(OpCodes.Ldtoken, fldInitData); 
            this.helper.Call(XmlILMethods.InitializeArray);
            this.helper.Emit(OpCodes.Stsfld, fldData);

            if (ebTypes != null) { 
                // Type[] types = new Type[s_ebTypes.Length];
                LocalBuilder locTypes = this.helper.DeclareLocal("$$$types", typeof(Type[])); 
                this.helper.LoadInteger(ebTypes.Length); 
                this.helper.Emit(OpCodes.Newarr, typeof(Type));
                this.helper.Emit(OpCodes.Stloc, locTypes); 

                for (int idx = 0; idx < ebTypes.Length; idx++) {
                    // types[idx] = ebTypes[idx];
                    this.helper.Emit(OpCodes.Ldloc, locTypes); 
                    this.helper.LoadInteger(idx);
                    this.helper.LoadType(ebTypes[idx]); 
                    this.helper.Emit(OpCodes.Stelem_Ref); 
                }
 
                // s_types = types;
                this.helper.Emit(OpCodes.Ldloc, locTypes);
                this.helper.Emit(OpCodes.Stsfld, fldTypes);
            } 

            this.helper.MethodEnd(); 
        } 
    }
 

    /// 
    /// This is the executable command generated by the XmlILGenerator.
    ///  
    internal class XmlILCommand : XmlCommand {
        private ExecuteDelegate delExec; 
        private XmlQueryStaticData staticData; 

        ///  
        /// Constructor.
        /// 
        public XmlILCommand(ExecuteDelegate delExec, XmlQueryStaticData staticData) {
            Debug.Assert(delExec != null && staticData != null); 
            this.delExec = delExec;
            this.staticData = staticData; 
        } 

        ///  
        /// Return execute delegate.
        /// 
        public ExecuteDelegate ExecuteDelegate {
            get { return delExec; } 
        }
 
        ///  
        /// Return query static data required by the runtime.
        ///  
        public XmlQueryStaticData StaticData {
            get { return staticData; }
        }
 
        /// 
        /// Default document as XPathNavigator. 
        ///  
        public override void Execute(IXPathNavigable contextDocument, XmlResolver dataSources, XsltArgumentList argumentList, XmlWriter results) {
            if (results == null) 
                throw new ArgumentNullException("results");

            if (contextDocument != null)
                Execute(contextDocument.CreateNavigator(), dataSources, argumentList, results, false); 
            else
                Execute(null, dataSources, argumentList, results, false); 
        } 

        ///  
        /// Default document as XPathNavigator.
        /// 
        public override void Execute(IXPathNavigable contextDocument, XmlResolver dataSources, XsltArgumentList argumentList, TextWriter results) {
            if (results == null) 
                throw new ArgumentNullException("results");
 
            Execute(contextDocument, dataSources, argumentList, XmlWriter.Create(results, this.staticData.DefaultWriterSettings)); 
        }
 
        /// 
        /// Default document as XPathNavigator.
        /// 
        public override void Execute(IXPathNavigable contextDocument, XmlResolver dataSources, XsltArgumentList argumentList, Stream results) { 
            if (results == null)
                throw new ArgumentNullException("results"); 
 
            Execute(contextDocument, dataSources, argumentList, XmlWriter.Create(results, this.staticData.DefaultWriterSettings));
        } 

        /// 
        /// Executes the query by accessing datasources via the XmlResolver and using run-time parameters
        /// as provided by the XsltArgumentList. The default document is mapped into the XmlResolver with the 
        /// provided name. The results are output to the provided XmlWriter.
        ///  
        public void Execute(string contextDocumentUri, XmlResolver dataSources, XsltArgumentList argumentList, XmlWriter results) { 
            if (results == null)
                throw new ArgumentNullException("results"); 

            Execute(contextDocumentUri, dataSources, argumentList, results, false);
        }
 
        /// 
        /// Executes the query by accessing datasources via the XmlResolver and using 
        /// run-time parameters as provided by the XsltArgumentList. The default document 
        /// is mapped into the XmlResolver with the provided name. The results are returned
        /// as an IList. 
        /// 
        public IList Evaluate(string contextDocumentUri, XmlResolver dataSources, XsltArgumentList argumentList) {
            XmlCachedSequenceWriter seqwrt = new XmlCachedSequenceWriter();
            Execute(contextDocumentUri, dataSources, argumentList, seqwrt); 
            return seqwrt.ResultSequence;
        } 
 
        /// 
        /// Executes the query by accessing datasources via the XmlResolver and using run-time parameters 
        /// as provided by the XsltArgumentList. The default document is mapped into the XmlResolver with the
        /// provided name. The results are output to the provided XmlWriter.
        /// 
        public override void Execute(XmlReader contextDocument, XmlResolver dataSources, XsltArgumentList argumentList, XmlWriter results) { 
            if (results == null)
                throw new ArgumentNullException("results"); 
 
            Execute(contextDocument, dataSources, argumentList, results, false);
        } 

        /// 
        /// Executes the query by accessing datasources via the XmlResolver and using run-time parameters
        /// as provided by the XsltArgumentList. The default document is mapped into the XmlResolver with the 
        /// provided name. The results are output to the provided TextWriter.
        ///  
        public override void Execute(XmlReader contextDocument, XmlResolver dataSources, XsltArgumentList argumentList, TextWriter results) { 
            if (results == null)
                throw new ArgumentNullException("results"); 

            Execute(contextDocument, dataSources, argumentList, XmlWriter.Create(results, this.staticData.DefaultWriterSettings), true);
        }
 
        /// 
        /// Executes the query by accessing datasources via the XmlResolver and using run-time parameters 
        /// as provided by the XsltArgumentList. The default document is mapped into the XmlResolver with the 
        /// provided name. The results are output to the provided Stream.
        ///  
        public override void Execute(XmlReader contextDocument, XmlResolver dataSources, XsltArgumentList argumentList, Stream results) {
            if (results == null)
                throw new ArgumentNullException("results");
 
            Execute(contextDocument, dataSources, argumentList, XmlWriter.Create(results, this.staticData.DefaultWriterSettings), true);
        } 
 
        /// 
        /// Executes the query by accessing datasources via the XmlResolver and using 
        /// run-time parameters as provided by the XsltArgumentList. The default document
        /// is mapped into the XmlResolver with the provided name. The results are returned
        /// as an IList.
        ///  
        public override IList Evaluate(XmlReader contextDocument, XmlResolver dataSources, XsltArgumentList argumentList) {
            XmlCachedSequenceWriter seqwrt = new XmlCachedSequenceWriter(); 
            Execute(contextDocument, dataSources, argumentList, seqwrt); 
            return seqwrt.ResultSequence;
        } 

        /// 
        /// Execute the dynamic assembly generated by the XmlILGenerator.
        ///  
        private void Execute(object defaultDocument, XmlResolver dataSources, XsltArgumentList argumentList, XmlWriter writer, bool closeWriter) {
            try { 
                // Try to extract a RawWriter 
                XmlWellFormedWriter wellFormedWriter = writer as XmlWellFormedWriter;
 
                if (wellFormedWriter != null &&
                    wellFormedWriter.WriteState == WriteState.Start &&
                    wellFormedWriter.Settings.ConformanceLevel != ConformanceLevel.Document) {
 
                    // Extracted RawWriter from WellFormedWriter
                    Execute(defaultDocument, dataSources, argumentList, new XmlMergeSequenceWriter(wellFormedWriter.RawWriter)); 
                } 
                else {
                    // Wrap Writer in RawWriter 
                    Execute(defaultDocument, dataSources, argumentList, new XmlMergeSequenceWriter(new XmlRawWriterWrapper(writer)));
                }
            }
            finally { 
                // Close Writers that are created by XmlILGenerator; flush external writers
                if (closeWriter) 
                    writer.Close(); 
                else
                    writer.Flush(); 
            }
        }

        ///  
        /// Execute the dynamic assembly generated by the XmlILGenerator.
        ///  
        private void Execute(object defaultDocument, XmlResolver dataSources, XsltArgumentList argumentList, XmlSequenceWriter results) { 
            Debug.Assert(results != null);
 
            // Ensure that dataSources is always non-null
            if (dataSources == null)
                dataSources = XmlNullResolver.Singleton;
 
            this.delExec(new XmlQueryRuntime(this.staticData, defaultDocument, dataSources, argumentList, results));
        } 
    } 
}

// File provided for Reference Use Only by Microsoft Corporation (c) 2007.

                        

Link Menu

Network programming in C#, Network Programming in VB.NET, Network Programming in .NET
This book is available now!
Buy at Amazon US or
Buy at Amazon UK