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; } } }