/// <summary>
            /// Add load library calls for all imported libs.
            /// </summary>
            private static void AddLoadLibraryCalls(TypeDefinition type, MethodDefinition classCtor)
            {
                var libs = type.Methods.Where(x => x.HasPInvokeInfo).Select(x => x.PInvokeInfo.Module.Name).Distinct().ToList();
                var typeSystem = type.Module.TypeSystem;
                var loadLibMethod = new MethodReference("LoadLibrary", typeSystem.Void, typeSystem.LookupType("Java.Lang", "System"));
                loadLibMethod.Parameters.Add(new ParameterDefinition(typeSystem.String));

                var seq = new ILSequence();
                foreach (var libName in libs)
                {
                    // Add System.LoadLibrary(libName) call
                    seq.Append(new Instruction(OpCodes.Ldstr, libName));
                    seq.Append(new Instruction(OpCodes.Call, loadLibMethod));
                }

                seq.InsertTo(0, classCtor.Body);
            }
Example #2
0
            /// <summary>
            /// Add load library calls for all imported libs.
            /// </summary>
            private static void AddLoadLibraryCalls(TypeDefinition type, MethodDefinition classCtor)
            {
                var libs          = type.Methods.Where(x => x.HasPInvokeInfo).Select(x => x.PInvokeInfo.Module.Name).Distinct().ToList();
                var typeSystem    = type.Module.TypeSystem;
                var loadLibMethod = new MethodReference("LoadLibrary", typeSystem.Void, typeSystem.LookupType("Java.Lang", "System"));

                loadLibMethod.Parameters.Add(new ParameterDefinition(typeSystem.String));

                var seq = new ILSequence();

                foreach (var libName in libs)
                {
                    // Add System.LoadLibrary(libName) call
                    seq.Append(new Instruction(OpCodes.Ldstr, libName));
                    seq.Append(new Instruction(OpCodes.Call, loadLibMethod));
                }

                seq.InsertTo(0, classCtor.Body);
            }
Example #3
0
        private static Instruction[] GetCallArguments(MethodBody body, Instruction callInstruction)
        {
            var ilseq = new ILSequence();

            foreach (var i in body.Instructions)
            {
                ilseq.Append(i);
                if (i == callInstruction)
                {
                    break;
                }
            }
            Instruction[] args = callInstruction.GetCallArguments(ilseq, false);
            return(args);
        }
Example #4
0
            /// <summary>
            /// Inline the call to the given method
            /// </summary>
            private static void InlineCall(Instruction instruction, MethodBody body, MethodDefinition targetMethod)
            {
                // Prepare
                var prefixSeq = new ILSequence();

                // Create "this" variable
                VariableDefinition thisVariable = null;

                if (targetMethod.HasThis)
                {
                    thisVariable = new VariableDefinition(targetMethod.DeclaringType);
                    body.Variables.Add(thisVariable);
                    body.InitLocals = true;
                }

                // Store argument in variables
                var paramVariables = new List <VariableDefinition>();

                foreach (var parameter in targetMethod.Parameters.Reverse())
                {
                    // Create variable
                    var paramVariable = new VariableDefinition(parameter.ParameterType);
                    body.Variables.Add(paramVariable);
                    paramVariables.Insert(0, paramVariable);

                    // Pop
                    prefixSeq.Emit(OpCodes.Stloc, paramVariable);
                }

                // Store this argument (if any)
                if (thisVariable != null)
                {
                    // Pop
                    prefixSeq.Emit(OpCodes.Stloc, thisVariable);
                }

                // Clone variables first
                var source    = targetMethod.Body;
                var variables = new List <VariableDefinition>();

                foreach (var sv in source.Variables)
                {
                    var clone = new VariableDefinition(sv.VariableType);
                    variables.Add(clone);
                    body.Variables.Add(clone);
                }

                // Now clone instructions
                var seq = new ILSequence();

                foreach (var instr in source.Instructions)
                {
                    var ni = new Instruction(instr.OpCode, instr.Operand);
                    seq.Append(ni);
                    ni.Offset = instr.Offset;

                    // Convert variable opcodes
                    switch (instr.OpCode.OperandType)
                    {
                    case OperandType.InlineVar:
                    case OperandType.ShortInlineVar:
                    {
                        var index = source.Variables.IndexOf((VariableDefinition)instr.Operand);
                        ni.Operand = variables[index];
                    }
                    break;
                    }

                    // Convert parameter opcodes
                    switch (instr.OpCode.Code)
                    {
                    case Code.Ldarg:
                    {
                        var index = targetMethod.Parameters.IndexOf((ParameterDefinition)instr.Operand);
                        ni.Operand = (index >= 0) ? paramVariables[index] : thisVariable;
                        ni.OpCode  = OpCodes.Ldloc;
                    }
                    break;

                    case Code.Ldarga:
                    {
                        var index = targetMethod.Parameters.IndexOf((ParameterDefinition)instr.Operand);
                        ni.Operand = (index >= 0) ? paramVariables[index] : thisVariable;
                        ni.OpCode  = OpCodes.Ldloca;
                    }
                    break;

                    case Code.Starg:
                    {
                        var index = targetMethod.Parameters.IndexOf((ParameterDefinition)instr.Operand);
                        ni.Operand = (index >= 0) ? paramVariables[index] : thisVariable;
                        ni.OpCode  = OpCodes.Stloc;
                    }
                    break;
                    }
                }

                // Update branch targets
                for (var i = 0; i < seq.Length; i++)
                {
                    var instr = seq[i];
                    var oldi  = source.Instructions[i];

                    if (instr.OpCode.OperandType == OperandType.InlineSwitch)
                    {
                        var olds    = (Instruction[])oldi.Operand;
                        var targets = new Instruction[olds.Length];

                        for (int j = 0; j < targets.Length; j++)
                        {
                            targets[j] = GetClone(seq, source.Instructions, olds[j]);
                        }

                        instr.Operand = targets;
                    }
                    else if (instr.OpCode.OperandType == OperandType.ShortInlineBrTarget ||
                             instr.OpCode.OperandType == OperandType.InlineBrTarget)
                    {
                        instr.Operand = GetClone(seq, source.Instructions, (Instruction)oldi.Operand);
                    }
                }

                // Clone exception handlers
                if (source.HasExceptionHandlers)
                {
                    body.ComputeOffsets();
                    CloneInstructions(seq, source.Instructions, body.ExceptionHandlers, source.ExceptionHandlers);
                }

                // Replace ret instructions
                var end             = seq.Emit(OpCodes.Nop);
                var retInstructions = seq.Where(x => x.OpCode.Code == Code.Ret).ToList();

                foreach (var ins in retInstructions)
                {
                    ins.OpCode  = OpCodes.Br;
                    ins.Operand = end;
                }

                // cast return type of a generic method. TODO: better might be to
                // correctly resolve calls to generic instances as well (above)
                if (targetMethod.ReturnType.IsGenericParameter)
                {
                    var           methodRef  = (MethodReference)instruction.Operand;
                    TypeReference returnType = null;

                    var gp = (GenericParameter)methodRef.ReturnType;
                    if (gp.Type == GenericParameterType.Type)
                    {
                        var gi = methodRef.DeclaringType as IGenericInstance;
                        if (gi != null && gi.HasGenericArguments)
                        {
                            returnType = gi.GenericArguments[gp.Position];
                        }
                    }
                    else if (gp.Type == GenericParameterType.Method)
                    {
                        var gi = methodRef as IGenericInstance;
                        if (gi != null && gi.HasGenericArguments)
                        {
                            returnType = gi.GenericArguments[gp.Position];
                        }
                    }

                    if (returnType != null)
                    {
                        if (!returnType.IsPrimitive)
                        {
                            seq.Emit(OpCodes.Castclass, returnType);
                        }
                        // todo: handle primitive types. unbox them? are structs correctly handled? enums?
                    }
                }

                // Insert cloned instructions
                prefixSeq.InsertTo(0, seq);
                seq.InsertToAfter(instruction, body);

                // Change replaced instruction to nop
                instruction.ChangeToNop();

                // Update offsets
                body.ComputeOffsets();
            }
            /// <summary>
            /// Inline the call to the given ctor
            /// </summary>
            private static void InlineNewObjCall(Instruction instruction, MethodBody body, MethodDefinition ctor)
            {
                // Prepare
                var prefixSeq = new ILSequence();

                ctor.Body.SimplifyMacros();

                // Create "this" variable
                var thisVariable = new VariableDefinition(ctor.DeclaringType);

                body.Variables.Add(thisVariable);
                body.InitLocals = true;

                // Store argument in variables
                var paramVariables = new List <VariableDefinition>();

                foreach (var parameter in ctor.Parameters.Reverse())
                {
                    // Create variable
                    var paramVariable = new VariableDefinition(parameter.ParameterType);
                    body.Variables.Add(paramVariable);
                    paramVariables.Insert(0, paramVariable);

                    // Pop
                    prefixSeq.Emit(OpCodes.Stloc, paramVariable);
                }

                // Clone variables first
                var source    = ctor.Body;
                var variables = new List <VariableDefinition>();

                foreach (var sv in source.Variables)
                {
                    var clone = new VariableDefinition(sv.VariableType);
                    variables.Add(clone);
                    body.Variables.Add(clone);
                }

                // Now clone instructions
                var seq = new ILSequence();

                foreach (var instr in source.Instructions)
                {
                    var ni = new Instruction(instr.OpCode, instr.Operand);
                    seq.Append(ni);
                    ni.Offset = instr.Offset;

                    // Convert variable opcodes
                    switch (instr.OpCode.OperandType)
                    {
                    case OperandType.InlineVar:
                    case OperandType.ShortInlineVar:
                    {
                        var index = source.Variables.IndexOf((VariableDefinition)instr.Operand);
                        ni.Operand = variables[index];
                    }
                    break;
                    }

                    // Convert parameter opcodes
                    switch (instr.OpCode.Code)
                    {
                    case Mono.Cecil.Cil.Code.Ldarg:
                    {
                        var index = ctor.Parameters.IndexOf((ParameterDefinition)instr.Operand);
                        ni.Operand = (index >= 0) ? paramVariables[index] : thisVariable;
                        ni.OpCode  = OpCodes.Ldloc;
                    }
                    break;

                    case Mono.Cecil.Cil.Code.Ldarga:
                    {
                        var index = ctor.Parameters.IndexOf((ParameterDefinition)instr.Operand);
                        ni.Operand = (index >= 0) ? paramVariables[index] : thisVariable;
                        ni.OpCode  = OpCodes.Ldloca;
                    }
                    break;

                    case Mono.Cecil.Cil.Code.Starg:
                    {
                        var index = ctor.Parameters.IndexOf((ParameterDefinition)instr.Operand);
                        ni.Operand = (index >= 0) ? paramVariables[index] : thisVariable;
                        ni.OpCode  = OpCodes.Stloc;
                    }
                    break;
                    }
                }

                // Update branch targets
                for (var i = 0; i < seq.Length; i++)
                {
                    var instr = seq[i];
                    var oldi  = source.Instructions[i];

                    if (instr.OpCode.OperandType == OperandType.InlineSwitch)
                    {
                        var olds    = (Instruction[])oldi.Operand;
                        var targets = new Instruction[olds.Length];

                        for (int j = 0; j < targets.Length; j++)
                        {
                            targets[j] = GetClone(seq, source.Instructions, olds[j]);
                        }

                        instr.Operand = targets;
                    }
                    else if (instr.OpCode.OperandType == OperandType.ShortInlineBrTarget ||
                             instr.OpCode.OperandType == OperandType.InlineBrTarget)
                    {
                        instr.Operand = GetClone(seq, source.Instructions, (Instruction)oldi.Operand);
                    }
                }

                // Clone exception handlers
                if (source.HasExceptionHandlers)
                {
                    CloneInstructions(seq, source.Instructions, body.ExceptionHandlers, source.ExceptionHandlers);
                }

                // Find call to "this" ctor
                var callToCtors = seq.Where(x => IsCallToThisCtor(x, ctor)).ToList();

                if (callToCtors.Count == 0)
                {
                    throw new CompilerException(string.Format("No call to another this ctor found in {0}", ctor));
                }
                if (callToCtors.Count > 1)
                {
                    throw new CompilerException(string.Format("Multiple calls to another this ctor found in {0}", ctor));
                }
                var callToCtor = callToCtors[0];

                // Change "ld this" to nop
                var args = callToCtor.GetCallArguments(seq, true);

                args[0].ChangeToNop(); // Replace ldarg.0

                // Replace call to this ctor with newobj
                var callSeq = new ILSequence();

                callSeq.Emit(OpCodes.Newobj, (MethodReference)callToCtor.Operand);
                callSeq.Emit(OpCodes.Stloc, thisVariable); // Save new object
                callToCtor.ChangeToNop();
                callSeq.InsertToBefore(callToCtor, seq);

                // Replace ret instructions
                var end             = seq.Emit(OpCodes.Ldloc, thisVariable);
                var retInstructions = seq.Where(x => x.OpCode.Code == Mono.Cecil.Cil.Code.Ret).ToList();

                foreach (var ins in retInstructions)
                {
                    ins.OpCode  = OpCodes.Br;
                    ins.Operand = end;
                }

                // Insert cloned instructions
                prefixSeq.InsertTo(0, seq);
                seq.InsertToAfter(instruction, body);

                // Change replaced instruction to nop
                instruction.ChangeToNop();

                // Update offsets
                body.ComputeOffsets();
            }
Example #6
0
 private static Instruction[] GetCallArguments(MethodBody body, Instruction callInstruction)
 {
     var ilseq = new ILSequence();
     foreach (var i in body.Instructions)
     {
         ilseq.Append(i);
         if (i == callInstruction)
             break;
     }
     Instruction[] args = callInstruction.GetCallArguments(ilseq, false);
     return args;
 }
Example #7
0
            /// <summary>
            /// Inline the call to the given method
            /// </summary>
            private static void InlineCall(Instruction instruction, MethodBody body, MethodDefinition targetMethod)
            {
                // Prepare
                var prefixSeq = new ILSequence();

                // Create "this" variable
                VariableDefinition thisVariable = null;

                if (targetMethod.HasThis)
                {
                    thisVariable = new VariableDefinition(targetMethod.DeclaringType);
                    body.Variables.Add(thisVariable);
                    body.InitLocals = true;
                }

                // Store argument in variables
                var paramVariables = new List <VariableDefinition>();

                foreach (var parameter in targetMethod.Parameters.Reverse())
                {
                    // Create variable
                    var paramVariable = new VariableDefinition(parameter.ParameterType);
                    body.Variables.Add(paramVariable);
                    paramVariables.Insert(0, paramVariable);

                    // Pop
                    prefixSeq.Emit(OpCodes.Stloc, paramVariable);
                }

                // Store this argument (if any)
                if (thisVariable != null)
                {
                    // Pop
                    prefixSeq.Emit(OpCodes.Stloc, thisVariable);
                }

                // Clone variables first
                var source    = targetMethod.Body;
                var variables = new List <VariableDefinition>();

                foreach (var sv in source.Variables)
                {
                    var clone = new VariableDefinition(sv.VariableType);
                    variables.Add(clone);
                    body.Variables.Add(clone);
                }

                // Now clone instructions
                var seq = new ILSequence();

                foreach (var instr in source.Instructions)
                {
                    var ni = new Instruction(instr.OpCode, instr.Operand);
                    seq.Append(ni);
                    ni.Offset = instr.Offset;

                    // Convert variable opcodes
                    switch (instr.OpCode.OperandType)
                    {
                    case OperandType.InlineVar:
                    case OperandType.ShortInlineVar:
                    {
                        var index = source.Variables.IndexOf((VariableDefinition)instr.Operand);
                        ni.Operand = variables[index];
                    }
                    break;
                    }

                    // Convert parameter opcodes
                    switch (instr.OpCode.Code)
                    {
                    case Code.Ldarg:
                    {
                        var index = targetMethod.Parameters.IndexOf((ParameterDefinition)instr.Operand);
                        ni.Operand = (index >= 0) ? paramVariables[index] : thisVariable;
                        ni.OpCode  = OpCodes.Ldloc;
                    }
                    break;

                    case Code.Ldarga:
                    {
                        var index = targetMethod.Parameters.IndexOf((ParameterDefinition)instr.Operand);
                        ni.Operand = (index >= 0) ? paramVariables[index] : thisVariable;
                        ni.OpCode  = OpCodes.Ldloca;
                    }
                    break;

                    case Code.Starg:
                    {
                        var index = targetMethod.Parameters.IndexOf((ParameterDefinition)instr.Operand);
                        ni.Operand = (index >= 0) ? paramVariables[index] : thisVariable;
                        ni.OpCode  = OpCodes.Stloc;
                    }
                    break;
                    }
                }

                // Update branch targets
                for (var i = 0; i < seq.Length; i++)
                {
                    var instr = seq[i];
                    var oldi  = source.Instructions[i];

                    if (instr.OpCode.OperandType == OperandType.InlineSwitch)
                    {
                        var olds    = (Instruction[])oldi.Operand;
                        var targets = new Instruction[olds.Length];

                        for (int j = 0; j < targets.Length; j++)
                        {
                            targets[j] = GetClone(seq, source.Instructions, olds[j]);
                        }

                        instr.Operand = targets;
                    }
                    else if (instr.OpCode.OperandType == OperandType.ShortInlineBrTarget ||
                             instr.OpCode.OperandType == OperandType.InlineBrTarget)
                    {
                        instr.Operand = GetClone(seq, source.Instructions, (Instruction)oldi.Operand);
                    }
                }

                // Clone exception handlers
                if (source.HasExceptionHandlers)
                {
                    body.ComputeOffsets();
                    CloneInstructions(seq, source.Instructions, body.ExceptionHandlers, source.ExceptionHandlers);
                }

                // Replace ret instructions
                var end             = seq.Emit(OpCodes.Nop);
                var retInstructions = seq.Where(x => x.OpCode.Code == Code.Ret).ToList();

                foreach (var ins in retInstructions)
                {
                    ins.OpCode  = OpCodes.Br;
                    ins.Operand = end;
                }

                // Insert cloned instructions
                prefixSeq.InsertTo(0, seq);
                seq.InsertToAfter(instruction, body);

                // Change replaced instruction to nop
                instruction.ChangeToNop();

                // Update offsets
                body.ComputeOffsets();
            }
            /// <summary>
            /// Inline the call to the given ctor
            /// </summary>
            private static void InlineNewObjCall(Instruction instruction, MethodBody body, MethodDefinition ctor)
            {
                // Prepare 
                var prefixSeq = new ILSequence();
                ctor.Body.SimplifyMacros();

                // Create "this" variable
                var thisVariable = new VariableDefinition(ctor.DeclaringType);
                body.Variables.Add(thisVariable);
                body.InitLocals = true;

                // Store argument in variables
                var paramVariables = new List<VariableDefinition>();
                foreach (var parameter in ctor.Parameters.Reverse())
                {
                    // Create variable
                    var paramVariable = new VariableDefinition(parameter.ParameterType);
                    body.Variables.Add(paramVariable);
                    paramVariables.Insert(0, paramVariable);

                    // Pop 
                    prefixSeq.Emit(OpCodes.Stloc, paramVariable);
                }

                // Clone variables first
                var source = ctor.Body;
                var variables = new List<VariableDefinition>();
                foreach (var sv in source.Variables)
                {
                    var clone = new VariableDefinition(sv.VariableType);
                    variables.Add(clone);
                    body.Variables.Add(clone);
                }

                // Now clone instructions
                var seq = new ILSequence();
                foreach (var instr in source.Instructions)
                {
                    var ni = new Instruction(instr.OpCode, instr.Operand);
                    seq.Append(ni);
                    ni.Offset = instr.Offset;

                    // Convert variable opcodes
                    switch (instr.OpCode.OperandType)
                    {
                        case OperandType.InlineVar:
                        case OperandType.ShortInlineVar:
                            {
                                var index = source.Variables.IndexOf((VariableDefinition) instr.Operand);
                                ni.Operand = variables[index];
                            }
                            break;
                    }

                    // Convert parameter opcodes
                    switch (instr.OpCode.Code)
                    {
                        case Mono.Cecil.Cil.Code.Ldarg:
                            {
                                var index = ctor.Parameters.IndexOf((ParameterDefinition) instr.Operand);
                                ni.Operand = (index >= 0) ? paramVariables[index] : thisVariable;
                                ni.OpCode = OpCodes.Ldloc;
                            }
                            break;
                        case Mono.Cecil.Cil.Code.Ldarga:
                            {
                                var index = ctor.Parameters.IndexOf((ParameterDefinition) instr.Operand);
                                ni.Operand = (index >= 0) ? paramVariables[index] : thisVariable;
                                ni.OpCode = OpCodes.Ldloca;
                            }
                            break;
                        case Mono.Cecil.Cil.Code.Starg:
                            {
                                var index = ctor.Parameters.IndexOf((ParameterDefinition) instr.Operand);
                                ni.Operand = (index >= 0) ? paramVariables[index] : thisVariable;
                                ni.OpCode = OpCodes.Stloc;
                            }
                            break;
                    }
                }

                // Update branch targets
                for (var i = 0; i < seq.Length; i++)
                {
                    var instr = seq[i];
                    var oldi = source.Instructions[i];

                    if (instr.OpCode.OperandType == OperandType.InlineSwitch)
                    {
                        var olds = (Instruction[]) oldi.Operand;
                        var targets = new Instruction[olds.Length];

                        for (int j = 0; j < targets.Length; j++)
                        {
                            targets[j] = GetClone(seq, source.Instructions, olds[j]);
                        }

                        instr.Operand = targets;
                    }
                    else if (instr.OpCode.OperandType == OperandType.ShortInlineBrTarget ||
                             instr.OpCode.OperandType == OperandType.InlineBrTarget)
                    {
                        instr.Operand = GetClone(seq, source.Instructions, (Instruction) oldi.Operand);
                    }
                }

                // Clone exception handlers
                if (source.HasExceptionHandlers)
                {
                    CloneInstructions(seq, source.Instructions, body.ExceptionHandlers, source.ExceptionHandlers);
                }

                // Find call to "this" ctor
                var callToCtors = seq.Where(x => IsCallToThisCtor(x, ctor)).ToList();
                if (callToCtors.Count == 0)
                    throw new CompilerException(string.Format("No call to another this ctor found in {0}", ctor));
                if (callToCtors.Count > 1)
                    throw new CompilerException(string.Format("Multiple calls to another this ctor found in {0}", ctor));
                var callToCtor = callToCtors[0];

                // Change "ld this" to nop
                var args = callToCtor.GetCallArguments(seq, true);
                args[0].ChangeToNop(); // Replace ldarg.0

                // Replace call to this ctor with newobj
                var callSeq = new ILSequence();
                callSeq.Emit(OpCodes.Newobj, (MethodReference) callToCtor.Operand);
                callSeq.Emit(OpCodes.Stloc, thisVariable); // Save new object
                callToCtor.ChangeToNop();
                callSeq.InsertToBefore(callToCtor, seq);

                // Replace ret instructions
                var end = seq.Emit(OpCodes.Ldloc, thisVariable);
                var retInstructions = seq.Where(x => x.OpCode.Code == Mono.Cecil.Cil.Code.Ret).ToList();
                foreach (var ins in retInstructions)
                {
                    ins.OpCode = OpCodes.Br;
                    ins.Operand = end;
                }

                // Insert cloned instructions
                prefixSeq.InsertTo(0, seq);
                seq.InsertToAfter(instruction, body);

                // Change replaced instruction to nop
                instruction.ChangeToNop();

                // Update offsets
                body.ComputeOffsets();
            }
Example #9
0
            /// <summary>
            /// Inline the call to the given method
            /// </summary>
            private static void InlineCall(Instruction instruction, MethodBody body, MethodDefinition targetMethod)
            {
                // Prepare 
                var prefixSeq = new ILSequence();

                // Create "this" variable
                VariableDefinition thisVariable = null;
                if (targetMethod.HasThis)
                {
                    thisVariable = new VariableDefinition(targetMethod.DeclaringType);
                    body.Variables.Add(thisVariable);
                    body.InitLocals = true;
                }

                // Store argument in variables
                var paramVariables = new List<VariableDefinition>();
                foreach (var parameter in targetMethod.Parameters.Reverse())
                {
                    // Create variable
                    var paramVariable = new VariableDefinition(parameter.ParameterType);
                    body.Variables.Add(paramVariable);
                    paramVariables.Insert(0, paramVariable);

                    // Pop 
                    prefixSeq.Emit(OpCodes.Stloc, paramVariable);
                }

                // Store this argument (if any)
                if (thisVariable != null)
                {
                    // Pop 
                    prefixSeq.Emit(OpCodes.Stloc, thisVariable);
                }

                // Clone variables first
                var source = targetMethod.Body;
                var variables = new List<VariableDefinition>();
                foreach (var sv in source.Variables)
                {
                    var clone = new VariableDefinition(sv.VariableType);
                    variables.Add(clone);
                    body.Variables.Add(clone);
                }

                // Now clone instructions
                var seq = new ILSequence();
                foreach (var instr in source.Instructions)
                {
                    var ni = new Instruction(instr.OpCode, instr.Operand);
                    seq.Append(ni);
                    ni.Offset = instr.Offset;

                    // Convert variable opcodes
                    switch (instr.OpCode.OperandType)
                    {
                        case OperandType.InlineVar:
                        case OperandType.ShortInlineVar:
                            {
                                var index = source.Variables.IndexOf((VariableDefinition)instr.Operand);
                                ni.Operand = variables[index];
                            }
                            break;
                    }

                    // Convert parameter opcodes
                    switch (instr.OpCode.Code)
                    {
                        case Code.Ldarg:
                            {
                                var index = targetMethod.Parameters.IndexOf((ParameterDefinition)instr.Operand);
                                ni.Operand = (index >= 0) ? paramVariables[index] : thisVariable;
                                ni.OpCode = OpCodes.Ldloc;
                            }
                            break;
                        case Code.Ldarga:
                            {
                                var index = targetMethod.Parameters.IndexOf((ParameterDefinition)instr.Operand);
                                ni.Operand = (index >= 0) ? paramVariables[index] : thisVariable;
                                ni.OpCode = OpCodes.Ldloca;
                            }
                            break;
                        case Code.Starg:
                            {
                                var index = targetMethod.Parameters.IndexOf((ParameterDefinition)instr.Operand);
                                ni.Operand = (index >= 0) ? paramVariables[index] : thisVariable;
                                ni.OpCode = OpCodes.Stloc;
                            }
                            break;
                    }
                }

                // Update branch targets
                for (var i = 0; i < seq.Length; i++)
                {
                    var instr = seq[i];
                    var oldi = source.Instructions[i];

                    if (instr.OpCode.OperandType == OperandType.InlineSwitch)
                    {
                        var olds = (Instruction[])oldi.Operand;
                        var targets = new Instruction[olds.Length];

                        for (int j = 0; j < targets.Length; j++)
                        {
                            targets[j] = GetClone(seq, source.Instructions, olds[j]);
                        }

                        instr.Operand = targets;
                    }
                    else if (instr.OpCode.OperandType == OperandType.ShortInlineBrTarget ||
                             instr.OpCode.OperandType == OperandType.InlineBrTarget)
                    {
                        instr.Operand = GetClone(seq, source.Instructions, (Instruction)oldi.Operand);
                    }
                }

                // Clone exception handlers
                if (source.HasExceptionHandlers)
                {
                    body.ComputeOffsets();
                    CloneInstructions(seq, source.Instructions, body.ExceptionHandlers, source.ExceptionHandlers);
                }

                // Replace ret instructions
                var end = seq.Emit(OpCodes.Nop);
                var retInstructions = seq.Where(x => x.OpCode.Code == Code.Ret).ToList();
                foreach (var ins in retInstructions)
                {
                    ins.OpCode = OpCodes.Br;
                    ins.Operand = end;
                }

                // cast return type of a generic method. TODO: better might be to
                // correctly resolve calls to generic instances as well (above)
                if (targetMethod.ReturnType.IsGenericParameter)
                {
                    var methodRef = (MethodReference)instruction.Operand;
                    TypeReference returnType = null;

                    var gp = (GenericParameter)methodRef.ReturnType;
                    if (gp.Type == GenericParameterType.Type)
                    {
                        var gi = methodRef.DeclaringType as IGenericInstance;
                        if (gi != null && gi.HasGenericArguments)
                            returnType = gi.GenericArguments[gp.Position];
                    }
                    else if (gp.Type == GenericParameterType.Method)
                    {
                        var gi = methodRef as IGenericInstance;
                        if (gi != null && gi.HasGenericArguments)
                            returnType = gi.GenericArguments[gp.Position];
                    }

                    if (returnType != null)
                    {
                        if (!returnType.IsPrimitive)
                        {
                            seq.Emit(OpCodes.Castclass, returnType);
                        }
                        // todo: handle primitive types. unbox them? are structs correctly handled? enums?
                    }
                }

                // Insert cloned instructions
                prefixSeq.InsertTo(0, seq);
                seq.InsertToAfter(instruction, body);

                // Change replaced instruction to nop
                instruction.ChangeToNop();

                // Update offsets
                body.ComputeOffsets();
            }
Example #10
0
            /// <summary>
            /// Inline the call to the given method
            /// </summary>
            private static void InlineCall(Instruction instruction, MethodBody body, MethodDefinition targetMethod)
            {
                // Prepare 
                var prefixSeq = new ILSequence();

                // Create "this" variable
                VariableDefinition thisVariable = null;
                if (targetMethod.HasThis)
                {
                    thisVariable = new VariableDefinition(targetMethod.DeclaringType);
                    body.Variables.Add(thisVariable);
                    body.InitLocals = true;
                }

                // Store argument in variables
                var paramVariables = new List<VariableDefinition>();
                foreach (var parameter in targetMethod.Parameters.Reverse())
                {
                    // Create variable
                    var paramVariable = new VariableDefinition(parameter.ParameterType);
                    body.Variables.Add(paramVariable);
                    paramVariables.Insert(0, paramVariable);

                    // Pop 
                    prefixSeq.Emit(OpCodes.Stloc, paramVariable);
                }

                // Store this argument (if any)
                if (thisVariable != null)
                {
                    // Pop 
                    prefixSeq.Emit(OpCodes.Stloc, thisVariable);
                }

                // Clone variables first
                var source = targetMethod.Body;
                var variables = new List<VariableDefinition>();
                foreach (var sv in source.Variables)
                {
                    var clone = new VariableDefinition(sv.VariableType);
                    variables.Add(clone);
                    body.Variables.Add(clone);
                }

                // Now clone instructions
                var seq = new ILSequence();
                foreach (var instr in source.Instructions)
                {
                    var ni = new Instruction(instr.OpCode, instr.Operand);
                    seq.Append(ni);
                    ni.Offset = instr.Offset;

                    // Convert variable opcodes
                    switch (instr.OpCode.OperandType)
                    {
                        case OperandType.InlineVar:
                        case OperandType.ShortInlineVar:
                            {
                                var index = source.Variables.IndexOf((VariableDefinition)instr.Operand);
                                ni.Operand = variables[index];
                            }
                            break;
                    }

                    // Convert parameter opcodes
                    switch (instr.OpCode.Code)
                    {
                        case Code.Ldarg:
                            {
                                var index = targetMethod.Parameters.IndexOf((ParameterDefinition)instr.Operand);
                                ni.Operand = (index >= 0) ? paramVariables[index] : thisVariable;
                                ni.OpCode = OpCodes.Ldloc;
                            }
                            break;
                        case Code.Ldarga:
                            {
                                var index = targetMethod.Parameters.IndexOf((ParameterDefinition)instr.Operand);
                                ni.Operand = (index >= 0) ? paramVariables[index] : thisVariable;
                                ni.OpCode = OpCodes.Ldloca;
                            }
                            break;
                        case Code.Starg:
                            {
                                var index = targetMethod.Parameters.IndexOf((ParameterDefinition)instr.Operand);
                                ni.Operand = (index >= 0) ? paramVariables[index] : thisVariable;
                                ni.OpCode = OpCodes.Stloc;
                            }
                            break;
                    }
                }

                // Update branch targets
                for (var i = 0; i < seq.Length; i++)
                {
                    var instr = seq[i];
                    var oldi = source.Instructions[i];

                    if (instr.OpCode.OperandType == OperandType.InlineSwitch)
                    {
                        var olds = (Instruction[])oldi.Operand;
                        var targets = new Instruction[olds.Length];

                        for (int j = 0; j < targets.Length; j++)
                        {
                            targets[j] = GetClone(seq, source.Instructions, olds[j]);
                        }

                        instr.Operand = targets;
                    }
                    else if (instr.OpCode.OperandType == OperandType.ShortInlineBrTarget ||
                             instr.OpCode.OperandType == OperandType.InlineBrTarget)
                    {
                        instr.Operand = GetClone(seq, source.Instructions, (Instruction)oldi.Operand);
                    }
                }

                // Clone exception handlers
                if (source.HasExceptionHandlers)
                {
                    body.ComputeOffsets();
                    CloneInstructions(seq, source.Instructions, body.ExceptionHandlers, source.ExceptionHandlers);
                }

                // Replace ret instructions
                var end = seq.Emit(OpCodes.Nop);
                var retInstructions = seq.Where(x => x.OpCode.Code == Code.Ret).ToList();
                foreach (var ins in retInstructions)
                {
                    ins.OpCode = OpCodes.Br;
                    ins.Operand = end;
                }

                // Insert cloned instructions
                prefixSeq.InsertTo(0, seq);
                seq.InsertToAfter(instruction, body);

                // Change replaced instruction to nop
                instruction.ChangeToNop();

                // Update offsets
                body.ComputeOffsets();
            }