Author: spouliot
Date: 2008-02-15 14:27:00 -0500 (Fri, 15 Feb 2008)
New Revision: 95796

Added:
   trunk/mono-tools/gendarme/framework/Gendarme.Framework.Helpers/
   trunk/mono-tools/gendarme/framework/Gendarme.Framework.Helpers/ChangeLog
   
trunk/mono-tools/gendarme/framework/Gendarme.Framework.Helpers/MethodSignature.cs
   
trunk/mono-tools/gendarme/framework/Gendarme.Framework.Helpers/MethodSignatures.cs
   
trunk/mono-tools/gendarme/framework/Gendarme.Framework.Helpers/StackEntryAnalysis.cs
Log:
2008-02-15  Sebastien Pouliot  <[EMAIL PROTECTED]> 

        * MethodSignature.cs
        * MethodSignatures.cs
        * StackEntryAnalysis.cs:
                Move helper classes into new namespace.


Added: trunk/mono-tools/gendarme/framework/Gendarme.Framework.Helpers/ChangeLog
===================================================================
--- trunk/mono-tools/gendarme/framework/Gendarme.Framework.Helpers/ChangeLog    
2008-02-15 19:25:08 UTC (rev 95795)
+++ trunk/mono-tools/gendarme/framework/Gendarme.Framework.Helpers/ChangeLog    
2008-02-15 19:27:00 UTC (rev 95796)
@@ -0,0 +1,6 @@
+2008-02-15  Sebastien Pouliot  <[EMAIL PROTECTED]> 
+
+       * MethodSignature.cs
+       * MethodSignatures.cs
+       * StackEntryAnalysis.cs:
+               Move helper classes into new namespace.

Copied: 
trunk/mono-tools/gendarme/framework/Gendarme.Framework.Helpers/MethodSignature.cs
 (from rev 95432, 
trunk/mono-tools/gendarme/framework/Gendarme.Framework/MethodSignature.cs)
===================================================================
--- trunk/mono-tools/gendarme/framework/Gendarme.Framework/MethodSignature.cs   
2008-02-11 12:21:21 UTC (rev 95432)
+++ 
trunk/mono-tools/gendarme/framework/Gendarme.Framework.Helpers/MethodSignature.cs
   2008-02-15 19:27:00 UTC (rev 95796)
@@ -0,0 +1,171 @@
+//
+// Gendarme.Framework.MethodSignature
+//
+// Authors:
+//     Andreas Noever <[EMAIL PROTECTED]>
+//     Sebastien Pouliot  <[EMAIL PROTECTED]>
+//
+//  (C) 2008 Andreas Noever
+// Copyright (C) 2008 Novell, Inc (http://www.novell.com)
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to
+// permit persons to whom the Software is furnished to do so, subject to
+// the following conditions:
+//
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+
+using System;
+using System.Collections.ObjectModel;
+using System.Collections.Generic;
+using System.Text;
+
+using Mono.Cecil;
+
+namespace Gendarme.Framework.Helpers {
+
+       /// <summary>
+       /// Used to match methods. Properties that are set to null are ignored
+       /// </summary>
+       /// <example>
+       /// <code>
+       /// MethodDefinition method = ...
+       /// MethodSignature sig = new MethodSignature ("Dispose");
+       /// if (sig.Match (method)) { 
+       ///     //matches any method named "Dispose" with any (or no) return 
value and any number of parameters
+       /// }
+       /// </code>
+       /// </example>
+       /// <seealso cref="Gendarme.Framework.MethodSignatures"/>
+       public class MethodSignature {
+
+               /// <summary>
+               /// The name of the method to match. Ignored if null.
+               /// </summary>
+               public string Name { get; private set; }
+
+               /// <summary>
+               /// The FullName (Namespace.Type) of the return type. Ignored 
if null.
+               /// </summary>
+               public string ReturnType { get; private set; }
+
+               /// <summary>
+               /// An array of FullNames (Namespace.Type) of parameter types. 
Ignored if null. Null entries act as wildcards.
+               /// </summary>
+               public ReadOnlyCollection<string> Parameters { get; private 
set; }
+
+               /// <summary>
+               /// An attribute mask matched against the attributes of the 
method.
+               /// </summary>
+               public MethodAttributes Attributes { get; private set; }
+
+
+               public MethodSignature ()
+               {
+               }
+
+               public MethodSignature (string name)
+                       : this (name, null, null)
+               {
+               }
+
+               public MethodSignature (string name, string returnType)
+                       : this (name, returnType, null)
+               {
+               }
+
+               public MethodSignature (string name, string returnType, 
string[] parameters)
+               {
+                       Name = name;
+                       ReturnType = returnType;
+                       if (parameters != null)
+                               Parameters = new ReadOnlyCollection<string> 
(new List<string> (parameters));
+               }
+
+               public MethodSignature (string name, string returnType, string 
[] parameters, MethodAttributes attributes)
+                       : this (name, returnType, parameters)
+               {
+                       Attributes = attributes;
+               }
+
+               /// <summary>
+               /// Checks if a MethodReference match the signature.
+               /// </summary>
+               /// <param name="method">The method to check.</param>
+               /// <returns>True if the MethodReference matches all aspects of 
the MethodSignature.</returns>
+               public bool Matches (MethodReference method)
+               {
+                       if (method == null)
+                               throw new ArgumentNullException ("method");
+
+                       if (Name != null && method.Name != Name)
+                               return false;
+
+                       if (ReturnType != null && 
method.ReturnType.ReturnType.FullName != ReturnType)
+                               return false;
+
+                       if (Parameters != null) {
+                               if (Parameters.Count != method.Parameters.Count)
+                                       return false;
+                               for (int i = 0; i < Parameters.Count; i++) {
+                                       if (Parameters [i] == null)
+                                               continue;//ignore parameter
+                                       if (Parameters [i] != method.Parameters 
[i].ParameterType.FullName) {
+                                               return false;
+                                       }
+                               }
+                       }
+
+                       // skip last check if no attributes are part of the 
signature
+                       if (((int) Attributes) == 0)
+                               return true;
+
+                       // put this at last step so we avoid the cast as much 
as possible
+                       MethodDefinition md = (method as MethodDefinition);
+                       return ((md == null) || ((md.Attributes & Attributes) 
== Attributes));
+               }
+
+               /// <summary>
+               /// 
+               /// </summary>
+               /// <returns></returns>
+               public override string ToString ()
+               {
+                       // if we do not have enough useful information return 
an empty string
+                       if (Name == null)
+                               return String.Empty;
+
+                       StringBuilder sb = new StringBuilder ();
+                       if (ReturnType != null) {
+                               sb.Append (ReturnType);
+                               sb.Append (' ');
+                       }
+
+                       sb.Append (Name);
+                       sb.Append ('(');
+                       if (Parameters != null) {
+                               for (int i = 0; i < Parameters.Count; i++) {
+                                       sb.Append (Parameters [i]);
+                                       if (i < Parameters.Count - 1)
+                                               sb.Append (',');
+                               }
+                       }
+                       sb.Append (')');
+
+                       return sb.ToString ();
+               }
+       }
+}

Copied: 
trunk/mono-tools/gendarme/framework/Gendarme.Framework.Helpers/MethodSignatures.cs
 (from rev 95432, 
trunk/mono-tools/gendarme/framework/Gendarme.Framework/MethodSignatures.cs)
===================================================================
--- trunk/mono-tools/gendarme/framework/Gendarme.Framework/MethodSignatures.cs  
2008-02-11 12:21:21 UTC (rev 95432)
+++ 
trunk/mono-tools/gendarme/framework/Gendarme.Framework.Helpers/MethodSignatures.cs
  2008-02-15 19:27:00 UTC (rev 95796)
@@ -0,0 +1,101 @@
+//
+// Gendarme.Framework.MethodSignatures
+//
+// Authors:
+//     Andreas Noever <[EMAIL PROTECTED]>
+//     Sebastien Pouliot  <[EMAIL PROTECTED]>
+//
+//  (C) 2008 Andreas Noever
+// Copyright (C) 2008 Novell, Inc (http://www.novell.com)
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to
+// permit persons to whom the Software is furnished to do so, subject to
+// the following conditions:
+//
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+
+using System;
+
+using Mono.Cecil;
+
+namespace Gendarme.Framework.Helpers {
+
+       /// <summary>
+       /// Defines commonly used MethodSignatures
+       /// </summary>
+       /// <see cref="Gendarme.Framework.Helpers.MethodSignature"/>
+       public static class MethodSignatures {
+               private static readonly string [] NoParameter = new string [0];
+               private static readonly string [] OneParameter = new string [1];
+               private static readonly string [] TwoParameters = new string 
[2];
+
+               // System.Object
+               public static readonly new MethodSignature Equals = new 
MethodSignature ("Equals", "System.Boolean", new string [] { "System.Object" }, 
 MethodAttributes.Public);
+               public static readonly MethodSignature Finalize = new 
MethodSignature ("Finalize", "System.Void", NoParameter, 
MethodAttributes.Family);
+               public static readonly new MethodSignature GetHashCode = new 
MethodSignature ("GetHashCode", "System.Int32", NoParameter, 
MethodAttributes.Public | MethodAttributes.Virtual);
+               public static readonly new MethodSignature ToString = new 
MethodSignature ("ToString", "System.String", NoParameter, 
MethodAttributes.Public | MethodAttributes.Virtual);
+
+               // IClonable
+               public static readonly MethodSignature Clone = new 
MethodSignature ("Clone", null, NoParameter);
+
+               // IDisposable
+               public static readonly MethodSignature Dispose = new 
MethodSignature ("Dispose", "System.Void", NoParameter);
+               public static readonly MethodSignature DisposeExplicit = new 
MethodSignature ("System.IDisposable.Dispose", "System.Void", NoParameter);
+
+               // ISerialization
+               private static string [] SerializationParameters = new string 
[] { "System.Runtime.Serialization.SerializationInfo", 
"System.Runtime.Serialization.StreamingContext" };
+               public static readonly MethodSignature SerializationConstructor 
= new MethodSignature (".ctor", "System.Void", SerializationParameters);
+               public static readonly MethodSignature GetObjectData = new 
MethodSignature ("GetObjectData", "System.Void", SerializationParameters);
+               public static readonly MethodSignature 
SerializationEventHandler = new MethodSignature (null, "System.Void", new 
string [] { "System.Runtime.Serialization.StreamingContext" }, 
MethodAttributes.Private);
+
+               // operators
+               private static readonly MethodAttributes OperatorAttributes = 
MethodAttributes.Static | MethodAttributes.SpecialName;
+               
+               // unary
+               public static readonly MethodSignature op_UnaryPlus = new 
MethodSignature ("op_UnaryPlus", null, OneParameter, OperatorAttributes);       
      // +5
+               public static readonly MethodSignature op_UnaryNegation = new 
MethodSignature ("op_UnaryNegation", null, OneParameter, OperatorAttributes);   
  // -5
+               public static readonly MethodSignature op_LogicalNot = new 
MethodSignature ("op_LogicalNot", null, OneParameter, OperatorAttributes);      
     // !true
+               public static readonly MethodSignature op_OnesComplement = new 
MethodSignature ("op_OnesComplement", null, OneParameter, OperatorAttributes);  
 // ~5
+
+               public static readonly MethodSignature op_Increment = new 
MethodSignature ("op_Increment", null, OneParameter, OperatorAttributes);       
      // 5++
+               public static readonly MethodSignature op_Decrement = new 
MethodSignature ("op_Decrement", null, OneParameter, OperatorAttributes);       
      // 5--
+               public static readonly MethodSignature op_True = new 
MethodSignature ("op_True", "System.Boolean", OneParameter, 
OperatorAttributes);           // if (object)          
+               public static readonly MethodSignature op_False = new 
MethodSignature ("op_False", "System.Boolean", OneParameter, 
OperatorAttributes);         // if (object)
+
+               // binary
+               public static readonly MethodSignature op_Addition = new 
MethodSignature ("op_Addition", null, TwoParameters, OperatorAttributes);       
       // 5 + 5
+               public static readonly MethodSignature op_Subtraction = new 
MethodSignature ("op_Subtraction", null, TwoParameters, OperatorAttributes);    
    // 5 - 5 
+               public static readonly MethodSignature op_Multiply = new 
MethodSignature ("op_Multiply", null, TwoParameters, OperatorAttributes);       
       // 5 * 5
+               public static readonly MethodSignature op_Division = new 
MethodSignature ("op_Division", null, TwoParameters, OperatorAttributes);       
       // 5 / 5
+               public static readonly MethodSignature op_Modulus = new 
MethodSignature ("op_Modulus", null, TwoParameters, OperatorAttributes);        
        // 5 % 5
+
+               public static readonly MethodSignature op_BitwiseAnd = new 
MethodSignature ("op_BitwiseAnd", null, TwoParameters, OperatorAttributes);     
     // 5 & 5
+               public static readonly MethodSignature op_BitwiseOr = new 
MethodSignature ("op_BitwiseOr", null, TwoParameters, OperatorAttributes);      
      // 5 | 5
+               public static readonly MethodSignature op_ExclusiveOr = new 
MethodSignature ("op_ExclusiveOr", null, TwoParameters, OperatorAttributes);    
    // 5 ^ 5
+
+               public static readonly MethodSignature op_LeftShift = new 
MethodSignature ("op_LeftShift", null, TwoParameters, OperatorAttributes);      
      // 5 << 5
+               public static readonly MethodSignature op_RightShift = new 
MethodSignature ("op_RightShift", null, TwoParameters, OperatorAttributes);     
     // 5 >> 5
+
+               // comparison
+               public static readonly MethodSignature op_Equality = new 
MethodSignature ("op_Equality", null, TwoParameters, OperatorAttributes);       
               // 5 == 5
+               public static readonly MethodSignature op_Inequality = new 
MethodSignature ("op_Inequality", null, TwoParameters, OperatorAttributes);     
             // 5 != 5
+               public static readonly MethodSignature op_GreaterThan = new 
MethodSignature ("op_GreaterThan", null, TwoParameters, OperatorAttributes);    
            // 5 > 5
+               public static readonly MethodSignature op_LessThan = new 
MethodSignature ("op_LessThan", null, TwoParameters, OperatorAttributes);       
               // 5 < 5
+               public static readonly MethodSignature op_GreaterThanOrEqual = 
new MethodSignature ("op_GreaterThanOrEqual", null, TwoParameters, 
OperatorAttributes);  // 5 >= 5
+               public static readonly MethodSignature op_LessThanOrEqual = new 
MethodSignature ("op_LessThanOrEqual", null, TwoParameters, 
OperatorAttributes);        // 5 <= 5
+       }
+}

Copied: 
trunk/mono-tools/gendarme/framework/Gendarme.Framework.Helpers/StackEntryAnalysis.cs
 (from rev 95432, 
trunk/mono-tools/gendarme/framework/Gendarme.Framework/StackEntryAnalysis.cs)
===================================================================
--- 
trunk/mono-tools/gendarme/framework/Gendarme.Framework/StackEntryAnalysis.cs    
    2008-02-11 12:21:21 UTC (rev 95432)
+++ 
trunk/mono-tools/gendarme/framework/Gendarme.Framework.Helpers/StackEntryAnalysis.cs
        2008-02-15 19:27:00 UTC (rev 95796)
@@ -0,0 +1,656 @@
+//
+// Gendarme.Framework.StackEntryAnalysis
+//
+// Authors:
+//     Andreas Noever <[EMAIL PROTECTED]>
+//
+//  (C) 2008 Andreas Noever
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to
+// permit persons to whom the Software is furnished to do so, subject to
+// the following conditions:
+//
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+
+using System;
+using System.Collections.Generic;
+
+using Mono.Cecil;
+using Mono.Cecil.Cil;
+
+using Gendarme.Framework.Rocks;
+
+namespace Gendarme.Framework.Helpers {
+
+       /// <summary>
+       /// This class can be used to find all usages of a reference on the 
stack.
+       /// Currently used for:
+       /// Gendarme.Rules.BadPractice.CheckNewExceptionWithoutThrowRule
+       /// Gendarme.Rules.BadPractice.CheckNewThreadWithoutStartRule
+       /// 
Gendarme.Rules.Interoperability.GetLastErrorMustBeCalledRightAfterPInvokeRule
+       /// </summary>
+       public class StackEntryAnalysis {
+
+               /// <summary>
+               /// Represents a usage of a StackEntry
+               /// </summary>
+               public struct UsageResult {
+                       /// <summary>
+                       /// The instruction that uses the StackEntry
+                       /// </summary>
+                       public readonly Instruction Instruction;
+                       /// <summary>
+                       /// The positive offset of the StackEntry before the 
instruction executes. 0 means right on top.
+                       /// </summary>
+                       public readonly int StackOffset;
+                       public UsageResult (Instruction ins, int offset)
+                       {
+                               this.Instruction = ins;
+                               this.StackOffset = offset;
+                       }
+               }
+
+               enum StoreType {
+                       None,
+                       Local,
+                       Argument,
+                       Field,
+                       StaticField,
+                       Out,
+               }
+
+               /// <summary>
+               /// Saves information about a local variable slot (argument or 
local variable).
+               /// Used to keep track of assignments.
+               /// </summary>
+               struct StoreSlot {
+                       public readonly StoreType Type;
+                       public readonly int Slot;
+                       public StoreSlot (StoreType type, int slot)
+                       {
+                               this.Type = type;
+                               this.Slot = slot;
+                       }
+
+                       /// <summary>
+                       /// Use this to check if an instruction accesses a 
StoreSlot. True if this is not a StoreSlot.
+                       /// </summary>
+                       public bool IsNone
+                       {
+                               get
+                               {
+                                       return this.Type == StoreType.None;
+                               }
+                       }
+
+                       public static bool operator == (StoreSlot a, StoreSlot 
b)
+                       {
+                               return a.Slot == b.Slot && a.Type == b.Type;
+                       }
+
+                       public static bool operator != (StoreSlot a, StoreSlot 
b)
+                       {
+                               return a.Slot != b.Slot || a.Type != b.Type;
+                       }
+
+                       public override bool Equals (object obj)
+                       {
+                               if (obj == null)
+                                       return false;
+                               if (!(obj is StoreSlot))
+                                       return false;
+                               StoreSlot other = (StoreSlot) obj;
+                               return this == other;
+                       }
+
+                       public override int GetHashCode ()
+                       {
+                               return Slot.GetHashCode () ^ Type.GetHashCode 
();
+                       }
+               }
+
+               /// <summary>
+               /// Wraps an instruction and a stack of leave statements used 
to get to this instruction.
+               /// Needed to do correct analysis in finally blocks.
+               /// </summary>
+               struct InstructionWithLeave {
+                       public static readonly InstructionWithLeave Empty = new 
InstructionWithLeave ();
+
+                       public readonly Instruction Instruction;
+                       public readonly Instruction [] LeaveStack;
+
+                       public InstructionWithLeave (Instruction instruction)
+                       {
+                               this.Instruction = instruction;
+                               this.LeaveStack = null;
+                       }
+
+                       private InstructionWithLeave (Instruction instruction, 
Instruction [] leaveStack)
+                       {
+                               this.Instruction = instruction;
+                               this.LeaveStack = leaveStack;
+                       }
+
+                       /// <summary>
+                       /// Returns a new InstructionWithLeave with leave 
pushed onto the stack.
+                       /// </summary>
+                       /// <param name="instruction">The new 
instruction.</param>
+                       /// <param name="leave">The leave instruction to push 
onto the stack.</param>
+                       /// <returns>A new InstructionWithLeave</returns>
+                       public InstructionWithLeave Push (Instruction 
instruction, Instruction leave)
+                       {
+                               Instruction [] newStack;
+                               if (this.LeaveStack != null) {
+                                       newStack = new Instruction 
[LeaveStack.Length + 1];
+                                       Array.Copy (LeaveStack, newStack, 
LeaveStack.Length);
+                                       newStack [LeaveStack.Length] = leave;
+                               } else {
+                                       newStack = new Instruction [] { leave };
+                               }
+                               return new InstructionWithLeave (instruction, 
newStack);
+                       }
+
+                       /// <summary>
+                       /// Returns a new InstructionWithLeave with the same 
LeaveStack and another instruction.
+                       /// </summary>
+                       /// <param name="instruction">The new 
instruction.</param>
+                       /// <returns>a new InstructionWithLeave</returns>
+                       public InstructionWithLeave Copy (Instruction 
instruction)
+                       {
+                               return new InstructionWithLeave (instruction, 
this.LeaveStack);
+                       }
+
+                       /// <summary>
+                       /// Returns a new InstructionWithLeave with the one 
leave statement popped and instruction set to the operand of the popped leave 
statement.
+                       /// </summary>
+                       /// <returns>a new InstructionWithLeave</returns>
+                       public InstructionWithLeave Pop ()
+                       {
+                               Instruction [] newStack = null;
+                               if (LeaveStack.Length != 1) {
+                                       newStack = new Instruction 
[LeaveStack.Length - 1];
+                                       Array.Copy (LeaveStack, newStack, 
newStack.Length);
+                               }
+                               return new InstructionWithLeave ((Instruction) 
this.LeaveStack [this.LeaveStack.Length - 1].Operand, newStack);
+                       }
+
+               }
+
+               public MethodDefinition Method {
+                       get; private set;
+               }
+
+               private MethodBody Body {
+                       get { return Method.Body; }
+               }
+
+               public StackEntryAnalysis (MethodDefinition method)
+               {
+                       this.Method = method;
+               }
+
+               //static lists to save allocations.
+               private static List<KeyValuePair<InstructionWithLeave, int>> 
UsedBy = new List<KeyValuePair<InstructionWithLeave, int>> ();
+               private static List<KeyValuePair<InstructionWithLeave, int>> 
AlternativePaths = new List<KeyValuePair<InstructionWithLeave, int>> ();
+
+               /// <summary>
+               /// Searches a method for usage of the value pushed onto the 
stack by the specified instruction.
+               /// </summary>
+               /// <param name="ins">The instruction.</param>
+               /// <returns>An array of UsageResults containing the 
instructions that use the value and the stack offset of the entry at that 
instruction. A StackOffset of 0 means right on top of the stack.</returns>
+               public UsageResult [] GetStackEntryUsage (Instruction ins)
+               {
+                       /* In the main loop we search for all usages of a 
StackEntry.
+                        * Then we check each usage for a store (to a local 
variable or an argument), search for corrosponding loads and search for usages 
of the new Stackentry.
+                        * This continues until no stores are found. */
+
+                       UsedBy.Clear ();
+                       AlternativePaths.Clear ();
+
+                       AlternativePaths.Add (new 
KeyValuePair<InstructionWithLeave, int> (new InstructionWithLeave (ins.Next), 
0));
+
+                       int lastAlternativesCount = 0;
+                       int lastUsedByCount = 0;
+
+                       while (lastAlternativesCount != AlternativePaths.Count) 
{ //continue until no more alternatives have been found (by CheckUsedBy)
+
+                               for (int i = lastAlternativesCount; i < 
AlternativePaths.Count; i++) { //find the instruction that pops the value and 
follow all branches
+                                       var result = FollowStackEntry 
(AlternativePaths [i].Key, AlternativePaths [i].Value);
+                                       if (result.Key.Instruction != null)
+                                               UsedBy.AddIfNew (result); //add 
to usedby list
+                               }
+                               lastAlternativesCount = AlternativePaths.Count; 
//check each path only once.
+
+                               CheckUsedBy (lastUsedByCount);
+                               lastUsedByCount = UsedBy.Count;
+                       }
+
+                       //build return value
+                       UsageResult [] results = new UsageResult [UsedBy.Count];
+                       for (int i = 0; i < results.Length; i++)
+                               results [i] = new UsageResult (UsedBy 
[i].Key.Instruction, UsedBy [i].Value);
+                       return results;
+               }
+
+               /// <summary>
+               /// Iterates over all Instructions inside UsedBy and spawns a 
new alternative if necessary.
+               /// </summary>
+               /// <param name="start">The first index to progress.</param>
+               private void CheckUsedBy (int start)
+               {
+                       for (int ii = start; ii < UsedBy.Count; ii++) {
+                               InstructionWithLeave use = UsedBy [ii].Key;
+
+                               StoreSlot slot = GetStoreSlot 
(use.Instruction); //check if this is a store instruction
+
+                               bool removeFromUseBy = false; //ignore the use
+
+                               if (use.Instruction.OpCode.Code == 
Code.Castclass) {
+                                       removeFromUseBy = true;
+                                       AlternativePaths.AddIfNew (new 
KeyValuePair<InstructionWithLeave, int> (use.Copy (use.Instruction.Next), 0));
+                               } else if (use.Instruction.OpCode.Code == 
Code.Pop) {//pop is not a valid usage
+                                       removeFromUseBy = true;
+                               } else if (!slot.IsNone) {
+                                       if (slot.Type == StoreType.Argument || 
slot.Type == StoreType.Local)
+                                               removeFromUseBy = true; 
//temporary save
+                                       foreach (var ld in this.FindLoad 
(use.Copy (use.Instruction.Next), slot)) { //start searching at the next 
instruction
+                                               AlternativePaths.AddIfNew (new 
KeyValuePair<InstructionWithLeave, int> (ld.Copy (ld.Instruction.Next), 0));
+                                       }
+                               }
+                               if (removeFromUseBy) {
+                                       UsedBy.RemoveAt (ii);
+                                       ii--;
+                               }
+                       }
+               }
+
+               /// <summary>
+               /// Follows the instructions until the specified stack entry is 
accessed.
+               /// </summary>
+               /// <param name="ins">The first instruction.</param>
+               /// <param name="stackEntry">The distance of the stack entry 
from the top of the stack. 0 means right on top.</param>
+               /// <returns>The instruction that pops the stackEntry and the 
distance of the entry to the top of the stack. If no valid instruction if found 
the method returns InstructionWithLeave.Empty.</returns>
+               private KeyValuePair<InstructionWithLeave, int> 
FollowStackEntry (InstructionWithLeave startInstruction, int stackEntryDistance)
+               {
+                       Instruction ins = startInstruction.Instruction;
+
+                       while (true) {
+                               int pop = this.GetPopCount (ins);
+                               int push = this.GetPushCount (ins);
+
+                               if (pop > stackEntryDistance)  //does this 
instruction pop the stack entry 
+                                       return new 
KeyValuePair<InstructionWithLeave, int> (startInstruction.Copy (ins), 
stackEntryDistance);
+
+                               stackEntryDistance -= pop;
+                               stackEntryDistance += push;
+
+                               //fetch ne next instruction
+                               object alternativeNext;
+                               Instruction nextInstruction = 
GetNextInstruction (ins, out alternativeNext);
+
+                               if (nextInstruction == null)
+                                       return new 
KeyValuePair<InstructionWithLeave, int> (); //return / throw / endfinally
+
+                               if (nextInstruction.OpCode.Code == Code.Leave 
|| nextInstruction.OpCode.Code == Code.Leave_S)
+                                       return new 
KeyValuePair<InstructionWithLeave, int> (); //leave clears the stack, the entry 
is gone.
+
+                               if (alternativeNext != null) { //branch / 
switch                                        
+                                       Instruction oneTarget = alternativeNext 
as Instruction;
+                                       if (oneTarget != null) { //branch
+                                               AlternativePaths.AddIfNew (new 
KeyValuePair<InstructionWithLeave, int> (startInstruction.Copy (oneTarget), 
stackEntryDistance));
+                                       } else { //switch
+                                               foreach (Instruction 
switchTarget in (Instruction []) alternativeNext)
+                                                       
AlternativePaths.AddIfNew (new KeyValuePair<InstructionWithLeave, int> 
(startInstruction.Copy (switchTarget), stackEntryDistance));
+                                       }
+                               }
+
+                               if (nextInstruction.OpCode.FlowControl == 
FlowControl.Branch || nextInstruction.OpCode.FlowControl == 
FlowControl.Cond_Branch) {
+                                       AlternativePaths.AddIfNew (new 
KeyValuePair<InstructionWithLeave, int> (startInstruction.Copy 
(nextInstruction), stackEntryDistance));
+                                       return new 
KeyValuePair<InstructionWithLeave, int> (); //end of block
+                               }
+                               ins = nextInstruction;
+                       }
+               }
+
+
+               //static lists to save allocations.
+               private static List<InstructionWithLeave> LoadAlternatives = 
new List<InstructionWithLeave> ();
+               private static List<InstructionWithLeave> LoadResults = new 
List<InstructionWithLeave> ();
+
+               /// <summary>
+               /// Follows the codeflow starting at a given instruction and 
finds all loads for a given slot.
+               /// Continues and follows all branches until the slot is 
overwritten or the method returns / throws.
+               /// </summary>
+               /// <param name="insWithLeave">The first instruction to start 
the search at.</param>
+               /// <param name="slot">The slot to search.</param>
+               /// <returns>An array of instructions that load from the 
slot.</returns>
+               private InstructionWithLeave [] FindLoad (InstructionWithLeave 
insWithLeave, StoreSlot slot)
+               {
+                       LoadAlternatives.Clear ();
+                       LoadResults.Clear ();
+
+                       LoadAlternatives.Add (insWithLeave);
+
+
+                       for (int i = 0; i < LoadAlternatives.Count; i++) { 
//loop over all branches, more will get added inside the loop
+                               insWithLeave = LoadAlternatives [i]; //the 
first instruction of the block (contains the leave stack)
+
+                               Instruction ins = insWithLeave.Instruction; 
//the current instruction
+                               while (true) {
+
+                                       if (GetStoreSlot (ins) == slot) //check 
if the slot gets overwritten
+                                               break;
+
+                                       if (slot == GetLoadSlot (ins))
+                                               LoadResults.AddIfNew 
(insWithLeave.Copy (ins)); //continue, might be loaded again
+
+                                       //we simply branch to every possible 
catch block.
+                                       foreach (ExceptionHandler handler in 
Body.ExceptionHandlers) {
+                                               if (handler.Type != 
ExceptionHandlerType.Catch)
+                                                       continue;
+                                               if (ins.Offset < 
handler.TryStart.Offset || ins.Offset >= handler.TryEnd.Offset)
+                                                       continue;
+                                               LoadAlternatives.AddIfNew 
(insWithLeave.Copy (handler.HandlerStart));
+                                       }
+
+                                       //Code.Leave leaves a try/catch block. 
Search for the finally block.
+                                       if (ins.OpCode.Code == Code.Leave || 
ins.OpCode.Code == Code.Leave_S) {
+                                               bool handlerFound = false;
+                                               foreach (ExceptionHandler 
handler in Body.ExceptionHandlers) {
+                                                       if (handler.Type != 
ExceptionHandlerType.Finally)
+                                                               continue;
+                                                       if 
(handler.TryStart.Offset > ins.Offset || handler.TryEnd.Offset <= ins.Offset)
+                                                               continue;
+                                                       
LoadAlternatives.AddIfNew (insWithLeave.Push (handler.HandlerStart, ins)); 
//push the leave instruction onto the leave stack
+                                                       handlerFound = true;
+                                                       break;
+                                               }
+                                               if (!handlerFound) //no finally 
found (try/catch without finally)
+                                                       
LoadAlternatives.AddIfNew (insWithLeave.Copy ((Instruction) ins.Operand));
+                                               break;
+
+                                       }
+
+                                       if (ins.OpCode.Code == Code.Endfinally) 
{ //pop the last leave instruction and branch to it
+                                               LoadAlternatives.AddIfNew 
(insWithLeave.Pop ());
+                                               break;
+                                       }
+
+                                       //fetch the next instruction (s)
+                                       object alternativeNext;
+                                       ins = GetNextInstruction (ins, out 
alternativeNext);
+                                       if (ins == null)
+                                               break;
+
+                                       if (alternativeNext != null) {
+                                               Instruction oneTarget = 
alternativeNext as Instruction;
+                                               if (oneTarget != null) { 
//normal branch
+                                                       
LoadAlternatives.AddIfNew (insWithLeave.Copy (oneTarget));
+                                               } else { //switch statement
+                                                       foreach (Instruction 
switchTarget in (Instruction []) alternativeNext)
+                                                               
LoadAlternatives.AddIfNew (insWithLeave.Copy (switchTarget));
+                                               }
+                                       }
+
+                                       if (ins.OpCode.FlowControl == 
FlowControl.Branch || ins.OpCode.FlowControl == FlowControl.Cond_Branch) {
+                                               if (ins.OpCode.Code != 
Code.Leave && ins.OpCode.Code != Code.Leave_S) {
+                                                       
LoadAlternatives.AddIfNew (insWithLeave.Copy (ins)); //add if new, avoid 
infinity loop
+                                                       break;
+                                               }
+                                       }
+                               }
+                       }
+                       return LoadResults.ToArray ();
+               }
+
+               /// <summary>
+               /// Helper method that returns the next Instruction.
+               /// </summary>
+               /// <param name="ins">The instruction</param>
+               /// <param name="alternative">If the instruction is a branch, 
the branch target is returned. For a switch statemant an array of targets is 
returned.</param>
+               /// <returns>The next instruction that would be executed by the 
runtime.</returns>
+               public static Instruction GetNextInstruction (Instruction ins, 
out object alternative)
+               {
+                       alternative = null;
+                       switch (ins.OpCode.FlowControl) {
+                       case FlowControl.Branch:
+                               return (Instruction) ins.Operand;
+                       case FlowControl.Cond_Branch:
+                               alternative = ins.Operand;
+                               return ins.Next;
+                       case FlowControl.Call:
+                       case FlowControl.Next:
+                       case FlowControl.Meta:
+                       case FlowControl.Break: //debugging breakpoint
+                               return ins.Next;
+                       case FlowControl.Return:
+                       case FlowControl.Throw:
+                               return null;
+                       default:
+                               throw new NotImplementedException 
("FlowControl: " + ins.OpCode.FlowControl + " is not supported.");
+
+                       }
+               }
+
+               /// <summary>
+               /// Checks if an instruction is a load and returns the slot it 
loads from.
+               /// </summary>
+               /// <param name="ins">The instruction</param>
+               /// <returns>If the instruction is a load returns the slot to 
load. Check slot.IsNone() to see if this instruction is a load.</returns>
+               private StoreSlot GetLoadSlot (Instruction ins)
+               {
+                       switch (ins.OpCode.Code) {
+                       case Code.Ldloc_0:
+                               return new StoreSlot (StoreType.Local, 0);
+                       case Code.Ldloc_1:
+                               return new StoreSlot (StoreType.Local, 1);
+                       case Code.Ldloc_2:
+                               return new StoreSlot (StoreType.Local, 2);
+                       case Code.Ldloc_3:
+                               return new StoreSlot (StoreType.Local, 3);
+                       case Code.Ldloc_S:
+                       case Code.Ldloc:
+                               return new StoreSlot (StoreType.Local, 
((VariableDefinition) ins.Operand).Index);
+
+                       case Code.Ldfld:
+                               //TODO: we do not check what instance is on the 
stack
+                               return new StoreSlot (StoreType.Field, (int) 
((FieldReference) ins.Operand).MetadataToken.ToUInt ());
+                       case Code.Ldsfld:
+                               return new StoreSlot (StoreType.StaticField, 
(int) ((FieldReference) ins.Operand).MetadataToken.ToUInt ());
+
+                       case Code.Ldarg_0:
+                               return new StoreSlot (StoreType.Argument, 0);
+                       case Code.Ldarg_1:
+                               return new StoreSlot (StoreType.Argument, 1);
+                       case Code.Ldarg_2:
+                               return new StoreSlot (StoreType.Argument, 2);
+                       case Code.Ldarg_3:
+                               return new StoreSlot (StoreType.Argument, 3);
+                       case Code.Ldarg_S:
+                       case Code.Ldarg: {
+                                       int sequence = ((ParameterDefinition) 
ins.Operand).Sequence;
+                                       if (!this.Method.HasThis)
+                                               sequence--;
+                                       return new StoreSlot 
(StoreType.Argument, sequence);
+                               }
+
+                       case Code.Ldind_I:
+                       case Code.Ldind_I1:
+                       case Code.Ldind_I2:
+                       case Code.Ldind_I4:
+                       case Code.Ldind_I8:
+                       case Code.Ldind_R4:
+                       case Code.Ldind_R8:
+                       case Code.Ldind_Ref:
+                       case Code.Ldind_U1:
+                       case Code.Ldind_U2:
+                       case Code.Ldind_U4:
+                               //TODO: improve stack check
+                               while (ins.Previous != null) { //quick fix for 
out parameters.
+                                       ins = ins.Previous;
+                                       StoreSlot last = GetLoadSlot (ins);
+                                       if (last.Type == StoreType.Argument)
+                                               return new StoreSlot 
(StoreType.Out, last.Slot);
+                               }
+                               goto default;
+                       default:
+                               return new StoreSlot (StoreType.None, -1);
+                       }
+               }
+               /// <summary>
+               /// Checks if an instruction is a store and returns the slot.
+               /// </summary>
+               /// <param name="ins">The instruction</param>
+               /// <returns>If the instruction is a store returns the slot to 
store. Check slot.IsNone() to see if this instruction is a store.</returns>
+               private StoreSlot GetStoreSlot (Instruction ins)
+               {
+                       switch (ins.OpCode.Code) {
+                       case Code.Stloc_0:
+                               return new StoreSlot (StoreType.Local, 0);
+                       case Code.Stloc_1:
+                               return new StoreSlot (StoreType.Local, 1);
+                       case Code.Stloc_2:
+                               return new StoreSlot (StoreType.Local, 2);
+                       case Code.Stloc_3:
+                               return new StoreSlot (StoreType.Local, 3);
+                       case Code.Stloc_S:
+                       case Code.Stloc:
+                               return new StoreSlot (StoreType.Local, 
((VariableDefinition) ins.Operand).Index);
+
+                       case Code.Stfld:
+                               //TODO: we do not check what instance is on the 
stack
+                               return new StoreSlot (StoreType.Field, (int) 
((FieldReference) ins.Operand).MetadataToken.ToUInt ());
+                       case Code.Stsfld:
+                               return new StoreSlot (StoreType.StaticField, 
(int) ((FieldReference) ins.Operand).MetadataToken.ToUInt ());
+
+                       case Code.Starg_S: //store arg (not ref / out etc)
+                       case Code.Starg: {
+                                       int sequence = ((ParameterDefinition) 
ins.Operand).Sequence;
+                                       if (!this.Method.HasThis)
+                                               sequence--;
+                                       return new StoreSlot 
(StoreType.Argument, sequence);
+                               }
+
+                       case Code.Stind_I:
+                       case Code.Stind_I1:
+                       case Code.Stind_I2:
+                       case Code.Stind_I4:
+                       case Code.Stind_I8:
+                       case Code.Stind_R4:
+                       case Code.Stind_R8:
+                       case Code.Stind_Ref:
+                               //TODO: improve stack check
+                               while (ins.Previous != null) { //quick fix for 
out parameters.
+                                       ins = ins.Previous;
+                                       StoreSlot last = GetLoadSlot (ins);
+                                       if (last.Type == StoreType.Argument)
+                                               return new StoreSlot 
(StoreType.Out, last.Slot);
+                               }
+                               goto default;
+
+
+
+                       default:
+                               return new StoreSlot (StoreType.None, -1);
+                       }
+               }
+
+               private int GetPopCount (Instruction ins)
+               {
+                       switch (ins.OpCode.StackBehaviourPop) {
+                       case StackBehaviour.Pop0:
+                               return 0;
+
+                       case StackBehaviour.Pop1:
+                       case StackBehaviour.Popi:
+                       case StackBehaviour.Popref:
+                               return 1;
+
+                       case StackBehaviour.Pop1_pop1:
+                       case StackBehaviour.Popi_pop1:
+                       case StackBehaviour.Popi_popi8:
+                       case StackBehaviour.Popi_popr4:
+                       case StackBehaviour.Popi_popr8:
+                       case StackBehaviour.Popref_pop1:
+                       case StackBehaviour.Popref_popi:
+                       case StackBehaviour.Popi_popi:
+                               return 2;
+
+                       case StackBehaviour.Popi_popi_popi:
+                       case StackBehaviour.Popref_popi_popi:
+                       case StackBehaviour.Popref_popi_popi8:
+                       case StackBehaviour.Popref_popi_popr4:
+                       case StackBehaviour.Popref_popi_popr8:
+                       case StackBehaviour.Popref_popi_popref:
+                               return 3;
+
+                       case StackBehaviour.PopAll:
+                               goto default;
+
+                       case StackBehaviour.Varpop:
+                               switch (ins.OpCode.FlowControl) {
+                               case FlowControl.Return:
+                                       return 
this.Method.ReturnType.ReturnType.FullName == "System.Void" ? 0 : 1;
+
+                               case FlowControl.Call:
+                                       IMethodSignature calledMethod = 
(IMethodSignature) ins.Operand;
+                                       if (ins.OpCode.Code != Code.Newobj)
+                                               if (calledMethod.HasThis)
+                                                       return 1 + 
calledMethod.Parameters.Count;
+                                       return calledMethod.Parameters.Count;
+
+                               default:
+                                       throw new NotImplementedException 
("Varpop not supported for this Instruction.");
+                               }
+
+                       default:
+                               throw new NotImplementedException 
(ins.OpCode.StackBehaviourPop + " not supported.");
+                       }
+               }
+
+               private int GetPushCount (Instruction ins)
+               {
+                       switch (ins.OpCode.StackBehaviourPush) {
+                       case StackBehaviour.Push0:
+                               return 0;
+
+                       case StackBehaviour.Push1:
+                       case StackBehaviour.Pushi:
+                       case StackBehaviour.Pushi8:
+                       case StackBehaviour.Pushr4:
+                       case StackBehaviour.Pushr8:
+                       case StackBehaviour.Pushref:
+                               return 1;
+
+                       case StackBehaviour.Push1_push1:
+                               return 2;
+
+                       case StackBehaviour.Varpush:
+                               if (ins.OpCode.Code == Code.Call || 
ins.OpCode.Code == Code.Calli || ins.OpCode.Code == Code.Callvirt) {
+                                       IMethodSignature calledMethod = 
(IMethodSignature) ins.Operand;
+                                       if 
(calledMethod.ReturnType.ReturnType.FullName == "System.Void")
+                                               return 0;
+                                       return 1;
+                               } else {
+                                       throw new NotImplementedException 
("Varpush not supported for this Instruction.");
+                               }
+                       default:
+                               throw new NotImplementedException 
(ins.OpCode.StackBehaviourPush + " not supported.");
+                       }
+               }
+       }
+}

_______________________________________________
Mono-patches maillist  -  [email protected]
http://lists.ximian.com/mailman/listinfo/mono-patches

Reply via email to