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