XmlIlGenerator.cs source code in C# .NET

Source code for the .NET framework in C#



/ 4.0 / 4.0 / untmp / DEVDIV_TFS / Dev10 / Releases / RTMRel / ndp / fx / src / XmlUtils / System / Xml / Xsl / XmlIlGenerator.cs / 1305376 / 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; 
using System.Runtime.Versioning;

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. 
        // SxS Note: The way the trace file names are created (hardcoded) is NOT SxS safe. However the files are
        // created only for internal tracing purposes. In addition XmlILTrace class is not compiled into retail 
        // builds. As a result it is fine to suppress the FxCop SxS warning.
        [ResourceConsumption(ResourceScope.Machine, ResourceScope.Machine)]
        public XmlILCommand Generate(QilExpression query, TypeBuilder typeBldr) { 
            this.qil = query;
            bool useLRE = ( 
                !this.qil.IsDebug &&
                (typeBldr == null) 
                && !XmlILTrace.IsEnabled // Dump assembly to disk; can't do this when using LRE
            bool emitSymbols = this.qil.IsDebug;
            // In debug code, ensure that input QIL is correct 
        #if DEBUG
            // Trace Qil before optimization
            XmlILTrace.WriteQil(this.qil, "qilbefore.xml");
            // Trace optimizations
            XmlILTrace.TraceOptimizations(this.qil, "qilopt.xml"); 

            // 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 
        #if DEBUG 
            // Trace Qil after optimization
            XmlILTrace.WriteQil(this.qil, "qilafter.xml"); 

            // 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 

            // Create metadata for the Execute function, which is the entry point to the query 
            // public static void Execute(XmlQueryRuntime);
            MethodInfo 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); 
            XmlILMethodAttributes methAttrs = (this.qil.Root.SourceLine == null) ? XmlILMethodAttributes.NonUser : XmlILMethodAttributes.None; 
            MethodInfo 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 

            // Create metadata for each QilExpression global variable and parameter 

            // 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
            XmlQueryStaticData staticData = new XmlQueryStaticData(

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

                // Finish up creation of the type 
                return null; 
            } else {
                // Finish up creation of the type 

                // Create delegate over "Execute" method
                ExecuteDelegate delExec = (ExecuteDelegate)this.module.CreateDelegate("Execute", typeof(ExecuteDelegate)); 
                return new XmlILCommand(delExec, staticData);

        /// 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 
            // Root(runtime);
            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( 
                            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.Brfalse, lblClone); 
            this.helper.Emit(OpCodes.Brfalse, lblClone);

            // LabelClone:
            // return navigatorThat.Clone(); 


        /// 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.");
        /// 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.Emit(OpCodes.Newarr, typeof(byte)); 
            this.helper.Emit(OpCodes.Ldtoken, fldInitData);
            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.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);

                // s_types = types; 
                this.helper.Emit(OpCodes.Ldloc, locTypes);
                this.helper.Emit(OpCodes.Stsfld, fldTypes);

// 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