private static void ProcessEachMethod(MethodDefinition methodDefinition) { var body = methodDefinition.Body; var handlers = body.ExceptionHandlers; if (handlers.Any(x => x.HandlerType != ExceptionHandlerType.Finally)) { Debug.LogWarning(methodDefinition.FullName + " contains not finally clause!"); return; } using (ScopedProcessor processor = body.GetILProcessor()) { processor.Simplify(); processor.PreProcessOptimization(); var exceptionHandlerTrees = ExceptionHandlerTree.Create(body); var totalDepth = exceptionHandlerTrees.Max(x => x.TotalDepth); for (var i = 0; i < totalDepth; i++) { body.Variables.Add(new VariableDefinition(methodDefinition.Module.TypeSystem.Int32)); } foreach (var exceptionHandlerTree in exceptionHandlerTrees) { ProcessComplexTree(processor, exceptionHandlerTree); } } handlers.Clear(); }
private void RewriteInner(ScopedProcessor processor, Instruction box1, Instruction box2, Instruction callInstruction) { processor .Remove(box1) .Remove(box2) .Replace(callInstruction, Instruction.Create(OpCodes.And)); }
private static void ProcessHandler(ScopedProcessor processor, ExceptionHandlerTree exceptionHandlerTree) { var iterator = exceptionHandlerTree.Handler.HandlerStart; for (var end = exceptionHandlerTree.Handler.HandlerEnd; iterator != end; iterator = iterator.Next) { if (iterator.OpCode.Code == Code.Endfinally) { break; } } if (iterator is null) { return; } var variableDefinitions = processor.Processor.Body.Variables; if (exceptionHandlerTree.IsRelay) { ProcessRelayHandler(processor, exceptionHandlerTree, variableDefinitions, exceptionHandlerTree.CurrentDepth, iterator); } else { ProcessSingleHandler(processor, exceptionHandlerTree, variableDefinitions, exceptionHandlerTree.CurrentDepth, iterator); } }
private void TryProcessEachInstruction(Instruction currentInstruction, ScopedProcessor processor) { if (!IsValidBoxInstruction(currentInstruction, out var enumTypeReference) || !enumTypeReference.TryToDefinition(out var enumTypeDefinition) || !enumTypeDefinition.IsEnum) { return; } TryRewriteFlowAnalysis(currentInstruction, processor, enumTypeDefinition); }
private static void RewriteConstantString(ScopedProcessor processor, TypeDefinition enumTypeDefinition, Instruction loadTokenInstruction, Instruction getTypeFromHandleInstruction, Instruction loadStrInstruction, Instruction isDefinedInstruction) { var name = (string)loadStrInstruction.Operand; var constExpr = enumTypeDefinition.Fields.Skip(1).Any(x => x.Name == name); processor .Remove(loadTokenInstruction) .Remove(getTypeFromHandleInstruction) .Remove(loadStrInstruction) .Replace(isDefinedInstruction, InstructionUtility.LoadConstant(constExpr)); }
public void Process(ModuleDefinition systemModuleDefinition, MethodDefinition methodDefinition) { using (ScopedProcessor processor = methodDefinition.Body.GetILProcessor()) { var instructions = methodDefinition.Body.Instructions; for (var index = instructions.Count - 1; index >= 0; index--) { TryProcessEachInstruction(instructions[index], processor); } } }
private static void ProcessComplexTree(ScopedProcessor processor, ExceptionHandlerTree exceptionHandlerTree) { foreach (var tree in exceptionHandlerTree.Trees) { ProcessComplexTree(processor, tree); } if (exceptionHandlerTree.Handler is null) { return; } ProcessSimpleTree(processor, exceptionHandlerTree); }
public void Process(ModuleDefinition systemModuleDefinition, MethodDefinition methodDefinition) { var moduleDefinition = methodDefinition.Module; using (ScopedProcessor processor = methodDefinition.Body.GetILProcessor()) { var instructions = methodDefinition.Body.Instructions; for (var index = instructions.Count - 1; index >= 0; index--) { var currentInstruction = instructions[index]; ProcessEachInstruction(systemModuleDefinition, currentInstruction, processor, moduleDefinition); } } }
private static void ProcessRelayHandler(ScopedProcessor processor, ExceptionHandlerTree exceptionHandlerTree, Collection <VariableDefinition> variables, int currentDepth, Instruction iterator) { var ld = variables.LoadLocal(variables.Count - 1 - currentDepth); Instruction @switch; if (exceptionHandlerTree.Parent?.Handler is null) { @switch = Instruction.Create(OpCodes.Switch, exceptionHandlerTree.EndDestinations); } else { @switch = Instruction.Create(OpCodes.Switch, exceptionHandlerTree.EndDestinations.Append(exceptionHandlerTree.Parent.Handler.HandlerStart).ToArray()); } processor.Replace(iterator, ld).InsertAfter(ld, @switch); }
private void TryProcessEachInstruction(Instruction constrainedInstruction, ScopedProcessor processor, ModuleDefinition moduleDefinition, ModuleDefinition systemModuleDefinition) { if (!(constrainedInstruction?.Operand is TypeReference enumTypeReference) || constrainedInstruction.OpCode.Code != Code.Constrained || !enumTypeReference.TryToDefinition(out var enumTypeDefinition) || enumTypeDefinition.HasMethods) { return; } var callVirtualInstruction = constrainedInstruction.Next; if (!(callVirtualInstruction?.Operand is MethodReference baseToStringMethodReference) || callVirtualInstruction.OpCode.Code != Code.Callvirt || baseToStringMethodReference.FullName != "System.String System.Object::ToString()") { return; } var utilityTypeDefinition = moduleDefinition.GetOrCreatePrivateImplementationDetails(systemModuleDefinition); var methodName = "pcysl5edgo_EnumSpecific_ToString_" + enumTypeDefinition.FullName.Replace('.', '/'); var toStringMethodDefinition = utilityTypeDefinition.Methods.FirstOrDefault(x => x.IsStatic && x.Name == methodName) ?? Implement(moduleDefinition, systemModuleDefinition, methodName, enumTypeDefinition, utilityTypeDefinition); processor .Remove(constrainedInstruction) .Replace(callVirtualInstruction, Instruction.Create(OpCodes.Call, toStringMethodDefinition)); }
private void TryRewriteFlowAnalysis(Instruction currentInstruction, ScopedProcessor processor, TypeDefinition enumTypeDefinition) { var count = 0; var next = currentInstruction; while (count >= 0) { next = next.Next; if (next is null) { break; } if (count == 1 && next.OpCode == OpCodes.Box) { var next2 = next.Next; if (next2 is null) { break; } if (next2.OpCode == OpCodes.Call && ((MethodReference)next2.Operand).FullName == "System.Boolean System.Enum::HasFlag(System.Enum)") { if (!(next.Operand as TypeReference).TryToDefinition(out var enumTypeDefinition2)) { break; } if (!enumTypeDefinition2.IsEnum || enumTypeDefinition2.FullName != enumTypeDefinition.FullName) { break; } RewriteInner(processor, currentInstruction, next, next2); break; } } if (!next.OpCode.StackCountChange(out var changeCount)) { break; } count += changeCount; } }
private static void ProcessSingleHandler(ScopedProcessor processor, ExceptionHandlerTree exceptionHandlerTree, Collection <VariableDefinition> variables, int currentDepth, Instruction iterator) { switch (exceptionHandlerTree.EndDestinations.Length) { case 1: { processor.Replace(iterator, Instruction.Create(OpCodes.Br, exceptionHandlerTree.EndDestinations[0])); } break; case 2: if (ReferenceEquals(exceptionHandlerTree.EndDestinations[0], exceptionHandlerTree.EndDestinations[1])) { processor.Replace(iterator, Instruction.Create(OpCodes.Br, exceptionHandlerTree.EndDestinations[0])); } else { var ld = variables.LoadLocal(variables.Count - 1 - currentDepth); var brFalse = Instruction.Create(OpCodes.Brfalse, exceptionHandlerTree.EndDestinations[0]); processor .Replace(iterator, ld) .InsertAfter(ld, brFalse) .InsertAfter(brFalse, Instruction.Create(OpCodes.Br, exceptionHandlerTree.EndDestinations[1])); } break; default: { var ld = variables.LoadLocal(variables.Count - 1 - currentDepth); processor .Replace(iterator, ld) .InsertAfter(ld, Instruction.Create(OpCodes.Switch, exceptionHandlerTree.EndDestinations)); } break; } }
private static void ProcessEachInstruction(ModuleDefinition systemModuleDefinition, Instruction loadTokenInstruction, ScopedProcessor processor, ModuleDefinition moduleDefinition) { if (!IsValidLoadTokenInstruction(loadTokenInstruction, out var enumTypeReference) || !enumTypeReference.TryToDefinition(out var enumTypeDefinition) || !enumTypeDefinition.IsEnum) { return; } var getTypeFromHandleInstruction = loadTokenInstruction.Next; if (getTypeFromHandleInstruction is null || getTypeFromHandleInstruction.OpCode.Code != Code.Call || !(getTypeFromHandleInstruction.Operand is MethodReference getTypeFromHandleMethodReference) || getTypeFromHandleMethodReference.FullName != "System.Type System.Type::GetTypeFromHandle(System.RuntimeTypeHandle)") { return; } var getValuesInstruction = getTypeFromHandleInstruction.Next; if (getValuesInstruction is null || getValuesInstruction.OpCode != OpCodes.Call || !(getValuesInstruction.Operand is MethodReference methodReference) || methodReference.FullName != "System.Array System.Enum::GetValues(System.Type)") { return; } var castClassOrIsInstInstruction = getValuesInstruction.Next; var enumsGetValuesInstructions = moduleDefinition.EnumGetValuesInstructions(enumTypeDefinition, systemModuleDefinition); if (!(castClassOrIsInstInstruction is null) && (castClassOrIsInstInstruction.OpCode == OpCodes.Isinst || castClassOrIsInstInstruction.OpCode == OpCodes.Castclass)) { var castTargetClass = (TypeReference)castClassOrIsInstInstruction.Operand; switch (enumsGetValuesInstructions.Length) { case 0: return; case 1: processor .Replace(loadTokenInstruction, enumsGetValuesInstructions[0]) .Remove(getTypeFromHandleInstruction) .Remove(getValuesInstruction); if (castTargetClass.GetElementType().FullName == enumTypeDefinition.FullName) { processor.Remove(castClassOrIsInstInstruction); } break; case 2: processor .Replace(loadTokenInstruction, enumsGetValuesInstructions[0]) .Replace(getTypeFromHandleInstruction, enumsGetValuesInstructions[1]) .Remove(getValuesInstruction); if (castTargetClass.GetElementType().FullName == enumTypeDefinition.FullName) { processor.Remove(castClassOrIsInstInstruction); } break; case 3: processor .Replace(loadTokenInstruction, enumsGetValuesInstructions[0]) .Replace(getTypeFromHandleInstruction, enumsGetValuesInstructions[1]) .Replace(getValuesInstruction, enumsGetValuesInstructions[2]); if (castTargetClass.GetElementType().FullName == enumTypeDefinition.FullName) { processor.Remove(castClassOrIsInstInstruction); } break; default: processor .Replace(loadTokenInstruction, enumsGetValuesInstructions[0]) .Replace(getTypeFromHandleInstruction, enumsGetValuesInstructions[1]) .Replace(getValuesInstruction, enumsGetValuesInstructions[2]); if (castTargetClass.GetElementType().FullName == enumTypeDefinition.FullName) { processor.Replace(castClassOrIsInstInstruction, enumsGetValuesInstructions[3]); for (var i = 4; i < enumsGetValuesInstructions.Length; i++) { processor.InsertAfter(enumsGetValuesInstructions[i - 1], enumsGetValuesInstructions[i]); } } else { for (var i = 3; i < enumsGetValuesInstructions.Length; i++) { processor.InsertAfter(enumsGetValuesInstructions[i - 1], enumsGetValuesInstructions[i]); } } break; } }
private static void ProcessEachInstruction(ModuleDefinition systemModuleDefinition, Instruction loadTokenInstruction, ScopedProcessor processor, ModuleDefinition moduleDefinition) { if (!IsValidLoadTokenInstruction(loadTokenInstruction, out var enumTypeReference) || !enumTypeReference.TryToDefinition(out var enumTypeDefinition) || !enumTypeDefinition.IsEnum) { return; } var getTypeFromHandleInstruction = loadTokenInstruction.Next; if (getTypeFromHandleInstruction is null || getTypeFromHandleInstruction.OpCode.Code != Code.Call || !(getTypeFromHandleInstruction.Operand is MethodReference getTypeFromHandleMethodReference) || getTypeFromHandleMethodReference.FullName != "System.Type System.Type::GetTypeFromHandle(System.RuntimeTypeHandle)") { return; } var getUnderLyingTypeInstruction = getTypeFromHandleInstruction.Next; if (getUnderLyingTypeInstruction is null || getUnderLyingTypeInstruction.OpCode != OpCodes.Call || !(getUnderLyingTypeInstruction.Operand is MethodReference methodReference) || methodReference.FullName != "System.Type System.Enum::GetUnderlyingType(System.Type)") { return; } processor.Replace(loadTokenInstruction, Instruction.Create(OpCodes.Ldtoken, processor.Processor.Body.Method.Module.ImportReference(enumTypeDefinition.Fields[0].FieldType))); processor.Remove(getUnderLyingTypeInstruction); }
private static void TryRewriteLoadInt64ConstantWhenBaseInt32(Instruction loadNumberInstruction, TypeDefinition enumTypeDefinition, string enumBaseTypeName, ScopedProcessor processor, Instruction loadTokenInstruction, Instruction getTypeFromHandleInstruction) { var convInstruction = loadNumberInstruction.Next; if (convInstruction is null) { return; } switch (convInstruction.OpCode.Code) { case Code.Conv_I1: case Code.Conv_I2: case Code.Conv_I4: case Code.Conv_U1: case Code.Conv_U2: case Code.Conv_U4: break; default: return; } var boxInstruction = convInstruction.Next; if (!IsValidBoxInstruction(boxInstruction, enumTypeDefinition, enumBaseTypeName)) { return; } var isDefinedInstruction = boxInstruction.Next; if (!IsValidIsDefinedInstruction(isDefinedInstruction)) { return; } var number = (long)loadNumberInstruction.Operand; bool answer; switch (enumBaseTypeName) { case "Byte": answer = enumTypeDefinition.Fields.Skip(1).Any(x => (byte)x.Constant == number); break; case "SByte": answer = enumTypeDefinition.Fields.Skip(1).Any(x => (sbyte)x.Constant == number); break; case "Int16": answer = enumTypeDefinition.Fields.Skip(1).Any(x => (short)x.Constant == number); break; case "UInt16": answer = enumTypeDefinition.Fields.Skip(1).Any(x => (ushort)x.Constant == number); break; case "Int32": answer = enumTypeDefinition.Fields.Skip(1).Any(x => (int)x.Constant == number); break; case "UInt32": answer = enumTypeDefinition.Fields.Skip(1).Any(x => (uint)x.Constant == number); break; default: throw new ArgumentException(enumBaseTypeName); } processor .Remove(loadTokenInstruction) .Remove(getTypeFromHandleInstruction) .Remove(loadNumberInstruction) .Remove(convInstruction) .Remove(boxInstruction) .Replace(isDefinedInstruction, InstructionUtility.LoadConstant(answer)); }
private static void TryRewriteLoadInt64ConstantWhenBaseInt64(Instruction loadNumberInstruction, TypeDefinition enumTypeDefinition, string enumBaseTypeName, ScopedProcessor processor, Instruction loadTokenInstruction, Instruction getTypeFromHandleInstruction) { var boxInstruction = loadNumberInstruction.Next; if (!IsValidBoxInstruction(boxInstruction, enumTypeDefinition, enumBaseTypeName)) { return; } var isDefinedInstruction = boxInstruction.Next; if (!IsValidIsDefinedInstruction(isDefinedInstruction)) { return; } var number = (long)loadNumberInstruction.Operand; bool answer; switch (enumBaseTypeName) { case "Int64": answer = enumTypeDefinition.Fields.Skip(1).Any(x => (long)x.Constant == number); break; case "UInt64": answer = enumTypeDefinition.Fields.Skip(1).Any(x => (ulong)x.Constant == (ulong)number); break; default: throw new ArgumentOutOfRangeException(enumBaseTypeName); } processor .Remove(loadTokenInstruction) .Remove(getTypeFromHandleInstruction) .Remove(loadNumberInstruction) .Remove(boxInstruction) .Replace(isDefinedInstruction, InstructionUtility.LoadConstant(answer)); }
public void Process(ModuleDefinition systemModuleDefinition, MethodDefinition methodDefinition) { var parameters = methodDefinition.Parameters; var variables = methodDefinition.Body.Variables; var instructions = methodDefinition.Body.Instructions; var moduleDefinition = methodDefinition.Module; using (ScopedProcessor processor = methodDefinition.Body.GetILProcessor()) { for (var index = instructions.Count - 1; index >= 0; index--) { var loadTokenInstruction = instructions[index]; if (!IsValidLoadTokenInstanceType(loadTokenInstruction, out var enumTypeReference) || !enumTypeReference.TryToDefinition(out var enumTypeDefinition) || !enumTypeDefinition.IsEnum) { continue; } var enumBaseTypeName = enumTypeDefinition.Fields[0].FieldType.Name; var getTypeFromHandleInstruction = loadTokenInstruction.Next; if (!IsValidGetTypeFromHandleInstruction(getTypeFromHandleInstruction)) { continue; } var nextInstruction = getTypeFromHandleInstruction.Next; if (nextInstruction is null) { continue; } switch (nextInstruction.OpCode.Code) { case Code.Ldstr: TryRewriteLoadStringConstant(processor, enumTypeDefinition, loadTokenInstruction, getTypeFromHandleInstruction, nextInstruction); continue; case Code.Ldc_I4_0: case Code.Ldc_I4_1: case Code.Ldc_I4_2: case Code.Ldc_I4_3: case Code.Ldc_I4_4: case Code.Ldc_I4_5: case Code.Ldc_I4_6: case Code.Ldc_I4_7: case Code.Ldc_I4_8: case Code.Ldc_I4_M1: case Code.Ldc_I4_S: case Code.Ldc_I4: TryRewriteLoadInt32Constant(processor, enumTypeDefinition, enumBaseTypeName, loadTokenInstruction, getTypeFromHandleInstruction, nextInstruction); continue; case Code.Ldc_I8: TryRewriteLoadInt64Constant(enumBaseTypeName, nextInstruction, enumTypeDefinition, processor, loadTokenInstruction, getTypeFromHandleInstruction); continue; case Code.Ldloc_0: TryRewriteLoadVariable(variables[0].VariableType, enumTypeDefinition, moduleDefinition, enumBaseTypeName, processor, loadTokenInstruction, getTypeFromHandleInstruction, nextInstruction); continue; case Code.Ldloc_1: TryRewriteLoadVariable(variables[1].VariableType, enumTypeDefinition, moduleDefinition, enumBaseTypeName, processor, loadTokenInstruction, getTypeFromHandleInstruction, nextInstruction); continue; case Code.Ldloc_2: TryRewriteLoadVariable(variables[2].VariableType, enumTypeDefinition, moduleDefinition, enumBaseTypeName, processor, loadTokenInstruction, getTypeFromHandleInstruction, nextInstruction); continue; case Code.Ldloc_3: TryRewriteLoadVariable(variables[3].VariableType, enumTypeDefinition, moduleDefinition, enumBaseTypeName, processor, loadTokenInstruction, getTypeFromHandleInstruction, nextInstruction); continue; case Code.Ldloc_S: case Code.Ldloc: TryRewriteLoadVariable(((VariableDefinition)nextInstruction.Operand).VariableType, enumTypeDefinition, moduleDefinition, enumBaseTypeName, processor, loadTokenInstruction, getTypeFromHandleInstruction, nextInstruction); continue; case Code.Ldarg_0: TryRewriteLoadVariable(methodDefinition.HasThis ? methodDefinition.Body.ThisParameter.ParameterType : parameters[0].ParameterType, enumTypeDefinition, moduleDefinition, enumBaseTypeName, processor, loadTokenInstruction, getTypeFromHandleInstruction, nextInstruction); continue; case Code.Ldarg_1: TryRewriteLoadVariable(parameters[methodDefinition.HasThis ? 0 : 1].ParameterType, enumTypeDefinition, moduleDefinition, enumBaseTypeName, processor, loadTokenInstruction, getTypeFromHandleInstruction, nextInstruction); continue; case Code.Ldarg_2: TryRewriteLoadVariable(parameters[methodDefinition.HasThis ? 1 : 2].ParameterType, enumTypeDefinition, moduleDefinition, enumBaseTypeName, processor, loadTokenInstruction, getTypeFromHandleInstruction, nextInstruction); continue; case Code.Ldarg_3: TryRewriteLoadVariable(parameters[methodDefinition.HasThis ? 2 : 3].ParameterType, enumTypeDefinition, moduleDefinition, enumBaseTypeName, processor, loadTokenInstruction, getTypeFromHandleInstruction, nextInstruction); continue; case Code.Ldarg_S: case Code.Ldarg: TryRewriteLoadVariable(((ParameterDefinition)nextInstruction.Operand).ParameterType, enumTypeDefinition, moduleDefinition, enumBaseTypeName, processor, loadTokenInstruction, getTypeFromHandleInstruction, nextInstruction); continue; case Code.Ldfld: case Code.Ldsfld: TryRewriteLoadVariable(((FieldDefinition)nextInstruction.Operand).FieldType, enumTypeDefinition, moduleDefinition, enumBaseTypeName, processor, loadTokenInstruction, getTypeFromHandleInstruction, nextInstruction); continue; } } } }
private static void TryRewriteLoadInt64Constant(string enumBaseTypeName, Instruction loadNumberInstruction, TypeDefinition enumTypeDefinition, ScopedProcessor processor, Instruction loadTokenInstruction, Instruction getTypeFromHandleInstruction) { if (enumBaseTypeName.IsInt32OnStack()) { TryRewriteLoadInt64ConstantWhenBaseInt32(loadNumberInstruction, enumTypeDefinition, enumBaseTypeName, processor, loadTokenInstruction, getTypeFromHandleInstruction); } else if (enumBaseTypeName.IsInt64OnStack()) { TryRewriteLoadInt64ConstantWhenBaseInt64(loadNumberInstruction, enumTypeDefinition, enumBaseTypeName, processor, loadTokenInstruction, getTypeFromHandleInstruction); } }
private static void TryRewriteLoadStringVariable(TypeDefinition enumTypeDefinition, ModuleDefinition moduleDefinition, ScopedProcessor processor, Instruction loadTokenInstruction, Instruction getTypeFromHandleInstruction, Instruction loadInstruction) { var method = enumTypeDefinition.Methods.FirstOrDefault(x => x.Name == "IsDefinedString"); if (method is null) { return; } var isDefinedInstruction = loadInstruction.Next; if (!IsValidIsDefinedInstruction(isDefinedInstruction)) { return; } processor .Remove(loadTokenInstruction) .Remove(getTypeFromHandleInstruction) .Replace(isDefinedInstruction, Instruction.Create(OpCodes.Call, moduleDefinition.ImportReference(method))); }
private static void TryRewriteLoadVariable(TypeReference targetType, TypeDefinition enumTypeDefinition, ModuleDefinition moduleDefinition, string enumBaseTypeName, ScopedProcessor processor, Instruction loadTokenInstruction, Instruction getTypeFromHandleInstruction, Instruction nextInstruction) { if (targetType.IsArray || targetType.IsGenericParameter || targetType.IsGenericInstance) { return; } if (targetType.FullName == "System.String") { TryRewriteLoadStringVariable(enumTypeDefinition, moduleDefinition, processor, loadTokenInstruction, getTypeFromHandleInstruction, loadInstruction: nextInstruction); } }
private static void ProcessTry(ScopedProcessor processor, ExceptionHandlerTree exceptionHandlerTree) { var handler = exceptionHandlerTree.Handler; var variables = processor.Processor.Body.Variables; for (Instruction iterator = handler.TryStart, end = handler.TryEnd; !(iterator is null) && !ReferenceEquals(iterator, end);)
private static void ProcessSimpleTree(ScopedProcessor processor, ExceptionHandlerTree exceptionHandlerTree) { ProcessHandler(processor, exceptionHandlerTree); ProcessTry(processor, exceptionHandlerTree); }