internal static bool AreEquivalent(Instruction source, Instruction target) { if (source.OpCode.Code != target.OpCode.Code) return false; if (source.OpCode.Code == Code.Ldstr) return true; //Check the types in ldarg stuff. Sometimes this scheme //could lead us to false positives. We need an analysis //which depends on the context. if (source.IsLoadArgument ()) { // case where 'ldarg this' is used (p.GetSequence () would be 0) if (!Current.HasParameters && !Target.HasParameters) return true; return AreEquivalent (source.GetParameter (Current), target.GetParameter (Target)); } // The same for ldloc / stloc if (source.IsLoadLocal () || source.IsStoreLocal ()) return AreEquivalent (source.GetVariable (Current), target.GetVariable (Target)); //WARNING: Dirty Evil Hack: this should be in the //Pattern class if (source.OpCode.Code == Code.Ret && source.Previous != null && (source.Previous.OpCode.StackBehaviourPush == StackBehaviour.Pushi || source.Previous.OpCode.Code == Code.Ldnull || source.Previous.OpCode.StackBehaviourPush == StackBehaviour.Push1)) return false; //if (source.Operand != target.Operand) if (source.Operand != null && target.Operand != null) { // we're sure that target.Operand is of the same type as source.Operand (same OpCode is used) Instruction si = (source.Operand as Instruction); if (si != null) return (si.Offset == ((Instruction) target.Operand).Offset); IMetadataTokenProvider sm = (source.Operand as IMetadataTokenProvider); if (sm != null) return sm.Equals (target.Operand as IMetadataTokenProvider); if (source.Operand == target.Operand) return true; // last chance: we do call ToString if (source.Operand.ToString () != target.Operand.ToString ()) return false; } return true; }
static bool OriginsMatch (MethodDefinition method, Instruction lhs, Instruction rhs) { bool match = false; object operand1 = lhs.GetOperand (method); object operand2 = rhs.GetOperand (method); if (lhs.OpCode.Code == rhs.OpCode.Code) { if (lhs.IsLoadArgument ()) match = operand1.Equals (operand2); else if (lhs.IsLoadElement ()) match = LoadElementMatch (method, lhs, rhs); else if (lhs.IsLoadIndirect ()) match = LoadIndirectMatch (method, lhs, rhs); else if (lhs.IsLoadLocal ()) match = LocalsMatch (operand1, operand2); } else if (lhs.IsStoreLocal () && rhs.IsLoadLocal ()) match = LocalsMatch (operand1, operand2); else if (lhs.IsLoadLocal () && rhs.IsStoreLocal ()) match = LocalsMatch (operand1, operand2); return match; }
// true == handled // false == unhandled bool CheckIfStored (Instruction afterLdstr) { switch (afterLdstr.OpCode.Code) { case Code.Stfld: // store into field case Code.Stsfld: CheckIdentifier ((afterLdstr.Operand as FieldReference).Name); break; default: if (afterLdstr.IsStoreLocal ()) CheckIdentifier (afterLdstr.GetVariable (method_body.Method).Name); else return false; break; } return true; // handled }
static string CheckDoubleAssignement (MethodDefinition method, Instruction ins, Instruction next) { // for a static field the pattern is // DUP, STSFLD, STSFLD if (ins.OpCode.Code == Code.Stsfld) { if (next.OpCode.Code != Code.Stsfld) return String.Empty; // check that we're assigning the same field twice FieldDefinition fd1 = ins.GetField (); FieldDefinition fd2 = next.GetField (); if (fd1.MetadataToken.RID != fd2.MetadataToken.RID) return String.Empty; return String.Format ("Static field '{0}'.", fd1.Name); } else if (ins.IsStoreLocal ()) { // for a local variable the pattern is // DUP, STLOC, STLOC VariableDefinition vd2 = next.GetVariable (method); // check that we're assigning the same variable twice if (vd2 != null) { VariableDefinition vd1 = ins.GetVariable (method); if (vd1.Index != vd2.Index) return String.Empty; return String.Format ("Local variable '{0}'.", vd1.Name); } else if (next.OpCode.Code == Code.Stfld) { // instance fields are a bit more complex... return CheckDoubleAssignementOnInstanceFields (method, ins, next); } } return String.Empty; }
private void Apply(Instruction instruction, Stack<Value> stack, ICollection<Value> values, IList<Value> locals) { // TODO: this method is a mess and needs to be refactored!! var pushCount = instruction.GetPushCount(); var popCount = instruction.GetPopCount(_method); if (popCount == Int32.MaxValue) popCount = stack.Count; var isCall = instruction.OpCode.FlowControl == FlowControl.Call; var callParams = isCall ? (instruction.Operand as MethodReference).Parameters : NoParams; // List of all popped values var poppedValues = new List<Value>(); var inputArguments = new List<Value>(); for (var i = 0; i < popCount; i++) { // Instruction is a consumer var value = stack.Pop(); poppedValues.Add(value); // If we popped a value for an out parameter, we're not a consumer! // Note that the first pop returns the last argument. if (isCall && i < callParams.Count && callParams[callParams.Count - i - 1].IsOut) { inputArguments.Insert(0, null); // empty slot continue; } value.Consumer = instruction; inputArguments.Insert(0, value); //TODO: not 'this'!?!? } int storeIndex; if (instruction.IsStoreLocal(out storeIndex)) { Debug.Assert(popCount == 1); locals[storeIndex] = poppedValues[0]; } for (var i = 0; i < pushCount; i++) { Value newValue; // Instruction is a producer int loadIndex; if (instruction.IsLoadLocal(out loadIndex)) { Debug.Assert(pushCount == 1); newValue = new Value(instruction); values.Add(newValue); // The local value can be null if we're passing loading a reference // destined for an out parameter. Note that we can get a non-null // value as well, so we have to sort things out when we handle the // call to the method with the out parameter. var localValue = locals[loadIndex]; if (localValue != null) { newValue.AddParents(new[] { localValue }); } } else { newValue = new Value(instruction); values.Add(newValue); newValue.AddParents(inputArguments.Where(a => a != null)); } stack.Push(newValue); } if (isCall) { var argValues = new Value[callParams.Count]; for (var i = 0; i < argValues.Length; i++) { argValues[i] = poppedValues[argValues.Length - i - 1]; } for (var i = 0; i < callParams.Count; i++) { // First of poppedValues is last argument var inputArgument = poppedValues[callParams.Count - i - 1]; if (!callParams[i].IsRef() && !callParams[i].IsOut) continue; var newValue = new Value(instruction); var storeAtIndex = (inputArgument.Producer.Operand as VariableDefinition).Index; // Add all input values (including any refs) as parents! for (var j = 0; j < callParams.Count; j++) { if (callParams[j].IsOut) continue; newValue.AddParents(new[] { argValues[j] }); } // Don't push onto the stack, but save the value and store // it in the locals array. values.Add(newValue); locals[storeAtIndex] = newValue; } } }
static Instruction LocalTraceBack (IMethodSignature method, Instruction ins) { ins = ins.TraceBack (method); while (ins != null) { if (ins.IsLoadLocal () || ins.IsStoreLocal ()) return ins; ins = ins.TraceBack (method); } return null; }