The Labs \ Source Viewer \ SSCLI \ System.Xml.Xsl.IlGen \ ItemLocation

  1. //------------------------------------------------------------------------------
  2. // <copyright file="IteratorDescriptor.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. //------------------------------------------------------------------------------
  15. using System;
  16. using System.Collections;
  17. using System.Collections.Generic;
  18. using System.Xml;
  19. using System.Xml.XPath;
  20. using System.Xml.Schema;
  21. using System.Globalization;
  22. using System.Diagnostics;
  23. using System.Reflection;
  24. using System.Reflection.Emit;
  25. using System.Xml.Xsl.Runtime;
  26. namespace System.Xml.Xsl.IlGen
  27. {
  28.    
  29.     /// <summary>
  30.     /// Type of location in which iterator items are stored.
  31.     /// </summary>
  32.     internal enum ItemLocation
  33.     {
  34.         None = 0,
  35.         Stack,
  36.         // Each value is stored as the top value on the IL stack
  37.         Parameter,
  38.         // Each value is stored as a parameter to the current method
  39.         Local,
  40.         // Each value is stored as a local variable in the current method
  41.         Current,
  42.         // Each value is stored as an iterator's Current property
  43.         Global
  44.         // Each value is stored as a global variable
  45.     }
  46.    
  47.    
  48.     /// <summary>
  49.     /// None--Not in a branching context
  50.     /// True--Branch if boolean expression evaluates to true
  51.     /// False--Branch if boolean expression evaluates to false
  52.     /// </summary>
  53.     internal enum BranchingContext
  54.     {
  55.         None,
  56.         OnTrue,
  57.         OnFalse
  58.     }
  59.    
  60.     /// <summary>
  61.     /// Describes the Clr type and location of items returned by an iterator.
  62.     /// This struct is meant to be immutable.
  63.     /// </summary>
  64.     internal struct StorageDescriptor
  65.     {
  66.         private ItemLocation location;
  67.         private object locationObject;
  68.         private Type itemStorageType;
  69.         private bool isCached;
  70.        
  71.        
  72.         //-----------------------------------------------
  73.         // Create Methods
  74.         //-----------------------------------------------
  75.        
  76.         /// <summary>
  77.         /// Create default, empty StorageDescriptor.
  78.         /// </summary>
  79.         public static StorageDescriptor None()
  80.         {
  81.             return new StorageDescriptor();
  82.         }
  83.        
  84.         /// <summary>
  85.         /// Create a StorageDescriptor for an item located on the stack.
  86.         /// </summary>
  87.         public static StorageDescriptor Stack(Type itemStorageType, bool isCached)
  88.         {
  89.             StorageDescriptor storage = new StorageDescriptor();
  90.             storage.location = ItemLocation.Stack;
  91.             storage.itemStorageType = itemStorageType;
  92.             storage.isCached = isCached;
  93.             return storage;
  94.         }
  95.        
  96.         /// <summary>
  97.         /// Create a StorageDescriptor for an item which is a parameter to the current method.
  98.         /// </summary>
  99.         public static StorageDescriptor Parameter(int paramIndex, Type itemStorageType, bool isCached)
  100.         {
  101.             StorageDescriptor storage = new StorageDescriptor();
  102.             storage.location = ItemLocation.Parameter;
  103.             storage.locationObject = paramIndex;
  104.             storage.itemStorageType = itemStorageType;
  105.             storage.isCached = isCached;
  106.             return storage;
  107.         }
  108.        
  109.         /// <summary>
  110.         /// Create a StorageDescriptor for an item located in a local variable.
  111.         /// </summary>
  112.         public static StorageDescriptor Local(LocalBuilder loc, Type itemStorageType, bool isCached)
  113.         {
  114.             Debug.Assert(loc.LocalType == itemStorageType || typeof(IList<>).MakeGenericType(itemStorageType).IsAssignableFrom(loc.LocalType), "Type " + itemStorageType + " does not match the local variable's type");
  115.            
  116.             StorageDescriptor storage = new StorageDescriptor();
  117.             storage.location = ItemLocation.Local;
  118.             storage.locationObject = loc;
  119.             storage.itemStorageType = itemStorageType;
  120.             storage.isCached = isCached;
  121.             return storage;
  122.         }
  123.        
  124.         /// <summary>
  125.         /// Create a StorageDescriptor for an item which is the Current item in an iterator.
  126.         /// </summary>
  127.         public static StorageDescriptor Current(LocalBuilder locIter, Type itemStorageType)
  128.         {
  129.             Debug.Assert(locIter.LocalType.GetMethod("get_Current").ReturnType == itemStorageType, "Type " + itemStorageType + " does not match type of Current property.");
  130.            
  131.             StorageDescriptor storage = new StorageDescriptor();
  132.             storage.location = ItemLocation.Current;
  133.             storage.locationObject = locIter;
  134.             storage.itemStorageType = itemStorageType;
  135.             return storage;
  136.         }
  137.        
  138.         /// <summary>
  139.         /// Create a StorageDescriptor for an item located in a global variable.
  140.         /// </summary>
  141.         public static StorageDescriptor Global(MethodInfo methGlobal, Type itemStorageType, bool isCached)
  142.         {
  143.             Debug.Assert(methGlobal.ReturnType == itemStorageType || typeof(IList<>).MakeGenericType(itemStorageType).IsAssignableFrom(methGlobal.ReturnType), "Type " + itemStorageType + " does not match the global method's return type");
  144.            
  145.             StorageDescriptor storage = new StorageDescriptor();
  146.             storage.location = ItemLocation.Global;
  147.             storage.locationObject = methGlobal;
  148.             storage.itemStorageType = itemStorageType;
  149.             storage.isCached = isCached;
  150.             return storage;
  151.         }
  152.        
  153.        
  154.         //-----------------------------------------------
  155.         // Accessor Methods
  156.         //-----------------------------------------------
  157.        
  158.         /// <summary>
  159.         /// Return copy of current descriptor, but change item's location to the stack.
  160.         /// </summary>
  161.         public StorageDescriptor ToStack()
  162.         {
  163.             return Stack(this.itemStorageType, this.isCached);
  164.         }
  165.        
  166.         /// <summary>
  167.         /// Create a StorageDescriptor for an item located in a local variable.
  168.         /// </summary>
  169.         public StorageDescriptor ToLocal(LocalBuilder loc)
  170.         {
  171.             return Local(loc, this.itemStorageType, this.isCached);
  172.         }
  173.        
  174.         /// <summary>
  175.         /// Create a StorageDescriptor which is the same as this one, except for the item storage type.
  176.         /// </summary>
  177.         public StorageDescriptor ToStorageType(Type itemStorageType)
  178.         {
  179.             StorageDescriptor storage = this;
  180.             storage.itemStorageType = itemStorageType;
  181.             return storage;
  182.         }
  183.        
  184.         /// <summary>
  185.         /// Return an enumeration specifying where the value is located.
  186.         /// </summary>
  187.         public ItemLocation Location {
  188.             get { return this.location; }
  189.         }
  190.        
  191.         /// <summary>
  192.         /// Return the index of the parameter that stores this iterator's values.
  193.         /// </summary>
  194.         public int ParameterLocation {
  195.             get { return (int)this.locationObject; }
  196.         }
  197.        
  198.         /// <summary>
  199.         /// Return the LocalBuilder that stores this iterator's values.
  200.         /// </summary>
  201.         public LocalBuilder LocalLocation {
  202.             get { return this.locationObject as LocalBuilder; }
  203.         }
  204.        
  205.         /// <summary>
  206.         /// Return the LocalBuilder that will store this iterator's helper class. The Current property
  207.         /// on this iterator can be accessed to get the current iteration value.
  208.         /// </summary>
  209.         public LocalBuilder CurrentLocation {
  210.             get { return this.locationObject as LocalBuilder; }
  211.         }
  212.        
  213.         /// <summary>
  214.         /// Return the MethodInfo for the method that computes this global value.
  215.         /// </summary>
  216.         public MethodInfo GlobalLocation {
  217.             get { return this.locationObject as MethodInfo; }
  218.         }
  219.        
  220.         /// <summary>
  221.         /// Return true if this iterator's values are cached.
  222.         /// </summary>
  223.         public bool IsCached {
  224.             get { return this.isCached; }
  225.         }
  226.        
  227.         /// <summary>
  228.         /// Return the Clr type of an individual item in the storage location (never an IList<> type).
  229.         /// </summary>
  230.         public Type ItemStorageType {
  231.             get { return this.itemStorageType; }
  232.         }
  233.     }
  234.    
  235.     /// <summary>
  236.     /// Iterators are joined together, are nested within each other, and reference each other. This internal class
  237.     /// contains detailed information about iteration next labels, caching, iterator item location, etc.
  238.     /// </summary>
  239.     internal class IteratorDescriptor
  240.     {
  241.         private GenerateHelper helper;
  242.        
  243.         // Related iterators
  244.         private IteratorDescriptor iterParent;
  245.        
  246.         // Iteration
  247.         private Label lblNext;
  248.         private bool hasNext;
  249.         private LocalBuilder locPos;
  250.        
  251.         // Branching
  252.         private BranchingContext brctxt;
  253.         private Label lblBranch;
  254.        
  255.         // Storage
  256.         private StorageDescriptor storage;
  257.        
  258.        
  259.         //-----------------------------------------------
  260.         // Initialize
  261.         //-----------------------------------------------
  262.        
  263.         /// <summary>
  264.         /// Create a "root" IteratorDescriptor which has no parent iterator.
  265.         /// </summary>
  266.         public IteratorDescriptor(GenerateHelper helper)
  267.         {
  268.             Init(null, helper);
  269.         }
  270.        
  271.         /// <summary>
  272.         /// Create an IteratorDescriptor that is nested in a parent iterator.
  273.         /// </summary>
  274.         public IteratorDescriptor(IteratorDescriptor iterParent)
  275.         {
  276.             Init(iterParent, iterParent.helper);
  277.         }
  278.        
  279.         /// <summary>
  280.         /// Internal helper initializor.
  281.         /// </summary>
  282.         private void Init(IteratorDescriptor iterParent, GenerateHelper helper)
  283.         {
  284.             this.helper = helper;
  285.             this.iterParent = iterParent;
  286.         }
  287.        
  288.        
  289.         //-----------------------------------------------
  290.         // Related Iterators
  291.         //-----------------------------------------------
  292.        
  293.         /// <summary>
  294.         /// Return the iterator in which this iterator is nested.
  295.         /// </summary>
  296.         public IteratorDescriptor ParentIterator {
  297.             get { return this.iterParent; }
  298.         }
  299.        
  300.        
  301.         //-----------------------------------------------
  302.         // Iteration
  303.         //-----------------------------------------------
  304.        
  305.         /// <summary>
  306.         /// Returns true if LabelNext is currently defined. If not, then this iterator will return
  307.         /// exactly one result (iterator is cardinality one).
  308.         /// </summary>
  309.         public bool HasLabelNext {
  310.             get { return this.hasNext; }
  311.         }
  312.        
  313.         /// <summary>
  314.         /// Return the label that is anchored to this code iterator's MoveNext code.
  315.         /// </summary>
  316.         public Label GetLabelNext()
  317.         {
  318.             Debug.Assert(this.hasNext);
  319.             return this.lblNext;
  320.         }
  321.        
  322.         /// <summary>
  323.         /// Set this iterator's next label and storage. This iterator will range over a set of values located in
  324.         /// "storage". To get the next value, jump to "lblNext".
  325.         /// </summary>
  326.         public void SetIterator(Label lblNext, StorageDescriptor storage)
  327.         {
  328.             this.lblNext = lblNext;
  329.             this.hasNext = true;
  330.             this.storage = storage;
  331.         }
  332.        
  333.         /// <summary>
  334.         /// Set this iterator to be the same as the specified iterator.
  335.         /// </summary>
  336.         public void SetIterator(IteratorDescriptor iterInfo)
  337.         {
  338.             if (iterInfo.HasLabelNext) {
  339.                 this.lblNext = iterInfo.GetLabelNext();
  340.                 this.hasNext = true;
  341.             }
  342.            
  343.             this.storage = iterInfo.Storage;
  344.         }
  345.        
  346.         /// <summary>
  347.         /// Continue iteration until it is complete. Branch to "lblOnEnd" when iteration is complete.
  348.         /// </summary>
  349.         /// <remarks>
  350.         /// goto LabelNextCtxt;
  351.         /// LabelOnEnd:
  352.         /// </remarks>
  353.         public void LoopToEnd(Label lblOnEnd)
  354.         {
  355.             if (this.hasNext) {
  356.                 this.helper.BranchAndMark(this.lblNext, lblOnEnd);
  357.                 this.hasNext = false;
  358.             }
  359.            
  360.             // After looping is finished, storage is N/A
  361.             this.storage = StorageDescriptor.None();
  362.         }
  363.        
  364.         /// <summary>
  365.         /// Storage location containing the position of the current item as an integer.
  366.         /// This location is only defined on iterators, and then only if they might be
  367.         /// referenced by a PositionOf operator.
  368.         /// </summary>
  369.         public LocalBuilder LocalPosition {
  370.             get { return this.locPos; }
  371.             set { this.locPos = value; }
  372.         }
  373.        
  374.        
  375.         //-----------------------------------------------
  376.         // Caching
  377.         //-----------------------------------------------
  378.        
  379.         /// <summary>
  380.         /// Push the count of items in the cache onto the stack.
  381.         /// </summary>
  382.         public void CacheCount()
  383.         {
  384.             Debug.Assert(this.storage.IsCached);
  385.             PushValue();
  386.             this.helper.CallCacheCount(this.storage.ItemStorageType);
  387.         }
  388.        
  389.         /// <summary>
  390.         /// If the iterator has been fully cached, then iterate the values one-by-one.
  391.         /// </summary>
  392.         public void EnsureNoCache()
  393.         {
  394.             if (this.storage.IsCached) {
  395.                 if (!HasLabelNext) {
  396.                     // If no Next label, this must be a singleton cache
  397.                     EnsureStack();
  398.                     this.helper.LoadInteger(0);
  399.                     this.helper.CallCacheItem(this.storage.ItemStorageType);
  400.                    
  401.                     this.storage = StorageDescriptor.Stack(this.storage.ItemStorageType, false);
  402.                 }
  403.                 else {
  404.                     // int idx;
  405.                     LocalBuilder locIdx = this.helper.DeclareLocal("$$$idx", typeof(int));
  406.                     Label lblNext;
  407.                    
  408.                     // Make sure cache is not on the stack
  409.                     EnsureNoStack("$$$cache");
  410.                    
  411.                     // idx = -1;
  412.                     this.helper.LoadInteger(-1);
  413.                     this.helper.Emit(OpCodes.Stloc, locIdx);
  414.                    
  415.                     // LabelNext:
  416.                     lblNext = this.helper.DefineLabel();
  417.                     this.helper.MarkLabel(lblNext);
  418.                    
  419.                     // idx++;
  420.                     this.helper.Emit(OpCodes.Ldloc, locIdx);
  421.                     this.helper.LoadInteger(1);
  422.                     this.helper.Emit(OpCodes.Add);
  423.                     this.helper.Emit(OpCodes.Stloc, locIdx);
  424.                    
  425.                     // if (idx >= cache.Count) goto LabelNextCtxt;
  426.                     this.helper.Emit(OpCodes.Ldloc, locIdx);
  427.                     CacheCount();
  428.                     this.helper.Emit(OpCodes.Bge, GetLabelNext());
  429.                    
  430.                     // item = cache[idx];
  431.                     PushValue();
  432.                     this.helper.Emit(OpCodes.Ldloc, locIdx);
  433.                     this.helper.CallCacheItem(this.storage.ItemStorageType);
  434.                    
  435.                     SetIterator(lblNext, StorageDescriptor.Stack(this.storage.ItemStorageType, false));
  436.                 }
  437.             }
  438.         }
  439.        
  440.        
  441.         //-----------------------------------------------
  442.         // If-then-else branching
  443.         //-----------------------------------------------
  444.        
  445.         /// <summary>
  446.         /// Setup a branching context. All nested iterators compiled in this context must evaluate
  447.         /// to a single boolean value. However, these expressions must not push the result as a boolean
  448.         /// onto the stack. Instead, if brctxt is BranchType.True, then the expression should
  449.         /// jump to lblBranch if it evaluates to true. If brctxt is BranchType.False, then the
  450.         /// branch should happen if the evaluation result is false.
  451.         /// </summary>
  452.         public void SetBranching(BranchingContext brctxt, Label lblBranch)
  453.         {
  454.             Debug.Assert(brctxt != BranchingContext.None);
  455.             this.brctxt = brctxt;
  456.             this.lblBranch = lblBranch;
  457.         }
  458.        
  459.         /// <summary>
  460.         /// True if within a branching context.
  461.         /// </summary>
  462.         public bool IsBranching {
  463.             get { return this.brctxt != BranchingContext.None; }
  464.         }
  465.        
  466.         /// <summary>
  467.         /// Returns the label to which conditionals should branch.
  468.         /// </summary>
  469.         public Label LabelBranch {
  470.             get { return this.lblBranch; }
  471.         }
  472.        
  473.         /// <summary>
  474.         /// If BranchingContext.OnTrue, branch on true. Otherwise, branch on false.
  475.         /// </summary>
  476.         public BranchingContext CurrentBranchingContext {
  477.             get { return this.brctxt; }
  478.         }
  479.        
  480.        
  481.         //-----------------------------------------------
  482.         // Storage
  483.         //-----------------------------------------------
  484.        
  485.         /// <summary>
  486.         /// Returns information about how and where iterator values are stored.
  487.         /// </summary>
  488.         public StorageDescriptor Storage {
  489.             get { return this.storage; }
  490.             set { this.storage = value; }
  491.         }
  492.        
  493.         /// <summary>
  494.         /// Push current item onto the stack without affecting Location.
  495.         /// </summary>
  496.         public void PushValue()
  497.         {
  498.             switch (this.storage.Location) {
  499.                 case ItemLocation.Stack:
  500.                     this.helper.Emit(OpCodes.Dup);
  501.                     break;
  502.                 case ItemLocation.Parameter:
  503.                    
  504.                     this.helper.LoadParameter(this.storage.ParameterLocation);
  505.                     break;
  506.                 case ItemLocation.Local:
  507.                    
  508.                     this.helper.Emit(OpCodes.Ldloc, this.storage.LocalLocation);
  509.                     break;
  510.                 case ItemLocation.Current:
  511.                    
  512.                     this.helper.Emit(OpCodes.Ldloca, this.storage.CurrentLocation);
  513.                     this.helper.Call(this.storage.CurrentLocation.LocalType.GetMethod("get_Current"));
  514.                     break;
  515.                 default:
  516.                    
  517.                     Debug.Assert(false, "Invalid location: " + this.storage.Location);
  518.                     break;
  519.             }
  520.         }
  521.        
  522.         /// <summary>
  523.         /// Ensure that the current item is pushed onto the stack.
  524.         /// </summary>
  525.         public void EnsureStack()
  526.         {
  527.             switch (this.storage.Location) {
  528.                 case ItemLocation.Stack:
  529.                     // Already on the stack
  530.                     return;
  531.                 case ItemLocation.Parameter:
  532.                 case ItemLocation.Local:
  533.                 case ItemLocation.Current:
  534.                    
  535.                     PushValue();
  536.                     break;
  537.                 case ItemLocation.Global:
  538.                    
  539.                     // Call method that computes the value of this global value
  540.                     this.helper.LoadQueryRuntime();
  541.                     this.helper.Call(this.storage.GlobalLocation);
  542.                     break;
  543.                 default:
  544.                    
  545.                     Debug.Assert(false, "Invalid location: " + this.storage.Location);
  546.                     break;
  547.             }
  548.            
  549.             this.storage = this.storage.ToStack();
  550.         }
  551.        
  552.         /// <summary>
  553.         /// If the current item is on the stack, move it to a local variable.
  554.         /// </summary>
  555.         public void EnsureNoStack(string locName)
  556.         {
  557.             if (this.storage.Location == ItemLocation.Stack)
  558.                 EnsureLocal(locName);
  559.         }
  560.        
  561.         /// <summary>
  562.         /// If current item is not already in a local variable, then move it to a local variable of the specified name.
  563.         /// </summary>
  564.         public void EnsureLocal(string locName)
  565.         {
  566.             if (this.storage.Location != ItemLocation.Local) {
  567.                 if (this.storage.IsCached)
  568.                     EnsureLocal(this.helper.DeclareLocal(locName, typeof(IList<>).MakeGenericType(this.storage.ItemStorageType)));
  569.                 else
  570.                     EnsureLocal(this.helper.DeclareLocal(locName, this.storage.ItemStorageType));
  571.             }
  572.         }
  573.        
  574.         /// <summary>
  575.         /// Ensure that current item is saved to the specified local variable.
  576.         /// </summary>
  577.         public void EnsureLocal(LocalBuilder bldr)
  578.         {
  579.             if (this.storage.LocalLocation != bldr) {
  580.                 // Push value onto stack and then save to bldr
  581.                 EnsureStack();
  582.                 this.helper.Emit(OpCodes.Stloc, bldr);
  583.                 this.storage = this.storage.ToLocal(bldr);
  584.             }
  585.         }
  586.        
  587.         /// <summary>
  588.         /// Discard the current item if it is pushed onto the stack.
  589.         /// </summary>
  590.         public void DiscardStack()
  591.         {
  592.             if (this.storage.Location == ItemLocation.Stack) {
  593.                 this.helper.Emit(OpCodes.Pop);
  594.                 this.storage = StorageDescriptor.None();
  595.             }
  596.         }
  597.        
  598.         /// <summary>
  599.         /// Ensure that the iterator's items are not cached, and that the current item is pushed onto the stack.
  600.         /// </summary>
  601.         public void EnsureStackNoCache()
  602.         {
  603.             EnsureNoCache();
  604.             EnsureStack();
  605.         }
  606.        
  607.         /// <summary>
  608.         /// Ensure that the iterator's items are not cached, and that if the current item is pushed onto the stack,
  609.         /// that it is moved to a local variable.
  610.         /// </summary>
  611.         public void EnsureNoStackNoCache(string locName)
  612.         {
  613.             EnsureNoCache();
  614.             EnsureNoStack(locName);
  615.         }
  616.        
  617.         /// <summary>
  618.         /// Ensure that the iterator's items are not cached, and that if the current item is not already in a local,
  619.         /// variable, that it is moved to a local variable of the specified name.
  620.         /// </summary>
  621.         public void EnsureLocalNoCache(string locName)
  622.         {
  623.             EnsureNoCache();
  624.             EnsureLocal(locName);
  625.         }
  626.        
  627.         /// <summary>
  628.         /// Ensure that the iterator's items are not cached and that the current item is saved to the specified local variable.
  629.         /// </summary>
  630.         public void EnsureLocalNoCache(LocalBuilder bldr)
  631.         {
  632.             EnsureNoCache();
  633.             EnsureLocal(bldr);
  634.         }
  635.        
  636.         /// <summary>
  637.         /// Each XmlQueryType has multiple legal CLR representations. Ensure that all items returned by this iterator are in
  638.         /// the Clr representation specified by "storageTypeDest".
  639.         /// </summary>
  640.         public void EnsureItemStorageType(XmlQueryType xmlType, Type storageTypeDest)
  641.         {
  642.             // If source type = destination type, then done
  643.             if (this.storage.ItemStorageType == storageTypeDest)
  644.                 goto SetStorageType;
  645.            
  646.             Debug.Assert(this.storage.ItemStorageType == typeof(XPathItem) || storageTypeDest == typeof(XPathItem), "EnsureItemStorageType must convert to or from Item");
  647.            
  648.             // If items are cached,
  649.             if (this.storage.IsCached) {
  650.                 // Check for special case of IList<XPathNavigator> -> IList<XPathItem>
  651.                 if (this.storage.ItemStorageType == typeof(XPathNavigator)) {
  652.                     EnsureStack();
  653.                     this.helper.Call(XmlILMethods.NavsToItems);
  654.                     goto SetStorageType;
  655.                 }
  656.                
  657.                 // Check for special case of IList<XPathItem> -> IList<XPathNavigator>
  658.                 if (storageTypeDest == typeof(XPathNavigator)) {
  659.                     EnsureStack();
  660.                     this.helper.Call(XmlILMethods.ItemsToNavs);
  661.                     goto SetStorageType;
  662.                 }
  663.             }
  664.            
  665.             // Iterate over each item, and convert each to the destination type
  666.             EnsureStackNoCache();
  667.            
  668.             // If source type is Item,
  669.             if (this.storage.ItemStorageType == typeof(XPathItem)) {
  670.                 // Then downcast to Navigator
  671.                 if (storageTypeDest == typeof(XPathNavigator)) {
  672.                     this.helper.Emit(OpCodes.Castclass, typeof(XPathNavigator));
  673.                 }
  674.                 else {
  675.                     // Call ValueAs methods for atomic types
  676.                     this.helper.CallValueAs(storageTypeDest);
  677.                 }
  678.                 goto SetStorageType;
  679.             }
  680.             else if (this.storage.ItemStorageType == typeof(XPathNavigator)) {
  681.                 // No-op if converting from XPathNavigator to XPathItem
  682.                 Debug.Assert(storageTypeDest == typeof(XPathItem), "Must be converting from XPathNavigator to XPathItem");
  683.                 goto SetStorageType;
  684.             }
  685.            
  686.             // Destination type must be item, so generate code to create an XmlAtomicValue
  687.             this.helper.LoadInteger(this.helper.StaticData.DeclareXmlType(xmlType));
  688.             this.helper.LoadQueryRuntime();
  689.             this.helper.Call(XmlILMethods.StorageMethods[this.storage.ItemStorageType].ToAtomicValue);
  690.             SetStorageType:
  691.            
  692.             this.storage = this.storage.ToStorageType(storageTypeDest);
  693.         }
  694.     }
  695. }

Developer Fusion