private VariableDefinition StoreVariablesInObjectArray(RewriteContext context, List <VariableDefinition> variables) { var arrayVariable = new VariableDefinition(context.Method.Module.Import(typeof(Object[]))); context.Processor.Body.Variables.Add(arrayVariable); context.Insert(Instruction.Create(OpCodes.Ldc_I4, variables.Count)); context.Insert(Instruction.Create(OpCodes.Newarr, context.Method.Module.Import(typeof(Object)))); context.Insert(Instruction.Create(OpCodes.Stloc, arrayVariable)); for (int i = 0; i < variables.Count; ++i) { VariableDefinition variable = variables[i]; TypeReference variableType = variable.VariableType; context.Insert(Instruction.Create(OpCodes.Ldloc, arrayVariable)); context.Insert(Instruction.Create(OpCodes.Ldc_I4, i)); context.Insert(Instruction.Create(OpCodes.Ldloc, variable)); if (variableType.IsByReference) { variableType = StripByReference(context, variable.VariableType); context.Insert(Instruction.Create(OpCodes.Ldobj, variableType)); } if (variableType.IsValueType) { context.Insert(Instruction.Create(OpCodes.Box, variableType)); } context.Insert(Instruction.Create(OpCodes.Stelem_Ref)); } return(arrayVariable); }
private static TypeReference StripByReference(RewriteContext context, TypeReference type) { return(context.Method.Module.Import(type.Resolve())); }
public bool Rewrite(Configuration configuration, MethodDefinition method, IRewriteTargetMatcher rewriteTargetMatcher) { bool rewritten = false; if (method.Body == null) { return rewritten; } ILProcessor processor = method.Body.GetILProcessor(); // Iterate copy of initial instructions without modifications foreach (var instruction in method.Body.Instructions.ToList()) { MethodReference calledMethod; if (_instructionHelper.TryGetCall(instruction, out calledMethod)) { List<IRewriteTarget> targets = rewriteTargetMatcher.GetMatchingTargets(calledMethod).ToList(); if (targets.Count == 0) { continue; } RewriteContext context = new RewriteContext(configuration, processor, instruction, targets, calledMethod); RewriteInstruction(context); rewritten = true; } } return rewritten; }
private static void PushMethod(RewriteContext context, MethodReference method) { context.Insert(Instruction.Create(OpCodes.Ldtoken, method)); context.Insert(Instruction.Create(OpCodes.Call, context.Method.Module.Import(GetMethodFromHandleMethod))); }
private List<VariableDefinition> StoreArgumentsInVariables(RewriteContext context) { List<TypeReference> parameters = GetParameters(context.Method); List<VariableDefinition> variables = parameters .Select(parameterType => new VariableDefinition(ResolveGenericParameters(parameterType, context.Method))) .ToList(); context.AddVariables(variables); // Add items on stack to array. Note that we pop them backwards, so // we add them backwards as well. for (int i = parameters.Count - 1; i >= 0; --i) { TypeReference parameterType = ResolveGenericParameters(parameters[i], context.Method); if (parameterType.IsByReference) { } else if (parameterType.IsValueType) { // If the declaring type of the method is a value type, it's *address* is pushed on the stack // and we need to dereference it using ldobj to get the actual value. if (context.Method.HasThis && i == 0) { context.Insert(Instruction.Create(OpCodes.Ldobj, parameterType)); } } context.Insert(Instruction.Create(OpCodes.Stloc, variables[i])); } return variables; }
private VariableDefinition StoreVariablesInObjectArray(RewriteContext context, List<VariableDefinition> variables) { var arrayVariable = new VariableDefinition(context.Method.Module.Import(typeof(Object[]))); context.Processor.Body.Variables.Add(arrayVariable); context.Insert(Instruction.Create(OpCodes.Ldc_I4, variables.Count)); context.Insert(Instruction.Create(OpCodes.Newarr, context.Method.Module.Import(typeof(Object)))); context.Insert(Instruction.Create(OpCodes.Stloc, arrayVariable)); for (int i = 0; i < variables.Count; ++i) { VariableDefinition variable = variables[i]; TypeReference variableType = variable.VariableType; context.Insert(Instruction.Create(OpCodes.Ldloc, arrayVariable)); context.Insert(Instruction.Create(OpCodes.Ldc_I4, i)); context.Insert(Instruction.Create(OpCodes.Ldloc, variable)); if (variableType.IsByReference) { variableType = StripByReference(context, variable.VariableType); context.Insert(Instruction.Create(OpCodes.Ldobj, variableType)); } if (variableType.IsValueType) { context.Insert(Instruction.Create(OpCodes.Box, variableType)); } context.Insert(Instruction.Create(OpCodes.Stelem_Ref)); } return arrayVariable; }
private void PushReturnValueOnStack(RewriteContext context, VariableDefinition interceptorResultVariable, bool isVoidMethod) { if (!isVoidMethod) { var type = context.Method.Module.Import(typeof(InterceptorResult<>)); var genericType = new GenericInstanceType(type); genericType.GenericArguments.Add(GetReturnValue(context.Method)); var returnValueProperty = type.Resolve().Properties.First(property => property.Name == "ReturnValue"); var getMethod = context.Method.Module.Import(returnValueProperty.GetMethod); getMethod.DeclaringType = genericType; context.Insert(Instruction.Create(OpCodes.Ldloc, interceptorResultVariable)); context.Insert(Instruction.Create(OpCodes.Callvirt, getMethod)); } }
private void RewriteInstruction(RewriteContext context) { bool isVoidMethod = context.Method.ReturnType.FullName == "System.Void" && !IsConstructor(context.Method); // As per: https://groups.google.com/forum/#!topic/mono-cecil/v6LfFLj9eAE context.Processor.Body.SimplifyMacros(); List<VariableDefinition> variables = StoreArgumentsInVariables(context); VariableDefinition arrayVariable = StoreVariablesInObjectArray(context, variables); VariableDefinition interceptorResultVariable = InvokeReplacementMethod(context, arrayVariable, isVoidMethod); Instruction currentPosition = context.CurrentPosition; // If the method was intercepted, we need to push the return value on the stack. PushReturnValueOnStack(context, interceptorResultVariable, isVoidMethod); // And copy any out/reference parameters back to their original address. CopyReferenceParametersToAddress(context, arrayVariable, variables); // Then skip over the part for when we didn't intercept. context.Insert(Instruction.Create(OpCodes.Br, context.OriginalInstruction.Next)); // If it wasn't we need to rebuild the stack and invoke the original method. Instruction invokeOriginalStartInstruction = InvokeOriginalMethod(context, variables); // Inject an instruction to jump based on whether the instruction was intercepted. var interceptedPropertyGetter = context.Method.Module.Import(InterceptedProperty.GetGetMethod()); context.InsertAfter(currentPosition, new[] { Instruction.Create(OpCodes.Ldloc, interceptorResultVariable), Instruction.Create(OpCodes.Callvirt, interceptedPropertyGetter), Instruction.Create(OpCodes.Brfalse, invokeOriginalStartInstruction) }); context.Processor.Remove(context.OriginalInstruction); context.Processor.Body.OptimizeMacros(); }
private VariableDefinition InvokeReplacementMethod(RewriteContext context, VariableDefinition arrayVariable, bool isVoidMethod) { EventRewriteTarget eventTarget = context.Targets[0] as EventRewriteTarget; var importedReplacement = GetInterceptorMethod(context.Method, isVoidMethod, eventTarget); context.Insert(Instruction.Create(OpCodes.Ldloc, arrayVariable)); PushMethod(context, context.Method); if (eventTarget != null) { PushMethod(context, context.Method.Module.Import(eventTarget.AddMethod)); PushMethod(context, context.Method.Module.Import(eventTarget.RemoveMethod)); } context.Insert(Instruction.Create(OpCodes.Call, importedReplacement)); var interceptorResultVariable = new VariableDefinition(GetInterceptorResultType(context.Method, isVoidMethod)); context.Processor.Body.Variables.Add(interceptorResultVariable); context.Insert(Instruction.Create(OpCodes.Stloc, interceptorResultVariable)); return interceptorResultVariable; }
private Instruction InvokeOriginalMethod(RewriteContext context, List<VariableDefinition> variables) { Instruction instruction; Instruction firstInstruction = null; for (int i = 0; i < variables.Count; ++i) { var variable = variables[i]; // If we're calling an instance method of a value type, we need to put the // *address* of the instance on the stack. if (i == 0 && context.Method.HasThis && context.Method.DeclaringType.IsValueType) { instruction = context.Insert(Instruction.Create(OpCodes.Ldloca, variable)); } else { instruction = context.Insert(Instruction.Create(OpCodes.Ldloc, variable)); } firstInstruction = firstInstruction ?? instruction; } instruction = context.Insert(context.OriginalInstruction); firstInstruction = firstInstruction ?? instruction; return firstInstruction; }
private void CopyReferenceParametersToAddress(RewriteContext context, VariableDefinition arrayVariable, List<VariableDefinition> variables) { for (int i = 0; i < variables.Count; ++i) { if (variables[i].VariableType.IsByReference) { var variableType = StripByReference(context, variables[i].VariableType); // Push address context.Insert(Instruction.Create(OpCodes.Ldloc, variables[i])); // Push value from arguments array context.Insert(Instruction.Create(OpCodes.Ldloc, arrayVariable)); context.Insert(Instruction.Create(OpCodes.Ldc_I4, i)); context.Insert(Instruction.Create(OpCodes.Ldelem_Any, context.Method.Module.Import(typeof(object)))); context.Insert(Instruction.Create(OpCodes.Unbox_Any, variableType)); // Copy value to address context.Insert(Instruction.Create(OpCodes.Stind_Ref)); } } }
private static TypeReference StripByReference(RewriteContext context, TypeReference type) { return context.Method.Module.Import(type.Resolve()); }