public void Process(MethodDefinition method, bool returnVoid, ReferenceContainer references) { try { InnerProcess(method, returnVoid, references); } catch (Exception exception) { throw new Exception(string.Format("An error occurred processing '{0}'. Error: {1}", method.FullName, exception.Message)); } }
private void WeaveMethod(MethodDefinition methodDefinition, ReferenceContainer references) { var asyncAttribute = methodDefinition.CustomAttributes.FirstOrDefault(_ => _.AttributeType.Name == "AsyncStateMachineAttribute"); if (asyncAttribute == null) { var returnVoid = methodDefinition.ReturnType == ModuleDefinition.TypeSystem.Void; var methodProcessor = new MethodProcessor(); methodProcessor.Process(methodDefinition, returnVoid, references); return; } LogError(string.Format("Could not process '{0}'. async methods are not currently supported. Feel free to do it yourself if you want this feature.", methodDefinition.FullName)); }
private Instruction InjectIlForCatch(ILProcessor processor, Instruction beforeReturn, ReferenceContainer references) { /* * stloc.2 * * nop * ldloc.2 * call void [Yalf]Yalf.Log::TraceException(class [mscorlib]System.Exception) * * nop * ldloc.0 * ldloc.2 * callvirt instance void [Yalf]Yalf.IContext::PreserveStackTrace(class [mscorlib]System.Exception) * * nop * rethrow */ var exceptionVariable = new VariableDefinition(references.ExceptionType); body.Variables.Add(exceptionVariable); var beginCatch = beforeReturn.Prepend(processor.Create(OpCodes.Stloc, exceptionVariable), processor); beginCatch.Append( processor.Create(OpCodes.Nop), processor) .AppendLdloc(processor, exceptionVariable.Index) .Append(processor.Create(OpCodes.Call, references.TraceExceptionMethod), processor) .Append(processor.Create(OpCodes.Nop), processor) .AppendLdloc(processor, contextVar.Index) .AppendLdloc(processor, exceptionVariable.Index) .Append(processor.Create(OpCodes.Callvirt, references.PreserveStackTraceMethod), processor) .Append(processor.Create(OpCodes.Nop), processor) .Append(processor.Create(OpCodes.Rethrow), processor); return(beginCatch); }
void InjectContext(ILProcessor processor, Instruction firstInstruction, MethodDefinition method, ReferenceContainer references) { /* * nop * ldstr "Namespace.Class.Method" * ldc.i4.1 * newarr object * stloc.s CS$0$0001 * ldloc.s CS$0$0001 * ldc.i4.0 * ldarg.1 * box int32 * stelem.ref * ldloc.s CS$0$0001 * call class [Yalf]Yalf.IContext [Yalf]Yalf.Log::MethodContext(string, object[]) * stloc.0 */ contextVar = new VariableDefinition(references.IContextType); body.Variables.Insert(0, contextVar); int objectParamsArrayIndex = method.AddVariable <object[]>(); // Generate MethodContext calling method name var builder = new StringBuilder(); builder.Append(string.Join(".", method.DeclaringType.Namespace, FormatType(method.DeclaringType), FormatMethod(method))); var current = firstInstruction .Prepend(processor.Create(OpCodes.Ldstr, builder.ToString()), processor); // Create object[] for MethodContext current = current .AppendLdcI4(processor, method.Parameters.Count) .Append(processor.Create(OpCodes.Newarr, method.Module.ImportType <object>()), processor) .AppendStloc(processor, objectParamsArrayIndex); var nonStaticMethodAddOne = method.IsStatic ? 0 : 1; // Set object[] values for (int i = 0; i < method.Parameters.Count; i++) { current = current .AppendLdloc(processor, objectParamsArrayIndex) .AppendLdcI4(processor, i); var paramType = method.Parameters[i].ParameterType; if (paramType.MetadataType == MetadataType.UIntPtr || paramType.MetadataType == MetadataType.FunctionPointer || paramType.MetadataType == MetadataType.IntPtr || paramType.MetadataType == MetadataType.Pointer) { // don't store pointer types into object[] (we can't ToString them anyway) // store type name as string instead current = current.AppendLdstr(processor, paramType.FullName); } else { current = current .AppendLdarg(processor, i + nonStaticMethodAddOne) .AppendBoxAndResolveRefIfNecessary(processor, paramType); } current = current.Append(processor.Create(OpCodes.Stelem_Ref), processor); } // Call Log.MethodContext current .AppendLdloc(processor, objectParamsArrayIndex) .Append(processor.Create(OpCodes.Call, references.MethodContextMethod), processor) .AppendStloc(processor, contextVar.Index); }
private Instruction InjectIlForFinaly(ILProcessor processor, Instruction beforeReturn, ReferenceContainer references) { // wrapped in nop (for try catch handling) // load context then call dispose /* * nop * ldloc.0 * callvirt instance void [Yalf]Yalf.IContext::Dispose() * nop * endfinally */ var beginFinally = beforeReturn.Prepend(processor.Create(OpCodes.Nop), processor); beginFinally.AppendLdloc(processor, contextVar.Index) .Append(processor.Create(OpCodes.Callvirt, references.DisposeMethod), processor) .Append(processor.Create(OpCodes.Nop), processor) .Append(processor.Create(OpCodes.Endfinally), processor); return(beginFinally); }
private void InjectIlForDispose(ILProcessor processor, Instruction returnInstruction, ReferenceContainer references) { // load context then call dispose /* * nop * ldloc.0 * callvirt instance void [Yalf]Yalf.IContext::Dispose() * return mumbo jumbo */ returnInstruction.AppendLdloc(processor, contextVar.Index) .Append(processor.Create(OpCodes.Callvirt, references.DisposeMethod), processor); }
private void InnerProcess(MethodDefinition method, bool returnVoid, ReferenceContainer references) { body = method.Body; body.SimplifyMacros(); if (body.Instructions.Count <= 3) return; // nothing to do (empty method) HandleReturnType(method, returnVoid, references); var ilProcessor = body.GetILProcessor(); var firstInstruction = FirstInstructionSkipCtor(method); firstInstruction = RejigFirstInstruction(ilProcessor, firstInstruction); InjectContext(ilProcessor, firstInstruction, method, references); var returnInstruction = FixReturns(); var beforeReturn = Instruction.Create(OpCodes.Nop); ilProcessor.InsertBefore(returnInstruction, beforeReturn); // exclude try-catch from constructors (lot's of pain otherwise) if (!method.IsConstructor) { var beginCatch = InjectIlForCatch(ilProcessor, beforeReturn, references); var beginFinally = InjectIlForFinaly(ilProcessor, beforeReturn, references); var catchHandler = new ExceptionHandler(ExceptionHandlerType.Catch) { TryStart = firstInstruction, TryEnd = beginCatch, CatchType = references.ExceptionType, HandlerStart = beginCatch, HandlerEnd = beginFinally, }; body.ExceptionHandlers.Add(catchHandler); var finallyHandler = new ExceptionHandler(ExceptionHandlerType.Finally) { TryStart = firstInstruction, TryEnd = beginFinally, HandlerStart = beginFinally, HandlerEnd = beforeReturn, }; body.ExceptionHandlers.Add(finallyHandler); } else { InjectIlForDispose(ilProcessor, returnInstruction, references); } body.InitLocals = true; body.OptimizeMacros(); }
private Instruction InjectIlForFinaly(ILProcessor processor, Instruction beforeReturn, ReferenceContainer references) { // wrapped in nop (for try catch handling) // load context then call dispose /* nop ldloc.0 callvirt instance void [Yalf]Yalf.IContext::Dispose() nop endfinally */ var beginFinally = beforeReturn.Prepend(processor.Create(OpCodes.Nop), processor); beginFinally.AppendLdloc(processor, contextVar.Index) .Append(processor.Create(OpCodes.Callvirt, references.DisposeMethod), processor) .Append(processor.Create(OpCodes.Nop), processor) .Append(processor.Create(OpCodes.Endfinally), processor); return beginFinally; }
private void InjectIlForDispose(ILProcessor processor, Instruction returnInstruction, ReferenceContainer references) { // load context then call dispose /* nop ldloc.0 callvirt instance void [Yalf]Yalf.IContext::Dispose() return mumbo jumbo */ returnInstruction.AppendLdloc(processor, contextVar.Index) .Append(processor.Create(OpCodes.Callvirt, references.DisposeMethod), processor); }
private Instruction InjectIlForCatch(ILProcessor processor, Instruction beforeReturn, ReferenceContainer references) { /* stloc.2 nop ldloc.2 call void [Yalf]Yalf.Log::TraceException(class [mscorlib]System.Exception) nop ldloc.0 ldloc.2 callvirt instance void [Yalf]Yalf.IContext::PreserveStackTrace(class [mscorlib]System.Exception) nop rethrow */ var exceptionVariable = new VariableDefinition(references.ExceptionType); body.Variables.Add(exceptionVariable); var beginCatch = beforeReturn.Prepend(processor.Create(OpCodes.Stloc, exceptionVariable), processor); beginCatch.Append( processor.Create(OpCodes.Nop), processor) .AppendLdloc(processor, exceptionVariable.Index) .Append(processor.Create(OpCodes.Call, references.TraceExceptionMethod), processor) .Append(processor.Create(OpCodes.Nop), processor) .AppendLdloc(processor, contextVar.Index) .AppendLdloc(processor, exceptionVariable.Index) .Append(processor.Create(OpCodes.Callvirt, references.PreserveStackTraceMethod), processor) .Append(processor.Create(OpCodes.Nop), processor) .Append(processor.Create(OpCodes.Rethrow), processor); return beginCatch; }
void InjectContext(ILProcessor processor, Instruction firstInstruction, MethodDefinition method, ReferenceContainer references) { /* nop ldstr "Namespace.Class.Method" ldc.i4.1 newarr object stloc.s CS$0$0001 ldloc.s CS$0$0001 ldc.i4.0 ldarg.1 box int32 stelem.ref ldloc.s CS$0$0001 call class [Yalf]Yalf.IContext [Yalf]Yalf.Log::MethodContext(string, object[]) stloc.0 */ contextVar = new VariableDefinition(references.IContextType); body.Variables.Insert(0, contextVar); int objectParamsArrayIndex = method.AddVariable<object[]>(); // Generate MethodContext calling method name var builder = new StringBuilder(); builder.Append(string.Join(".", method.DeclaringType.Namespace, FormatType(method.DeclaringType), FormatMethod(method))); var current = firstInstruction .Prepend(processor.Create(OpCodes.Ldstr, builder.ToString()), processor); // Create object[] for MethodContext current = current .AppendLdcI4(processor, method.Parameters.Count) .Append(processor.Create(OpCodes.Newarr, method.Module.ImportType<object>()), processor) .AppendStloc(processor, objectParamsArrayIndex); var nonStaticMethodAddOne = method.IsStatic ? 0 : 1; // Set object[] values for (int i = 0; i < method.Parameters.Count; i++) { current = current .AppendLdloc(processor, objectParamsArrayIndex) .AppendLdcI4(processor, i); var paramType = method.Parameters[i].ParameterType; if (paramType.MetadataType == MetadataType.UIntPtr || paramType.MetadataType == MetadataType.FunctionPointer || paramType.MetadataType == MetadataType.IntPtr || paramType.MetadataType == MetadataType.Pointer) { // don't store pointer types into object[] (we can't ToString them anyway) // store type name as string instead current = current.AppendLdstr(processor, paramType.FullName); } else { current = current .AppendLdarg(processor, i + nonStaticMethodAddOne) .AppendBoxAndResolveRefIfNecessary(processor, paramType); } current = current.Append(processor.Create(OpCodes.Stelem_Ref), processor); } // Call Log.MethodContext current .AppendLdloc(processor, objectParamsArrayIndex) .Append(processor.Create(OpCodes.Call, references.MethodContextMethod), processor) .AppendStloc(processor, contextVar.Index); }
private void HandleReturnType(MethodDefinition method, bool returnVoid, ReferenceContainer references) { _returnType = null; if (!returnVoid) { _returnType = method.ReturnType; _recordReturnMethod = references.CreateRecordReturnMethod(_returnType); } }
public void Execute() { IsDebugBuild = DefineConstants.Any(x => x.ToLower() == "debug"); var references = new ReferenceContainer(ModuleDefinition, AssemblyResolver); WeaveMethods(references); }
private void WeaveMethods(ReferenceContainer references) { var methodDefinitions = SelectMethods(ModuleDefinition, NoLogAttributeName); foreach (var methodDefinition in methodDefinitions) { WeaveMethod(methodDefinition, references); } }