Exemplo n.º 1
0
        private static MethodBody GenerateBody(MethodDefinition method)
        {
            MethodBody  body      = new MethodBody(method);
            ILProcessor processor = body.GetILProcessor();

            Dictionary <Instruction, VariableDefinition> Branches = new Dictionary <Instruction, VariableDefinition>();
            Dictionary <int, VariableDefinition>         Locals   = new Dictionary <int, VariableDefinition>();

            // create an instance of DynamicMethod
            processor.CreateDynamicMethod(method);
            VariableDefinition dynamicMethod = new VariableDefinition(TypeReferences["DynamicMethod"]);

            processor.Body.Variables.Add(dynamicMethod);
            processor.Emit(OpCodes.Stloc, dynamicMethod);
            processor.Emit(OpCodes.Ldloc, dynamicMethod);

            // generate an ILGenerator object from the DynamicMethod
            VariableDefinition ilgenerator = new VariableDefinition(TypeReferences["ILGenerator"]);

            processor.Body.Variables.Add(ilgenerator);
            processor.Emit(OpCodes.Callvirt, MethodReferences["GetILGenerator"]);
            processor.Emit(OpCodes.Stloc, ilgenerator);

            // pre-emission phase (runs after DynamicMethod and ILGenerator are instantiated)
            foreach (Instruction instruction in method.Body.Instructions)
            {
                // output instructions & operand types to console
                Console.Write(instruction.Offset + ": " + instruction.OpCode.Name);
                Console.Write(instruction.Operand == null ? "\n" : "        -> " + instruction.Operand.GetType() + "\n");

                // define branch variables ('Label' objects)
                if (instruction.Operand is Instruction)
                {
                    // ignore leave.s branching, since it's used for exception handling
                    if (instruction.OpCode == OpCodes.Leave_S)
                    {
                        continue;
                    }

                    Instruction target = instruction.Operand as Instruction;
                    if (Branches.ContainsKey(target))
                    {
                        continue;
                    }

                    VariableDefinition label = new VariableDefinition(TypeReferences["Label"]);
                    processor.Body.Variables.Add(label);

                    processor.Emit(OpCodes.Ldloc, ilgenerator);
                    processor.Emit(OpCodes.Callvirt, MethodReferences["DefineLabel"]);
                    processor.Emit(OpCodes.Stloc, label);

                    Branches.Add(target, label);
                }
            }

            // define local variables
            for (int vI = 0; vI < method.Body.Variables.Count; vI++)
            {
                VariableDefinition local = new VariableDefinition(TypeReferences["LocalBuilder"]);
                processor.Body.Variables.Add(local);

                TypeReference variableType = method.Body.Variables[vI].VariableType;

                processor.Emit(OpCodes.Ldloc, ilgenerator);
                processor.EmitType(variableType);
                processor.Emit(OpCodes.Callvirt, MethodReferences["DeclareLocal"]);
                processor.Emit(OpCodes.Stloc, local);

                Locals.Add(vI, local);
            }


            // iterate through instructions, writer ILGenerator.Emit calls
            for (int iI = 0; iI < method.Body.Instructions.Count; iI++)
            {
                // the current instruction
                Instruction instruction = method.Body.Instructions[iI];

                foreach (ExceptionHandler exH in method.Body.ExceptionHandlers)
                {
                    if (exH.TryStart == instruction)
                    {
                        processor.Emit(OpCodes.Ldloc, ilgenerator);
                        processor.Emit(OpCodes.Callvirt, MethodReferences["TryStart"]);
                        processor.Emit(OpCodes.Pop); // pop TryStart return value from eval stack
                    }
                    else if (exH.HandlerStart == instruction)
                    {
                        processor.Emit(OpCodes.Ldloc, ilgenerator);
                        if (exH.HandlerType == ExceptionHandlerType.Catch)
                        {
                            processor.EmitType(exH.CatchType);
                            processor.Emit(OpCodes.Callvirt, MethodReferences["CatchBlock"]);
                        }
                        else if (exH.HandlerType == ExceptionHandlerType.Finally)
                        {
                            processor.Emit(OpCodes.Callvirt, MethodReferences["FinallyBlock"]);
                        }
                    }
                    else if (exH.TryEnd == instruction || exH.HandlerEnd == instruction)
                    {
                        processor.Emit(OpCodes.Ldloc, ilgenerator);
                        processor.Emit(OpCodes.Callvirt, MethodReferences["TryEnd"]);
                    }
                }

                // ignore leave.s branching, since it's used for exception handling
                if (instruction.OpCode == OpCodes.Leave_S)
                {
                    continue;
                }

                // mark a label for this instruction, if we have a branch going here
                if (Branches.ContainsKey(instruction))
                {
                    processor.Emit(OpCodes.Ldloc, ilgenerator);
                    processor.EmitMarkLabel(Branches[instruction]);
                }

                // load ilgenerator object into memory to make Emit call from it
                processor.Emit(OpCodes.Ldloc, ilgenerator);

                // determine index of stloc/ldloc call based on OpCode
                int stlocIndex = instruction.GetStlocIndex();
                int ldlocIndex = instruction.GetLdlocIndex();

                // modify stloc/ldloc implementation to use LocalBuilder
                if (stlocIndex > -1 || ldlocIndex > -1)
                {
                    // new determine new OpCode & local variable index
                    bool isStloc = stlocIndex > -1;
                    instruction.OpCode = isStloc ? OpCodes.Stloc : OpCodes.Ldloc;
                    int localIndex = isStloc ? stlocIndex : ldlocIndex;

                    // load OpCode (param #1) and LocalBuilder object (param #2) onto eval stack
                    // make ILGenerator.Emit call with these 2 params to fix local variables in ILGenerator methods
                    processor.Emit(OpCodes.Ldsfld, Utils.GetReflectedOpCode(instruction));
                    processor.Emit(OpCodes.Ldloc, Locals[localIndex]);
                    processor.Emit(OpCodes.Callvirt, Utils.GetILGeneratorEmitter(typeof(System.Reflection.Emit.LocalBuilder)));
                    continue;
                }

                // load the OpCode to be emitted onto the eval stack (param #1)
                processor.Emit(OpCodes.Ldsfld, Utils.GetReflectedOpCode(instruction));

                // type of ILGenerator.Emit function to invoke.
                // if null, Emit will be invoked without an operand.
                // all operands must be handled in the following if/else blocks
                Type EmitType = null;

                // handle operands (note: each operand needs to be handled differently)
                // this will be used in the ILGenerator.Emit call (param #2)
                if (instruction.Operand != null)
                {
                    if (instruction.Operand is FieldDefinition)
                    {
                        FieldDefinition fieldDefinition = instruction.Operand as FieldDefinition;
                        processor.EmitFieldGetter(fieldDefinition);
                        EmitType = typeof(System.Reflection.FieldInfo);
                    }
                    else if (instruction.Operand is MethodDefinition)
                    {
                        MethodDefinition methodDefinition = instruction.Operand as MethodDefinition;
                        processor.EmitMethodGetter(methodDefinition);
                        EmitType = methodDefinition.IsConstructor ? typeof(System.Reflection.ConstructorInfo) : typeof(System.Reflection.MethodInfo);
                    }
                    else if (instruction.Operand is MethodReference)
                    {
                        MethodReference methodReference = instruction.Operand as MethodReference;
                        processor.EmitMethodGetter(methodReference);
                        EmitType = typeof(System.Reflection.MethodInfo);
                    }
                    else if (instruction.Operand.GetType() == typeof(sbyte))
                    {
                        sbyte value = Convert.ToSByte(instruction.Operand);
                        processor.Emit(OpCodes.Ldc_I4_S, value);
                        EmitType = typeof(sbyte);
                    }
                    else if (instruction.Operand.GetType() == typeof(string))
                    {
                        string value = Convert.ToString(instruction.Operand);
                        processor.Emit(OpCodes.Ldstr, value);
                        EmitType = typeof(string);
                    }
                    else if (instruction.Operand.GetType() == typeof(int))
                    {
                        int value = Convert.ToInt32(instruction.Operand);
                        processor.Emit(OpCodes.Ldc_I4, value);
                        EmitType = typeof(int);
                    }
                    else if (instruction.Operand.GetType() == typeof(float))
                    {
                        float value = Convert.ToSingle(instruction.Operand);
                        processor.Emit(OpCodes.Ldc_R4, value);
                        EmitType = typeof(float);
                    }
                    else if (instruction.Operand is Instruction)
                    {
                        Instruction targetInstruction = instruction.Operand as Instruction;
                        processor.Emit(OpCodes.Ldloc, Branches[targetInstruction]);
                        EmitType = typeof(System.Reflection.Emit.Label);
                    }
                    else if (instruction.Operand is TypeReference)
                    {
                        TypeReference typeReference = instruction.Operand as TypeReference;
                        processor.EmitType(typeReference);
                        EmitType = typeof(Type);
                    }
                    else
                    {
                        Console.WriteLine("UNHANDLED OPERAND: opcode = " + instruction.OpCode.Name + ", type = " + instruction.Operand.GetType());
                    }
                }

                // if we're emitting a 'call' or 'callvirt' opcode, use the ILGenerator.EmitCall function
                if (instruction.OpCode == OpCodes.Call || instruction.OpCode == OpCodes.Callvirt)
                {
                    // null = 3rd param in EmitCall (1st was the opcode, 2nd was the MethodInfo)
                    processor.Emit(OpCodes.Ldnull);
                    processor.Emit(OpCodes.Callvirt, MethodReferences["EmitCall"]);
                    continue;
                }

                // call the ILGenerator.Emit func
                // if EmitType is null, second parameter (operand) is ignored
                processor.Emit(OpCodes.Callvirt, Utils.GetILGeneratorEmitter(EmitType));
            }

            // load the DynamicMethod object back onto the stack
            processor.Emit(OpCodes.Ldloc, dynamicMethod);

            // object parameter in DynamicMethod.Invoke, should always be null (param #1)
            processor.Emit(OpCodes.Ldnull);

            // create an array of objects to hold parameters to send to DynamicMethod.Invoke (param #2)
            processor.Emit(OpCodes.Ldc_I4_S, Convert.ToSByte(method.Parameters.Count));
            processor.Emit(OpCodes.Newarr, Module.TypeSystem.Object);

            // load parameters into the created array
            for (int pI = 0; pI < method.Parameters.Count; pI++)
            {
                ParameterDefinition parameter = method.Parameters[pI];
                processor.Emit(OpCodes.Dup);
                processor.Emit(OpCodes.Ldc_I4_S, Convert.ToSByte(pI));
                processor.Emit(OpCodes.Ldarg_S, parameter);
                processor.Emit(OpCodes.Box, parameter.ParameterType);
                processor.Emit(OpCodes.Stelem_Ref);
            }

            // call the invoker
            processor.Emit(OpCodes.Callvirt, MethodReferences["Invoker"]);

            // cast the returned object to the return type
            if (method.ReturnType != Module.TypeSystem.Void)
            {
                processor.Emit(OpCodes.Unbox_Any, method.ReturnType);
            }
            else
            {
                processor.Emit(OpCodes.Pop);
            }

            // return the remaining value on the stack (result of dynamic method)
            processor.Emit(OpCodes.Ret);

            return(body);
        }