public void ParseEmptyFormatting(string input, string expectedOutput) { var processor = new ParameterFormattingProcessor(); var info = processor.ParseParameterFormatting(input); Assert.NotNull(info); Assert.Equal(expectedOutput, info.Format); }
public void ParseComplexFormatting() { var processor = new ParameterFormattingProcessor(); var info = processor.ParseParameterFormatting("This is a {fileName} test with id = '{id}' and {fileName} but don't replace fileName"); Assert.Equal("This is a {0} test with id = '{1}' and {0} but don't replace fileName", info.Format); Assert.Equal("fileName", info.ParameterNames[0]); Assert.Equal("id", info.ParameterNames[1]); }
public void ParseSimpleFormatting() { var processor = new ParameterFormattingProcessor(); var info = processor.ParseParameterFormatting("This is a {fileName}"); Assert.NotNull(info); Assert.Equal("This is a {0}", info.Format); Assert.Equal("fileName", info.ParameterNames[0]); }
IEnumerable <Instruction> ProcessTimeAttribute(MethodDefinition methodDefinition, FieldDefinition formattedFieldDefinition) { // Load everything for a string format var timeAttribute = methodDefinition.GetTimeAttribute(); if (timeAttribute != null) { var value = timeAttribute.ConstructorArguments.FirstOrDefault().Value as string; if (!string.IsNullOrWhiteSpace(value)) { var info = parameterFormattingProcessor.ParseParameterFormatting(value); yield return(Instruction.Create(OpCodes.Ldarg_0)); yield return(Instruction.Create(OpCodes.Ldstr, info.Format)); yield return(Instruction.Create(OpCodes.Ldc_I4, info.ParameterNames.Count)); yield return(Instruction.Create(OpCodes.Newarr, ModuleWeaver.TypeSystem.ObjectReference)); for (var i = 0; i < info.ParameterNames.Count; i++) { yield return(Instruction.Create(OpCodes.Dup)); yield return(Instruction.Create(OpCodes.Ldc_I4, i)); var field = stateMachineType.Fields.FirstOrDefault(x => x.Name.Equals(info.ParameterNames[i])); if (field == null) { ModuleWeaver.LogError($"Parameter '{info.ParameterNames[i]}' is not available on the async state machine. Probably it has been optimized away by the compiler. Please update the format so it excludes this parameter."); yield return(Instruction.Create(OpCodes.Ldnull)); } else { yield return(Instruction.Create(OpCodes.Ldarg_0)); yield return(Instruction.Create(OpCodes.Ldfld, field)); if (field.FieldType.IsBoxingRequired(ModuleWeaver.TypeSystem.ObjectReference)) { yield return(Instruction.Create(OpCodes.Box, ModuleWeaver.ModuleDefinition.ImportReference(field.FieldType))); } } yield return(Instruction.Create(OpCodes.Stelem_Ref)); } yield return(Instruction.Create(OpCodes.Call, ModuleWeaver.StringFormatWithArray)); } else { // Load null a string yield return(Instruction.Create(OpCodes.Ldarg_0)); yield return(Instruction.Create(OpCodes.Ldnull)); } yield return(Instruction.Create(OpCodes.Stfld, formattedFieldDefinition)); } }
IEnumerable <Instruction> GetWriteTimeInstruction(MethodDefinition methodDefinition) { yield return(Instruction.Create(OpCodes.Ldarg_0)); yield return(Instruction.Create(OpCodes.Ldfld, stopwatchField)); yield return(Instruction.Create(OpCodes.Call, ModuleWeaver.StopMethod)); var logWithMessageMethod = ModuleWeaver.LogWithMessageMethod; var logMethod = ModuleWeaver.LogMethod; if (logWithMessageMethod != null) { // Important notes: // 1. Because async works with state machines, use the state machine & fields instead of method & variables. // 2. The ldarg_0 calls are required to load the state machine class and is required before every field call. //var moveNextMethodDefinition = stateMachineType.Methods.First(x => x.Name == "MoveNext"); //var formattedVariableDefinition = new VariableDefinition(ModuleWeaver.ModuleDefinition.TypeSystem.String); //moveNextMethodDefinition.Body.Variables.Add(formattedVariableDefinition); var formattedFieldDefinition = stateMachineType.Fields.FirstOrDefault(x => x.Name.Equals("methodTimerMessage")); if (formattedFieldDefinition == null) { formattedFieldDefinition = new FieldDefinition("methodTimerMessage", FieldAttributes.Private | FieldAttributes.CompilerControlled, ModuleWeaver.ModuleDefinition.TypeSystem.String); stateMachineType.Fields.Add(formattedFieldDefinition); } // Load everything for a string format var timeAttribute = methodDefinition.GetTimeAttribute(); if (timeAttribute != null) { var value = timeAttribute.ConstructorArguments.FirstOrDefault().Value as string; if (!string.IsNullOrWhiteSpace(value)) { var info = parameterFormattingProcessor.ParseParameterFormatting(value); yield return(Instruction.Create(OpCodes.Ldarg_0)); yield return(Instruction.Create(OpCodes.Ldstr, info.Format)); yield return(Instruction.Create(OpCodes.Ldc_I4, info.ParameterNames.Count)); yield return(Instruction.Create(OpCodes.Newarr, ModuleWeaver.ModuleDefinition.TypeSystem.Object)); for (var i = 0; i < info.ParameterNames.Count; i++) { yield return(Instruction.Create(OpCodes.Dup)); yield return(Instruction.Create(OpCodes.Ldc_I4, i)); var field = stateMachineType.Fields.FirstOrDefault(x => x.Name.Equals(info.ParameterNames[i])); if (field == null) { ModuleWeaver.LogError($"Parameter '{info.ParameterNames[i]}' is not available on the async state machine. Probably it has been optimized away by the compiler. Please update the format so it excludes this parameter."); yield return(Instruction.Create(OpCodes.Ldnull)); } else { yield return(Instruction.Create(OpCodes.Ldarg_0)); yield return(Instruction.Create(OpCodes.Ldfld, field)); if (field.FieldType.IsBoxingRequired(ModuleWeaver.ModuleDefinition.TypeSystem.Object)) { yield return(Instruction.Create(OpCodes.Box, ModuleWeaver.ModuleDefinition.ImportReference(field.FieldType))); } } yield return(Instruction.Create(OpCodes.Stelem_Ref)); } yield return(Instruction.Create(OpCodes.Call, ModuleWeaver.StringFormatWithArray)); } else { // Load null a string yield return(Instruction.Create(OpCodes.Ldarg_0)); yield return(Instruction.Create(OpCodes.Ldnull)); } yield return(Instruction.Create(OpCodes.Stfld, formattedFieldDefinition)); } // Handle call to log method yield return(Instruction.Create(OpCodes.Ldtoken, methodDefinition)); yield return(Instruction.Create(OpCodes.Ldtoken, methodDefinition.DeclaringType)); yield return(Instruction.Create(OpCodes.Call, ModuleWeaver.GetMethodFromHandle)); yield return(Instruction.Create(OpCodes.Ldarg_0)); yield return(Instruction.Create(OpCodes.Ldfld, stopwatchField)); yield return(Instruction.Create(OpCodes.Call, ModuleWeaver.ElapsedMilliseconds)); yield return(Instruction.Create(OpCodes.Ldarg_0)); yield return(Instruction.Create(OpCodes.Ldfld, formattedFieldDefinition)); yield return(Instruction.Create(OpCodes.Call, logWithMessageMethod)); } else if (logMethod != null) { yield return(Instruction.Create(OpCodes.Ldtoken, methodDefinition)); yield return(Instruction.Create(OpCodes.Ldtoken, methodDefinition.DeclaringType)); yield return(Instruction.Create(OpCodes.Call, ModuleWeaver.GetMethodFromHandle)); yield return(Instruction.Create(OpCodes.Ldarg_0)); yield return(Instruction.Create(OpCodes.Ldfld, stopwatchField)); yield return(Instruction.Create(OpCodes.Call, ModuleWeaver.ElapsedMilliseconds)); yield return(Instruction.Create(OpCodes.Call, logMethod)); } else { yield return(Instruction.Create(OpCodes.Ldstr, methodDefinition.MethodName())); yield return(Instruction.Create(OpCodes.Ldarg_0)); yield return(Instruction.Create(OpCodes.Ldfld, stopwatchField)); yield return(Instruction.Create(OpCodes.Call, ModuleWeaver.ElapsedMilliseconds)); yield return(Instruction.Create(OpCodes.Box, ModuleWeaver.ModuleDefinition.TypeSystem.Int64)); yield return(Instruction.Create(OpCodes.Ldstr, "ms")); yield return(Instruction.Create(OpCodes.Call, ModuleWeaver.ConcatMethod)); yield return(Instruction.Create(OpCodes.Call, ModuleWeaver.TraceWriteLineMethod)); } }
IEnumerable <Instruction> ProcessTimeAttribute(MethodDefinition methodDefinition, FieldDefinition formattedFieldDefinition) { // Load everything for a string format var timeAttribute = methodDefinition.GetTimeAttribute(); if (timeAttribute != null) { var value = timeAttribute.ConstructorArguments.FirstOrDefault().Value as string; if (!string.IsNullOrWhiteSpace(value)) { // Note: no need to validate, already done in AssemblyProcessor::ProcessMethod var info = parameterFormattingProcessor.ParseParameterFormatting(value); yield return(Instruction.Create(OpCodes.Ldarg_0)); yield return(Instruction.Create(OpCodes.Ldstr, info.Format)); yield return(Instruction.Create(OpCodes.Ldc_I4, info.ParameterNames.Count)); yield return(Instruction.Create(OpCodes.Newarr, ModuleWeaver.TypeSystem.ObjectReference)); for (var i = 0; i < info.ParameterNames.Count; i++) { var parameterName = info.ParameterNames[i]; yield return(Instruction.Create(OpCodes.Dup)); yield return(Instruction.Create(OpCodes.Ldc_I4, i)); if (string.Equals(parameterName, "this")) { // Field name is <>4__this parameterName = "<>4__this"; if (!stateMachineType.Fields.Any(x => x.Name.Equals(parameterName))) { // {this} could be optimized away, let's add it for the user InjectThisIntoStateMachine(methodDefinition); } } var field = stateMachineType.Fields.FirstOrDefault(x => x.Name.Equals(parameterName)); if (field is null) { ModuleWeaver.LogError($"Parameter '{parameterName}' is not available on the async state machine. Probably it has been optimized away by the compiler. Please update the format so it excludes this parameter."); yield break; } else { yield return(Instruction.Create(OpCodes.Ldarg_0)); yield return(Instruction.Create(OpCodes.Ldfld, field)); if (field.FieldType.IsBoxingRequired(ModuleWeaver.TypeSystem.ObjectReference)) { yield return(Instruction.Create(OpCodes.Box, ModuleWeaver.ModuleDefinition.ImportReference(field.FieldType))); } } yield return(Instruction.Create(OpCodes.Stelem_Ref)); } yield return(Instruction.Create(OpCodes.Call, ModuleWeaver.StringFormatWithArray)); } else { // Load null a string yield return(Instruction.Create(OpCodes.Ldarg_0)); yield return(Instruction.Create(OpCodes.Ldnull)); } yield return(Instruction.Create(OpCodes.Stfld, formattedFieldDefinition)); } }