/// <summary>Returns true if we're calling an instance method with caller's this /// pointer, or if we're calling a static method in caller's declaring /// type with caller's this pointer as an argument.</summary> public bool IsThisCall(AssemblyCache cache, MethodInfo caller, int callIndex) { DBC.Pre(cache != null, "cache is null"); DBC.Pre(caller != null, "caller is null"); if (!m_isThisCall.HasValue) { m_isThisCall = false; MethodInfo target = cache.FindMethod(Target); if (target != null && !target.Method.IsConstructor) { if (target.Method.HasThis) { int nth = target.Method.Parameters.Count; int j = caller.Tracker.GetStackIndex(callIndex, nth); if (j >= 0) m_isThisCall = caller.Instructions.LoadsThisArg(j); } else if (target.Method.IsStatic) { for (int i = 0; i < target.Method.Parameters.Count && !m_isThisCall.Value; ++i) { int j = caller.Tracker.GetStackIndex(callIndex, i); if (j >= 0) m_isThisCall = caller.Instructions.LoadsThisArg(j); } } } } return m_isThisCall.Value; }
private bool DoChecksState(MethodInfo info) { bool usesStates = false; Log.DebugLine(this, "checking:", info.Instructions); Log.DebugLine(this, "{0:F}", info.Instructions); for (int i = 1; i < info.Instructions.Length && !usesStates; ++i) { Code code = info.Instructions[i].Untyped.OpCode.Code; if (code == Code.Ldfld || code == Code.Ldflda) { if (info.Instructions[i - 1].Untyped.OpCode.Code == Code.Ldarg_0) { usesStates = true; } } else if (code == Code.Call || code == Code.Callvirt) { string name = ((MethodReference) info.Instructions[i].Untyped.Operand).Name; if (name.StartsWith("get_")) { if (info.Instructions[i - 1].Untyped.OpCode.Code == Code.Ldarg_0) { usesStates = true; } } } } return usesStates; }
private int DoCallsVirtual(AssemblyCache cache, MethodInfo info) { int offset = -1; if (m_tested.IndexOf(info.Method.ToString()) >= 0) return offset; m_tested.Add(info.Method.ToString()); Log.DebugLine(this, "checking:"); Log.Indent(); // If the class isn't sealed then, if (info.Method.Body != null && info.Instructions != null) { Log.DebugLine(this, "{0:F}", info.Instructions); // loop through every instruction, for (int i = 0; i < info.Instructions.Length && offset < 0; ++i) { // if it's a call, Call call = info.Instructions[i] as Call; if (call != null) { // then we have a problem if we're calling a virtual method // on our instance, MethodInfo targetInfo = cache.FindMethod(call.Target); if (targetInfo != null) { if (call.IsThisCall(Cache, info, i)) { if (targetInfo.Method.IsVirtual && !targetInfo.Method.IsFinal) { Log.DebugLine(this, "{0} is virtual", call.Target); { m_details = call.Target.ToString(); offset = call.Untyped.Offset; Log.DebugLine(this, "found virtual call at {0:X2}", offset); } } else { // or we're calling one of our methods which calls a virtual method // on our instance. offset = DoCallsVirtual(cache, targetInfo); } } } } } } Log.Unindent(); if (offset >= 0) m_details = info.Method + " -> " + Environment.NewLine + " " + m_details; return offset; }
/// <summary>Returns the index of the instruction which pushed the method call's /// this argument onto the stack. May return -1 if a single such instruction /// could not be found.</summary> public int GetThisIndex(MethodInfo info) { DBC.Pre(info != null, "info is null"); DBC.Pre(Target.HasThis, "the method is static"); if (!m_thisIndex.HasValue) m_thisIndex = info.Tracker.GetStackIndex(Index, Target.Parameters.Count); return m_thisIndex.Value; }
private void DoCheck(MethodInfo info) { if (info.Instructions.Length == 2) { LoadConstantInt load = info.Instructions[0] as LoadConstantInt; End end = info.Instructions[1] as End; if (load != null && end != null && end.Untyped.OpCode.Code == Code.Ret) { Log.DebugLine(this, "returns constant"); Reporter.MethodFailed(info.Method, CheckID, 0, string.Empty); } } }
private void DoVisit(RuleDispatcher dispatcher, TypeDefinition type) { dispatcher.Dispatch(type); // Some rules consider all the methods in a type. These rules cannot be tested // with a MethodTest so we'll visit methods here in order to be able to unit // test those rules. dispatcher.Dispatch(new BeginMethods(type)); foreach (MethodDefinition method in type.Constructors) { var minfo = new Smokey.Framework.Support.MethodInfo(type, method); dispatcher.Dispatch(minfo); } foreach (MethodDefinition method in type.Methods) { var minfo = new Smokey.Framework.Support.MethodInfo(type, method); dispatcher.Dispatch(minfo); } dispatcher.Dispatch(new EndMethods(type)); }
private void DoCheckMethod(MethodInfo info) { if (DoObjectRequiresCheck(info.Method.ToString())) { m_callback(info.Method.ToString()); m_dispatcher.Dispatch(info); } }
public void Check(MethodInfo method) { if (method.Method.Body != null) DoCheckMethod(method); }
public static bool IsIntOperand(MethodInfo info, int index, int nth) { bool isInt = false; int i = info.Tracker.GetStackIndex(index, nth); if (i >= 0) { do { LoadArg arg = info.Instructions[i] as LoadArg; if (arg != null && arg.Arg >= 1) { ParameterDefinition p = info.Method.Parameters[arg.Arg - 1]; if (p.ParameterType.FullName == "System.Int32") isInt = true; break; } LoadConstantInt constant = info.Instructions[i] as LoadConstantInt; if (constant != null) { Code code = constant.Untyped.OpCode.Code; switch (code) { case Code.Ldc_I4_M1: case Code.Ldc_I4_0: case Code.Ldc_I4_1: case Code.Ldc_I4_2: case Code.Ldc_I4_3: case Code.Ldc_I4_4: case Code.Ldc_I4_5: case Code.Ldc_I4_6: case Code.Ldc_I4_7: case Code.Ldc_I4_8: case Code.Ldc_I4_S: case Code.Ldc_I4: isInt = true; break; } break; } LoadField field = info.Instructions[i] as LoadField; if (field != null) { if (field.Field.FieldType.FullName == "System.Int32") isInt = true; break; } LoadLocal local = info.Instructions[i] as LoadLocal; if (local != null) { VariableDefinition v = info.Method.Body.Variables[local.Variable]; if (v.VariableType.FullName == "System.Int32") isInt = true; break; } LoadStaticField sfield = info.Instructions[i] as LoadStaticField; if (sfield != null) { if (sfield.Field.FieldType.FullName == "System.Int32") isInt = true; break; } } while (false); } return isInt; }
private bool DoMethodCallsNoInstanceMethods(MethodInfo info) { bool foundCall = false; for (int i = 1; i < info.Instructions.Length && !foundCall; ++i) { Call call = info.Instructions[i] as Call; if (call != null && !call.Target.Name.StartsWith("get_")) { MethodInfo targetInfo = Cache.FindMethod(call.Target); if (targetInfo != null) { if (call.IsThisCall(Cache, info, call.Index)) { Log.DebugLine(this, "{0} at {1:X2} is a this call", call.Target.Name, call.Untyped.Offset); foundCall = true; } } // Special case operator== calling Object.Equals with this arg. else if (info.Method.Name == "op_Equality") { if (call.Target.ToString().Contains("System.Object::Equals")) { if (info.Instructions[i - 1].Untyped.OpCode.Code == Code.Ldarg_0 || info.Instructions[i - 2].Untyped.OpCode.Code == Code.Ldarg_0) { Log.DebugLine(this, "{0} at {1:X2} is calling Object.Equals with this", call.Target.Name, call.Untyped.Offset); foundCall = true; } } } // Generics suck: when we call a generic method or a method in a generic type we're // calling an instantiated version of the method which has a different token. To avoid // false positives we need to set foundCall to true so we'll hack up a test here. else { string name = call.Target.ToString(); int j = name.IndexOf(':'); if (name.Contains("<") && name.Contains(">") && j > 0) { name = name.Substring(0, j); if (name.Contains(info.Type.FullName)) { foundCall = true; Log.DebugLine(this, "{0} at {1:X2} seems to be a generic my call", call.Target.Name, call.Untyped.Offset); } } } } } return !foundCall; }
private void DoAddReference(Dictionary<MethodInfo, List<string>> references, MethodInfo info, string name, int offset) { List<string> names; if (!references.TryGetValue(info, out names)) { names = new List<string>(); references.Add(info, names); } if (names.IndexOf(name) < 0) { Log.DebugLine(this, "found {0} at {1:X2} for {2}", name, offset, info.Method.Name); names.Add(name); } }
private void DoGetReferences(Dictionary<MethodInfo, List<string>> references, MethodInfo info) { for (int i = 1; i < info.Instructions.Length; ++i) { do { LoadField field = info.Instructions[i] as LoadField; if (field != null) { if (info.Instructions.LoadsThisArg(i - 1)) DoAddReference(references, info, field.Field.Name, field.Untyped.Offset); break; } LoadFieldAddress addr = info.Instructions[i] as LoadFieldAddress; if (addr != null) { if (info.Instructions.LoadsThisArg(i - 1)) DoAddReference(references, info, addr.Field.Name, addr.Untyped.Offset); break; } Call call = info.Instructions[i] as Call; if (call != null && call.Target.Name.StartsWith("get_")) { if (info.Instructions.LoadsThisArg(i - 1)) DoAddReference(references, info, call.Target.Name, call.Untyped.Offset); break; } } while (false); } }
public CodeBlock(MethodInfo info, int index) { Method = info.Method; Instructions = info.Instructions; Index = index; }
private static bool DoNeedsCheck(MethodInfo info) { bool needs = false; if (!info.Method.IsStatic && !info.Method.IsVirtual) { if (!info.Method.ToString().Contains("CompilerGenerated") && !info.Method.ToString().Contains("_AnonymousMethod")) { if (!info.Type.Namespace.StartsWith("System") && !info.Type.Namespace.StartsWith("Mono")) { needs = true; } // If the type is in the System or Mono namespaces then it can only // be made static if it's access is private or internal to the assembly. else if ((info.Method.Attributes & MethodAttributes.MemberAccessMask) == MethodAttributes.Private) { needs = true; } else if ((info.Method.Attributes & MethodAttributes.MemberAccessMask) == MethodAttributes.FamANDAssem) { needs = true; } } } return needs; }
// Returns the smallest number k such that the instructions in // [offset - k, offset] push a single value onto the stack. private int DoGetPushRange(MethodInfo info, int offset) { int delta = DoGetStackDelta(info.Instructions[offset]); int k = 0; while (delta <= 0 && offset - k >= 0) { ++k; delta += DoGetStackDelta(info.Instructions[offset - k]); } DBC.Assert(offset - k >= 0, "couldn't find the range"); return k; }
public MethodCapture(MethodInfo info) { Method = info.Method; Instructions = info.Instructions; }