de4dot-cex/AssemblyData/methodsrewriter/CodeGenerator.cs

341 lines
10 KiB
C#
Raw Normal View History

2011-09-27 07:48:47 +08:00
/*
2015-10-30 05:45:26 +08:00
Copyright (C) 2011-2015 de4dot@gmail.com
2011-09-27 07:48:47 +08:00
This file is part of de4dot.
de4dot is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
de4dot is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with de4dot. If not, see <http://www.gnu.org/licenses/>.
*/
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Reflection;
using System.Reflection.Emit;
using dnlib.DotNet.Emit;
using dnlib.DotNet;
2011-09-27 07:48:47 +08:00
using de4dot.blocks;
using OpCode = dnlib.DotNet.Emit.OpCode;
using OpCodes = dnlib.DotNet.Emit.OpCodes;
using OperandType = dnlib.DotNet.Emit.OperandType;
2011-09-27 07:48:47 +08:00
using ROpCode = System.Reflection.Emit.OpCode;
using ROpCodes = System.Reflection.Emit.OpCodes;
namespace AssemblyData.methodsrewriter {
class CodeGenerator {
static Dictionary<OpCode, ROpCode> dnlibToReflection = new Dictionary<OpCode, ROpCode>();
2011-09-27 07:48:47 +08:00
static CodeGenerator() {
var refDict = new Dictionary<short, ROpCode>(0x100);
foreach (var f in typeof(ROpCodes).GetFields(BindingFlags.Static | BindingFlags.Public)) {
if (f.FieldType != typeof(ROpCode))
continue;
var ropcode = (ROpCode)f.GetValue(null);
refDict[ropcode.Value] = ropcode;
}
foreach (var f in typeof(OpCodes).GetFields(BindingFlags.Static | BindingFlags.Public)) {
if (f.FieldType != typeof(OpCode))
continue;
var opcode = (OpCode)f.GetValue(null);
ROpCode ropcode;
if (!refDict.TryGetValue(opcode.Value, out ropcode))
continue;
dnlibToReflection[opcode] = ropcode;
2011-09-27 07:48:47 +08:00
}
}
IMethodsRewriter methodsRewriter;
2011-09-28 04:06:43 +08:00
string methodName;
2011-09-27 07:48:47 +08:00
IList<Instruction> allInstructions;
IList<ExceptionHandler> allExceptionHandlers;
ILGenerator ilg;
Type methodReturnType;
Type[] methodParameters;
Type delegateType;
MMethod methodInfo;
LocalBuilder tempObjLocal;
LocalBuilder tempObjArrayLocal;
int thisArgIndex;
List<LocalBuilder> locals;
List<Label> labels;
Dictionary<Instruction, int> instrToIndex;
2011-09-28 04:06:43 +08:00
Stack<ExceptionHandler> exceptionHandlersStack;
2011-09-27 07:48:47 +08:00
public Type DelegateType {
get { return delegateType; }
}
2011-09-28 04:06:43 +08:00
public CodeGenerator(IMethodsRewriter methodsRewriter, string methodName) {
2011-09-27 07:48:47 +08:00
this.methodsRewriter = methodsRewriter;
2011-09-28 04:06:43 +08:00
this.methodName = methodName;
2011-09-27 07:48:47 +08:00
}
2013-01-19 20:03:57 +08:00
public void SetMethodInfo(MMethod methodInfo) {
2011-09-27 07:48:47 +08:00
this.methodInfo = methodInfo;
2013-01-19 20:03:57 +08:00
methodReturnType = ResolverUtils.GetReturnType(methodInfo.methodBase);
methodParameters = GetMethodParameterTypes(methodInfo.methodBase);
delegateType = Utils.GetDelegateType(methodReturnType, methodParameters);
2011-09-27 07:48:47 +08:00
}
2013-01-19 20:03:57 +08:00
public Delegate Generate(IList<Instruction> allInstructions, IList<ExceptionHandler> allExceptionHandlers) {
2011-09-27 07:48:47 +08:00
this.allInstructions = allInstructions;
this.allExceptionHandlers = allExceptionHandlers;
2011-09-28 04:06:43 +08:00
var dm = new DynamicMethod(methodName, methodReturnType, methodParameters, methodInfo.methodBase.Module, true);
2011-09-27 07:48:47 +08:00
var lastInstr = allInstructions[allInstructions.Count - 1];
2012-11-01 05:44:54 +08:00
ilg = dm.GetILGenerator((int)lastInstr.Offset + lastInstr.GetSize());
2011-09-27 07:48:47 +08:00
2013-01-19 20:03:57 +08:00
InitInstrToIndex();
InitLocals();
InitLabels();
2011-09-27 07:48:47 +08:00
2011-09-28 04:06:43 +08:00
exceptionHandlersStack = new Stack<ExceptionHandler>();
2011-09-27 07:48:47 +08:00
for (int i = 0; i < allInstructions.Count; i++) {
2013-01-19 20:03:57 +08:00
UpdateExceptionHandlers(i);
2011-09-27 07:48:47 +08:00
var instr = allInstructions[i];
ilg.MarkLabel(labels[i]);
if (instr.Operand is Operand)
2013-01-19 20:03:57 +08:00
WriteSpecialInstr(instr, (Operand)instr.Operand);
2011-09-27 07:48:47 +08:00
else
2013-01-19 20:03:57 +08:00
WriteInstr(instr);
2011-09-27 07:48:47 +08:00
}
2013-01-19 20:03:57 +08:00
UpdateExceptionHandlers(-1);
2011-09-27 07:48:47 +08:00
return dm.CreateDelegate(delegateType);
}
2013-01-19 20:03:57 +08:00
Instruction GetExceptionInstruction(int instructionIndex) {
2011-09-28 04:06:43 +08:00
return instructionIndex < 0 ? null : allInstructions[instructionIndex];
}
2013-01-19 20:03:57 +08:00
void UpdateExceptionHandlers(int instructionIndex) {
var instr = GetExceptionInstruction(instructionIndex);
UpdateExceptionHandlers(instr);
if (AddTryStart(instr))
UpdateExceptionHandlers(instr);
2011-09-28 04:06:43 +08:00
}
2013-01-19 20:03:57 +08:00
void UpdateExceptionHandlers(Instruction instr) {
2011-09-28 04:06:43 +08:00
while (exceptionHandlersStack.Count > 0) {
var ex = exceptionHandlersStack.Peek();
if (ex.TryEnd == instr) {
}
if (ex.FilterStart == instr) {
}
if (ex.HandlerStart == instr) {
2012-11-02 14:36:02 +08:00
if (ex.HandlerType == ExceptionHandlerType.Finally)
2011-09-28 04:06:43 +08:00
ilg.BeginFinallyBlock();
else
2013-01-19 20:03:57 +08:00
ilg.BeginCatchBlock(Resolver.GetRtType(ex.CatchType));
2011-09-28 04:06:43 +08:00
}
if (ex.HandlerEnd == instr) {
exceptionHandlersStack.Pop();
2013-01-19 20:03:57 +08:00
if (exceptionHandlersStack.Count == 0 || !IsSameTryBlock(ex, exceptionHandlersStack.Peek()))
2011-09-28 04:06:43 +08:00
ilg.EndExceptionBlock();
}
else
break;
}
}
2013-01-19 20:03:57 +08:00
bool AddTryStart(Instruction instr) {
2011-09-28 04:06:43 +08:00
var list = new List<ExceptionHandler>();
foreach (var ex in allExceptionHandlers) {
if (ex.TryStart == instr)
list.Add(ex);
}
list.Reverse();
foreach (var ex in list) {
2013-01-19 20:03:57 +08:00
if (exceptionHandlersStack.Count == 0 || !IsSameTryBlock(ex, exceptionHandlersStack.Peek()))
2011-09-28 04:06:43 +08:00
ilg.BeginExceptionBlock();
exceptionHandlersStack.Push(ex);
}
return list.Count > 0;
}
2013-01-19 20:03:57 +08:00
static bool IsSameTryBlock(ExceptionHandler ex1, ExceptionHandler ex2) {
2011-09-28 04:06:43 +08:00
return ex1.TryStart == ex2.TryStart && ex1.TryEnd == ex2.TryEnd;
}
2013-01-19 20:03:57 +08:00
void InitInstrToIndex() {
2011-09-27 07:48:47 +08:00
instrToIndex = new Dictionary<Instruction, int>(allInstructions.Count);
for (int i = 0; i < allInstructions.Count; i++)
instrToIndex[allInstructions[i]] = i;
}
2013-01-19 20:03:57 +08:00
void InitLocals() {
2011-09-27 07:48:47 +08:00
locals = new List<LocalBuilder>();
foreach (var local in methodInfo.methodDef.Body.Variables)
2013-01-19 20:03:57 +08:00
locals.Add(ilg.DeclareLocal(Resolver.GetRtType(local.Type), local.Type.RemoveModifiers().IsPinned));
2011-09-27 07:48:47 +08:00
tempObjLocal = ilg.DeclareLocal(typeof(object));
tempObjArrayLocal = ilg.DeclareLocal(typeof(object[]));
}
2013-01-19 20:03:57 +08:00
void InitLabels() {
2011-09-27 07:48:47 +08:00
labels = new List<Label>(allInstructions.Count);
for (int i = 0; i < allInstructions.Count; i++)
labels.Add(ilg.DefineLabel());
}
2013-01-19 20:03:57 +08:00
Type[] GetMethodParameterTypes(MethodBase method) {
2011-09-27 07:48:47 +08:00
var list = new List<Type>();
2013-01-19 20:03:57 +08:00
if (ResolverUtils.HasThis(method))
2011-09-27 07:48:47 +08:00
list.Add(method.DeclaringType);
foreach (var param in method.GetParameters())
list.Add(param.ParameterType);
thisArgIndex = list.Count;
list.Add(methodsRewriter.GetType());
return list.ToArray();
}
2013-01-19 20:03:57 +08:00
void WriteSpecialInstr(Instruction instr, Operand operand) {
2011-09-27 07:48:47 +08:00
BindingFlags flags;
switch (operand.type) {
case Operand.Type.ThisArg:
2013-01-19 20:03:57 +08:00
ilg.Emit(ConvertOpCode(instr.OpCode), (short)thisArgIndex);
2011-09-27 07:48:47 +08:00
break;
case Operand.Type.TempObj:
2013-01-19 20:03:57 +08:00
ilg.Emit(ConvertOpCode(instr.OpCode), tempObjLocal);
2011-09-27 07:48:47 +08:00
break;
case Operand.Type.TempObjArray:
2013-01-19 20:03:57 +08:00
ilg.Emit(ConvertOpCode(instr.OpCode), tempObjArrayLocal);
2011-09-27 07:48:47 +08:00
break;
case Operand.Type.OurMethod:
flags = BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.Static | BindingFlags.NonPublic | BindingFlags.Public;
var methodName = (string)operand.data;
var ourMethod = methodsRewriter.GetType().GetMethod(methodName, flags);
if (ourMethod == null)
throw new ApplicationException(string.Format("Could not find method {0}", methodName));
2013-01-19 20:03:57 +08:00
ilg.Emit(ConvertOpCode(instr.OpCode), ourMethod);
2011-09-27 07:48:47 +08:00
break;
case Operand.Type.NewMethod:
var methodBase = (MethodBase)operand.data;
2013-01-19 20:03:57 +08:00
var delegateType = methodsRewriter.GetDelegateType(methodBase);
2011-09-27 07:48:47 +08:00
flags = BindingFlags.DeclaredOnly | BindingFlags.Public | BindingFlags.Instance;
var invokeMethod = delegateType.GetMethod("Invoke", flags);
2013-01-19 20:03:57 +08:00
ilg.Emit(ConvertOpCode(instr.OpCode), invokeMethod);
2011-09-27 07:48:47 +08:00
break;
case Operand.Type.ReflectionType:
2013-01-19 20:03:57 +08:00
ilg.Emit(ConvertOpCode(instr.OpCode), (Type)operand.data);
2011-09-27 07:48:47 +08:00
break;
default:
throw new ApplicationException(string.Format("Unknown operand type: {0}", operand.type));
}
}
2013-01-19 20:03:57 +08:00
Label GetLabel(Instruction target) {
2011-09-27 07:48:47 +08:00
return labels[instrToIndex[target]];
}
2013-01-19 20:03:57 +08:00
Label[] GetLabels(Instruction[] targets) {
2011-09-27 07:48:47 +08:00
var labels = new Label[targets.Length];
for (int i = 0; i < labels.Length; i++)
2013-01-19 20:03:57 +08:00
labels[i] = GetLabel(targets[i]);
2011-09-27 07:48:47 +08:00
return labels;
}
2013-01-19 20:03:57 +08:00
void WriteInstr(Instruction instr) {
var opcode = ConvertOpCode(instr.OpCode);
2011-09-27 07:48:47 +08:00
switch (instr.OpCode.OperandType) {
case OperandType.InlineNone:
ilg.Emit(opcode);
break;
case OperandType.InlineBrTarget:
case OperandType.ShortInlineBrTarget:
2013-01-19 20:03:57 +08:00
ilg.Emit(opcode, GetLabel((Instruction)instr.Operand));
2011-09-27 07:48:47 +08:00
break;
case OperandType.InlineSwitch:
2013-01-19 20:03:57 +08:00
ilg.Emit(opcode, GetLabels((Instruction[])instr.Operand));
2011-09-27 07:48:47 +08:00
break;
case OperandType.ShortInlineI:
if (instr.OpCode.Code == Code.Ldc_I4_S)
ilg.Emit(opcode, (sbyte)instr.Operand);
else
ilg.Emit(opcode, (byte)instr.Operand);
break;
case OperandType.InlineI:
ilg.Emit(opcode, (int)instr.Operand);
break;
case OperandType.InlineI8:
ilg.Emit(opcode, (long)instr.Operand);
break;
case OperandType.InlineR:
ilg.Emit(opcode, (double)instr.Operand);
break;
case OperandType.ShortInlineR:
ilg.Emit(opcode, (float)instr.Operand);
break;
case OperandType.InlineString:
ilg.Emit(opcode, (string)instr.Operand);
break;
case OperandType.InlineTok:
case OperandType.InlineType:
case OperandType.InlineMethod:
case OperandType.InlineField:
2013-01-19 20:03:57 +08:00
var obj = Resolver.GetRtObject((ITokenOperand)instr.Operand);
2011-09-27 07:48:47 +08:00
if (obj is ConstructorInfo)
ilg.Emit(opcode, (ConstructorInfo)obj);
else if (obj is MethodInfo)
ilg.Emit(opcode, (MethodInfo)obj);
else if (obj is FieldInfo)
ilg.Emit(opcode, (FieldInfo)obj);
else if (obj is Type)
ilg.Emit(opcode, (Type)obj);
else
throw new ApplicationException(string.Format("Unknown type: {0}", (obj == null ? obj : obj.GetType())));
2011-09-27 07:48:47 +08:00
break;
case OperandType.InlineVar:
2012-11-02 14:36:02 +08:00
ilg.Emit(opcode, checked((short)((IVariable)instr.Operand).Index));
2011-09-27 07:48:47 +08:00
break;
case OperandType.ShortInlineVar:
2012-11-02 14:36:02 +08:00
ilg.Emit(opcode, checked((byte)((IVariable)instr.Operand).Index));
2011-09-27 07:48:47 +08:00
break;
case OperandType.InlineSig: //TODO:
default:
throw new ApplicationException(string.Format("Unknown OperandType {0}", instr.OpCode.OperandType));
}
}
2013-01-19 20:03:57 +08:00
ROpCode ConvertOpCode(OpCode opcode) {
2011-09-29 16:55:28 +08:00
ROpCode ropcode;
if (dnlibToReflection.TryGetValue(opcode, out ropcode))
2011-09-29 16:55:28 +08:00
return ropcode;
return ROpCodes.Nop;
2011-09-27 07:48:47 +08:00
}
}
}