private void TryCatchSetTargetRegister(FinallyBlockState finallyBlock, bool needTargetRegister)
        {
            var localTargets = finallyBlock.Targets.Where(t => t.State == finallyBlock);

            if (needTargetRegister)
            {
                finallyBlock.TargetRegister = Frame.AllocateTemp(PrimitiveType.Int);
                var setToZero = new Instruction(RCode.Const, 0, new [] { finallyBlock.TargetRegister });
                instructions.Insert(finallyBlock.FirstInstruction.Index, setToZero);

                foreach (var t in localTargets)
                {
                    if (t.SetTarget.Code != RCode.Nop)
                    {
                        t.SetTarget.Registers.Add(finallyBlock.TargetRegister);
                    }
                }
            }
            else
            {
                foreach (var t in localTargets)
                {
                    t.SetTarget.ConvertToNop();
                }
            }
        }
            private RLRange BranchToFinally(FinallyTarget target, IEnumerable <RLRange> prefix, ISourceLocation seqp, ref int insIdx)
            {
                Debug.Assert(this.HasFinally);

                Targets.Add(target);

                var setTarget = new Instruction(RCode.Const)
                {
                    SequencePoint = seqp
                };                                                                     // operand and register are set later

                _compiler.instructions.Insert(insIdx++, setTarget);
                var branch = new Instruction(RCode.Goto, NonException)
                {
                    SequencePoint = seqp
                };

                _compiler.instructions.Insert(insIdx++, branch);

                target.SetTarget   = setTarget;
                target.GotoFinally = branch;
                target.State       = this;

                return(new RLRange(prefix, setTarget, branch, null));
            }
            public RLRange BranchToFinally_Leave(Instruction insTarget, ref int insIdx)
            {
                var target = new FinallyTarget {
                    IsLeave = true, Destination = insTarget
                };

                return(BranchToFinally(target, null, AstNode.NoSource, ref insIdx));
            }
            public RLRange BranchToFinally_FallOut(ISourceLocation sourceLocation, Instruction insTarget, List <RLRange> args)
            {
                int insIdx = _compiler.instructions.Count;
                var target = new FinallyTarget {
                    Destination = insTarget, IsFallOut = true
                };

                return(BranchToFinally(target, args, sourceLocation, ref insIdx));
            }
 public FinallyBlockState(FinallyBlockState outerBlock, int depth, AstCompilerVisitor compiler, Instruction first, Instruction last)
 {
     _compiler           = compiler;
     NonException        = new Instruction();
     AfterExceptionCheck = new Instruction();
     FirstInstruction    = first;
     LastInstruction     = last;
     Depth             = depth;
     OuterFinallyBlock = outerBlock;
 }
        private Instruction TryCatchGotoEnd(FinallyBlockState state, Instruction last)
        {
            if (state.HasFinally)
            {
                var rlrange = state.BranchToFinally_FallOut(AstNode.NoSource, last, new List <RLRange>());
                return(rlrange.Last);
            }

            // goto end
            return(this.Add(AstNode.NoSource, RCode.Goto, last));
        }
        /// <summary>
        /// Create the body of the equals method.
        /// </summary>
        private static MethodBody CreateEqualsBody(ISourceLocation sequencePoint, AssemblyCompiler compiler, DexTargetPackage targetPackage, XMethodDefinition equalsMethod, Prototype equalsPrototype, FieldDefinition instanceField, ClassReference delegateClass)
        {
            MethodBody body = new MethodBody(null);

            // This pointer and method argument.
            Register rthis  = body.AllocateRegister(RCategory.Argument, RType.Object);
            Register rother = body.AllocateRegister(RCategory.Argument, RType.Object);

            // Create code.
            var ins = body.Instructions;

            // Temporary parameter result.
            Register result = body.AllocateRegister(RCategory.Temp, RType.Value);

            // Prepare the return instruction.
            Instruction returnInstruction = new Instruction(RCode.Return, result);

            // Check if other object can be casted.
            ins.Add(new Instruction(RCode.Instance_of, delegateClass, new[] { result, rother }));
            ins.Add(new Instruction(RCode.If_eqz, returnInstruction, new[] { result })); // compare instance members

            // Cast of the other object.
            ins.Add(new Instruction(RCode.Check_cast, delegateClass, new[] { rother }));

            // Get instance fields of this and other.
            var thisInstance  = body.AllocateRegister(RCategory.Temp, RType.Object);
            var otherInstance = body.AllocateRegister(RCategory.Temp, RType.Object);

            // Load the instance fields.
            ins.Add(new Instruction(RCode.Iget_object, thisInstance, rthis)
            {
                Operand = instanceField
            });
            ins.Add(new Instruction(RCode.Iget_object, otherInstance, rother)
            {
                Operand = instanceField
            });

            // Compare the instance fields.
            ins.Add(new Instruction(RCode.If_eq, returnInstruction, new[] { thisInstance, otherInstance })); // compare instance members

            // Set result to false if not equal.
            ins.Add(new Instruction(RCode.Const, 0, new[] { result }));

            // Add return instructions
            ins.Add(returnInstruction);

            return(body);
        }
Exemple #8
0
        /// <summary>
        /// Gets the lowest size (in bitsX) that is available for the given register in the given instruction.
        /// </summary>
        private static RegisterFlags GetLowestSize(Instruction instruction, Register r)
        {
            var result    = RegisterFlags.Bits16;
            var info      = OpCodeInfo.Get(instruction.Code.ToDex());
            var registers = instruction.Registers;

            for (var i = 0; i < registers.Count; i++)
            {
                if (registers[i] == r)
                {
                    var size = info.GetUsage(i) & RegisterFlags.SizeMask;
                    if (size < result)
                    {
                        result = size;
                    }
                }
            }
            return(result);
        }
        /// <summary>
        /// return the first emitted instruction
        /// </summary>
        private Instruction TryCatchEmitTargetInstruction(FinallyTarget target, ref int insIdx, FinallyBlockState outerFinallyBlock)
        {
            bool emitReturn = outerFinallyBlock == null && target.IsReturn;

            bool chainToOuterBlock = outerFinallyBlock != null && target.IsReturn;

            if (target.IsLeave && outerFinallyBlock != null)
            {
                // check if the leave leaves the outer finally block as well.
                if (target.Destination.Index < outerFinallyBlock.FirstInstruction.Index
                 || target.Destination.Index > outerFinallyBlock.LastInstruction.Index)
                {
                    chainToOuterBlock = true;
                }
            }

            if (emitReturn)
            {
                Instruction ret;
                if (currentMethod.ReturnsVoid)
                {
                    ret = new Instruction(RCode.Return_void);
                }
                else
                {
                    var retCode = currentMethod.ReturnsDexWide
                        ? RCode.Return_wide
                        : currentMethod.ReturnsDexValue
                            ? RCode.Return
                            : RCode.Return_object;
                    ret = new Instruction(retCode, finallyState.ReturnValueRegister);
                }
                instructions.Insert(insIdx++, ret);
                return ret;
            }
            else if (chainToOuterBlock)
            {
                Debug.Assert(outerFinallyBlock != null);
                int id = target.SetTarget.Code == RCode.Nop ? 0 : (int)target.SetTarget.Operand;

                RLRange range;
                if (target.IsReturn)
                    range = outerFinallyBlock.BranchToFinally_Ret(ref insIdx);
                else // IsLeave
                    range = outerFinallyBlock.BranchToFinally_Leave(target.Destination, ref insIdx);

                // This is a little bit hackish. We need to set the operand in the setTarget instruction.
                if (id == 0) range.First.ConvertToNop();
                else range.First.Operand = id;

                return range.First;
            }
            else
            {
                // goto
                var insGoto = new Instruction(RCode.Goto, target.Destination);
                instructions.Insert(insIdx++, insGoto);
                return insGoto;
            }
        }
        /// <summary>
        /// Create try/catch/finally/fault block
        /// </summary>
        public override RLRange Visit(AstTryCatchBlock node, AstNode parent)
        {
            var handler = new ExceptionHandler();

            //if (node.FaultBlock != null)
            //{
            //    Debugger.Break();
            //}

            // Setup instruction before/after my node.
            var first = new Instruction() { SequencePoint = node.SourceLocation};
            var last = new Instruction(RCode.Nop);

            FinallyBlockState finState = null;
            FinallyBlockState outerFinState = tryCatchStack.FirstOrDefault(f => f.HasFinally);

            if (tryCatchStack.Count == 0)
                finallyState.FinallyStacks.Add(new List<FinallyBlockState>());

            if (node.FinallyBlock != null)
            {
                // store finaly state
                finState = new FinallyBlockState(outerFinState, tryCatchStack.Count, this, first, last);

                finState.FinallyExceptionRegister = frame.AllocateTemp(new ClassReference("java.lang.Throwable")).Register;
                // clear the variable to make sure it isn't stale from a previous loop.
                // make sure this is outside the try block for edge case exceptions.
                this.Add(node.SourceLocation, RCode.Const, 0, finState.FinallyExceptionRegister);

                tryCatchStack.Push(finState);
                finallyState.FinallyStacks.Last().Add(finState);
            }
            else
            {
                finState = new FinallyBlockState(outerFinState, tryCatchStack.Count);
                tryCatchStack.Push(finState);
            }

            instructions.Add(first);
            // Emit try block
            handler.TryStart = first;
            node.TryBlock.AcceptOrDefault(this, node);
            handler.TryEnd = TryCatchGotoEnd(finState, last);

            var catchesStart = this.Add(AstNode.NoSource, RCode.Nop);

            // Emit "normal" catch blocks
            foreach (var catchBlock in node.CatchBlocks.Where(x => !x.IsCatchAll()))
            {
                var c = new Catch { Type = catchBlock.ExceptionType.GetReference(targetPackage) };
                handler.Catches.Add(c);
                var catchStart = this.Add(catchBlock.SourceLocation, RCode.Nop);
                catchBlock.Accept(this, node);
                c.Instruction = catchStart;

                TryCatchGotoEnd(finState, last);
            }

            // Emit "catch all" (if any)
            var catchAllBlock = node.CatchBlocks.SingleOrDefault(x => x.IsCatchAll());
            if (catchAllBlock != null)
            {
                var catchStart = this.Add(catchAllBlock.SourceLocation, RCode.Nop);
                catchAllBlock.Accept(this, node);
                handler.CatchAll = catchStart;

                TryCatchGotoEnd(finState, last);
            }

            var catchesEnd = this.Add(AstNode.NoSource, RCode.Nop);

            // clear try/catch/finally stack: we don't want to cover ourselves!
            tryCatchStack.Pop();

            // Emit finally code
            if (node.FinallyBlock != null)
            {
                // preparation.
                var finallyStart = this.Add(node.FinallyBlock.SourceLocation, RCode.Move_exception,
                                            finState.FinallyExceptionRegister);
                instructions.Add(finState.NonException);

                // the original handler
                node.FinallyBlock.Accept(this, node);

                // prepare the routing
                this.Add(AstNode.NoSource, RCode.If_eqz, finState.AfterExceptionCheck, finState.FinallyExceptionRegister);
                this.Add(AstNode.NoSource, RCode.Throw, finState.FinallyExceptionRegister);
                instructions.Add(finState.AfterExceptionCheck);

                // Set up exception handlers.
                if (catchAllBlock == null)
                {
                    // we need to cover the try block.
                    handler.CatchAll = finallyStart;
                }

                if (node.CatchBlocks.Any())
                {
                    // we need to cover the catch blocks
                    var finallyHandler = new ExceptionHandler
                    {
                        TryStart = catchesStart,
                        TryEnd = catchesEnd,
                        CatchAll = finallyStart
                    };
                    body.Exceptions.Add(finallyHandler);
                }
            }

            // Add end
            instructions.Add(last);

            // Record catch/catch-all handler
            if ((handler.CatchAll != null) || handler.Catches.Any())
            {
                body.Exceptions.Add(handler);
            }

            return new RLRange(first, last, null);
        }
        /// <summary>
        /// return the first emitted instruction
        /// </summary>
        private Instruction TryCatchEmitTargetInstruction(FinallyTarget target, ref int insIdx, FinallyBlockState outerFinallyBlock)
        {
            bool emitReturn = outerFinallyBlock == null && target.IsReturn;

            bool chainToOuterBlock = outerFinallyBlock != null && target.IsReturn;

            if (target.IsLeave && outerFinallyBlock != null)
            {
                // check if the leave leaves the outer finally block as well.
                if (target.Destination.Index < outerFinallyBlock.FirstInstruction.Index ||
                    target.Destination.Index > outerFinallyBlock.LastInstruction.Index)
                {
                    chainToOuterBlock = true;
                }
            }

            if (emitReturn)
            {
                Instruction ret;
                if (currentMethod.ReturnsVoid)
                {
                    ret = new Instruction(RCode.Return_void);
                }
                else
                {
                    var retCode = currentMethod.ReturnsDexWide
                        ? RCode.Return_wide
                        : currentMethod.ReturnsDexValue
                            ? RCode.Return
                            : RCode.Return_object;
                    ret = new Instruction(retCode, finallyState.ReturnValueRegister);
                }
                instructions.Insert(insIdx++, ret);
                return(ret);
            }
            else if (chainToOuterBlock)
            {
                Debug.Assert(outerFinallyBlock != null);
                int id = target.SetTarget.Code == RCode.Nop ? 0 : (int)target.SetTarget.Operand;

                RLRange range;
                if (target.IsReturn)
                {
                    range = outerFinallyBlock.BranchToFinally_Ret(ref insIdx);
                }
                else // IsLeave
                {
                    range = outerFinallyBlock.BranchToFinally_Leave(target.Destination, ref insIdx);
                }

                // This is a little bit hackish. We need to set the operand in the setTarget instruction.
                if (id == 0)
                {
                    range.First.ConvertToNop();
                }
                else
                {
                    range.First.Operand = id;
                }

                return(range.First);
            }
            else
            {
                // goto
                var insGoto = new Instruction(RCode.Goto, target.Destination);
                instructions.Insert(insIdx++, insGoto);
                return(insGoto);
            }
        }
        private void TryCatchSetTargetRegister(FinallyBlockState finallyBlock, bool needTargetRegister)
        {
            var localTargets = finallyBlock.Targets.Where(t => t.State == finallyBlock);

            if (needTargetRegister)
            {
                finallyBlock.TargetRegister = Frame.AllocateTemp(PrimitiveType.Int);
                var setToZero = new Instruction(RCode.Const, 0, new []{ finallyBlock.TargetRegister});
                instructions.Insert(finallyBlock.FirstInstruction.Index, setToZero);
                
                foreach (var t in localTargets)
                {
                    if (t.SetTarget.Code != RCode.Nop)
                        t.SetTarget.Registers.Add(finallyBlock.TargetRegister);
                }
            }
            else
            {
                foreach (var t in localTargets)
                    t.SetTarget.ConvertToNop();
            }
        }
        private void FixFinallyStack(List <FinallyBlockState> finallyStack)
        {
            // Find all identical targets in this stack.
            // To minimize assigments, order by number of targets. To simplify the routing
            // code when possible, prefer IsFallOut over IsLeave over IsReturn
            var targets = TryCatchGroupTargets(finallyStack.SelectMany(f => f.Targets));

            if (targets.Count == 0)
            {
                return;
            }

            // assign target group ids. zero is the default, and does not need to be set.
            int id = -1;

            foreach (var targetGroup in targets)
            {
                ++id;
                foreach (var target in targetGroup)
                {
                    if (id == 0)
                    {
                        target.SetTarget.ConvertToNop();
                    }
                    else
                    {
                        target.SetTarget.Operand = id;
                    }
                }
            }

            // now work from the innermost to the outermost.
            var finallyBlocks = finallyStack.Where(f => f.HasFinally)
                                .OrderByDescending(p => p.Depth)
                                .ToList();

            foreach (var finallyBlock in finallyBlocks)
            {
                targets = TryCatchGroupTargets(finallyBlock.Targets);

                // reset 'leave' instruction that do not leave this block.
                foreach (var targetGroup in targets.Where(t => t.Key.IsLeave).ToList())
                {
                    bool staysInBlock = targetGroup.Key.Destination.Index >= finallyBlock.FirstInstruction.Index &&
                                        targetGroup.Key.Destination.Index <= finallyBlock.LastInstruction.Index;

                    if (staysInBlock)
                    {
                        foreach (var source in targetGroup)
                        {
                            source.GotoFinally.Operand = source.Destination;
                            source.SetTarget.ConvertToNop();
                        }
                        targets.Remove(targetGroup);
                    }
                }

                if (targets.Count == 0)
                {
                    continue;
                }

                bool needTargetRegister = targets.Count > 1;
                TryCatchSetTargetRegister(finallyBlock, needTargetRegister);

                var outerFinallyBlock = finallyBlock.OuterFinallyBlock;
                int insIdx            = finallyBlock.AfterExceptionCheck.Index + 1;

                if (targets.Count == 1)
                {
                    // primitive case
                    var target = targets[0];
                    TryCatchEmitTargetInstruction(target.Key, ref insIdx, outerFinallyBlock);
                }
                else if (targets.Count == 2)
                {
                    // use a single comparison
                    var target = targets[0].Key;
                    id = target.SetTarget.Code == RCode.Nop ? 0 : (int)target.SetTarget.Operand;

                    Instruction compare;
                    if (id == 0)
                    {
                        compare = new Instruction(RCode.If_nez, finallyBlock.TargetRegister);
                    }
                    else
                    {
                        var comparisonRegister = frame.AllocateTemp(PrimitiveType.Int);
                        var iConst             = new Instruction(RCode.Const, id, comparisonRegister.Registers.ToArray());
                        instructions.Insert(insIdx++, iConst);
                        compare = new Instruction(RCode.If_ne, finallyBlock.TargetRegister, comparisonRegister);
                    }
                    instructions.Insert(insIdx++, compare);
                    TryCatchEmitTargetInstruction(target, ref insIdx, outerFinallyBlock);

                    target = targets[1].Key;
                    var def = TryCatchEmitTargetInstruction(target, ref insIdx, outerFinallyBlock);
                    compare.Operand = def;
                }
                else
                {
                    // use a sparse-switch
                    List <Tuple <int, Instruction> > sparseSwitchData = new List <Tuple <int, Instruction> >();
                    var ps = new Instruction(RCode.Sparse_switch, finallyBlock.TargetRegister);
                    instructions.Insert(insIdx++, ps);

                    // emit default.
                    TryCatchEmitTargetInstruction(targets.Last().Key, ref insIdx, outerFinallyBlock);

                    foreach (var targetGrouping in targets.Take(targets.Count - 1))
                    {
                        var target = targetGrouping.Key;
                        id = target.SetTarget.Code == RCode.Nop ? 0 : (int)target.SetTarget.Operand;

                        var first = TryCatchEmitTargetInstruction(target, ref insIdx, outerFinallyBlock);
                        sparseSwitchData.Add(Tuple.Create(id, first));
                    }
                    ps.Operand = sparseSwitchData.ToArray();
                }
            }
        }
        /// <summary>
        /// Create try/catch/finally/fault block
        /// </summary>
        public override RLRange Visit(AstTryCatchBlock node, AstNode parent)
        {
            var handler = new ExceptionHandler();

            //if (node.FaultBlock != null)
            //{
            //    Debugger.Break();
            //}

            // Setup instruction before/after my node.
            var first = new Instruction()
            {
                SequencePoint = node.SourceLocation
            };
            var last = new Instruction(RCode.Nop);

            FinallyBlockState finState      = null;
            FinallyBlockState outerFinState = tryCatchStack.FirstOrDefault(f => f.HasFinally);

            if (tryCatchStack.Count == 0)
            {
                finallyState.FinallyStacks.Add(new List <FinallyBlockState>());
            }

            if (node.FinallyBlock != null)
            {
                // store finaly state
                finState = new FinallyBlockState(outerFinState, tryCatchStack.Count, this, first, last);

                finState.FinallyExceptionRegister = frame.AllocateTemp(new ClassReference("java.lang.Throwable")).Register;
                // clear the variable to make sure it isn't stale from a previous loop.
                // make sure this is outside the try block for edge case exceptions.
                this.Add(node.SourceLocation, RCode.Const, 0, finState.FinallyExceptionRegister);

                tryCatchStack.Push(finState);
                finallyState.FinallyStacks.Last().Add(finState);
            }
            else
            {
                finState = new FinallyBlockState(outerFinState, tryCatchStack.Count);
                tryCatchStack.Push(finState);
            }

            instructions.Add(first);
            // Emit try block
            handler.TryStart = first;
            node.TryBlock.AcceptOrDefault(this, node);
            handler.TryEnd = TryCatchGotoEnd(finState, last);

            var catchesStart = this.Add(AstNode.NoSource, RCode.Nop);

            // Emit "normal" catch blocks
            foreach (var catchBlock in node.CatchBlocks.Where(x => !x.IsCatchAll()))
            {
                var c = new Catch {
                    Type = catchBlock.ExceptionType.GetReference(targetPackage)
                };
                handler.Catches.Add(c);
                var catchStart = this.Add(catchBlock.SourceLocation, RCode.Nop);
                catchBlock.Accept(this, node);
                c.Instruction = catchStart;

                TryCatchGotoEnd(finState, last);
            }

            // Emit "catch all" (if any)
            var catchAllBlock = node.CatchBlocks.SingleOrDefault(x => x.IsCatchAll());

            if (catchAllBlock != null)
            {
                var catchStart = this.Add(catchAllBlock.SourceLocation, RCode.Nop);
                catchAllBlock.Accept(this, node);
                handler.CatchAll = catchStart;

                TryCatchGotoEnd(finState, last);
            }

            var catchesEnd = this.Add(AstNode.NoSource, RCode.Nop);

            // clear try/catch/finally stack: we don't want to cover ourselves!
            tryCatchStack.Pop();

            // Emit finally code
            if (node.FinallyBlock != null)
            {
                // preparation.
                var finallyStart = this.Add(node.FinallyBlock.SourceLocation, RCode.Move_exception,
                                            finState.FinallyExceptionRegister);
                instructions.Add(finState.NonException);

                // the original handler
                node.FinallyBlock.Accept(this, node);

                // prepare the routing
                this.Add(AstNode.NoSource, RCode.If_eqz, finState.AfterExceptionCheck, finState.FinallyExceptionRegister);
                this.Add(AstNode.NoSource, RCode.Throw, finState.FinallyExceptionRegister);
                instructions.Add(finState.AfterExceptionCheck);

                // Set up exception handlers.
                if (catchAllBlock == null)
                {
                    // we need to cover the try block.
                    handler.CatchAll = finallyStart;
                }

                if (node.CatchBlocks.Any())
                {
                    // we need to cover the catch blocks
                    var finallyHandler = new ExceptionHandler
                    {
                        TryStart = catchesStart,
                        TryEnd   = catchesEnd,
                        CatchAll = finallyStart
                    };
                    body.Exceptions.Add(finallyHandler);
                }
            }

            // Add end
            instructions.Add(last);

            // Record catch/catch-all handler
            if ((handler.CatchAll != null) || handler.Catches.Any())
            {
                body.Exceptions.Add(handler);
            }

            return(new RLRange(first, last, null));
        }
Exemple #15
0
 /// <summary>
 /// Gets the lowest size (in bitsX) that is available for the given register in the given instruction.
 /// </summary>
 private static RegisterFlags GetLowestSize(Instruction instruction, Register r)
 {
     var result = RegisterFlags.Bits16;
     var info = OpCodeInfo.Get(instruction.Code.ToDex());
     var registers = instruction.Registers;
     for (var i = 0; i < registers.Count; i++)
     {
         if (registers[i] == r)
         {
             var size = info.GetUsage(i) & RegisterFlags.SizeMask;
             if (size < result) result = size;
         }
     }
     return result;
 }
 public FinallyBlockState(FinallyBlockState outerBlock, int depth, AstCompilerVisitor compiler, Instruction first, Instruction last)
 {
     _compiler = compiler;
     NonException = new Instruction();
     AfterExceptionCheck = new Instruction();
     FirstInstruction = first;
     LastInstruction = last;
     Depth = depth;
     OuterFinallyBlock = outerBlock;
 }
        private void FixFinallyStack(List<FinallyBlockState> finallyStack)
        {
            // Find all identical targets in this stack.
            // To minimize assigments, order by number of targets. To simplify the routing 
            // code when possible, prefer IsFallOut over IsLeave over IsReturn
            var targets = TryCatchGroupTargets(finallyStack.SelectMany(f => f.Targets));

            if (targets.Count == 0)
                return;

            // assign target group ids. zero is the default, and does not need to be set.
            int id = -1;
            foreach (var targetGroup in targets)
            {
                ++id;
                foreach (var target in targetGroup)
                {
                    if (id == 0) target.SetTarget.ConvertToNop();
                    else         target.SetTarget.Operand = id;
                }
            }

            // now work from the innermost to the outermost.
            var finallyBlocks = finallyStack.Where(f=>f.HasFinally)
                                            .OrderByDescending(p=>p.Depth)
                                            .ToList();
            foreach(var finallyBlock in finallyBlocks)
            {
                targets = TryCatchGroupTargets(finallyBlock.Targets);

                // reset 'leave' instruction that do not leave this block.
                foreach (var targetGroup in targets.Where(t => t.Key.IsLeave).ToList())
                {
                    bool staysInBlock = targetGroup.Key.Destination.Index >= finallyBlock.FirstInstruction.Index 
                                    &&  targetGroup.Key.Destination.Index <= finallyBlock.LastInstruction.Index;

                    if(staysInBlock)
                    {
                        foreach (var source in targetGroup)
                        {
                            source.GotoFinally.Operand = source.Destination;
                            source.SetTarget.ConvertToNop();
                        }
                        targets.Remove(targetGroup);
                    }
                }

                if (targets.Count == 0)
                    continue;

                bool needTargetRegister = targets.Count > 1;
                TryCatchSetTargetRegister(finallyBlock, needTargetRegister);

                var outerFinallyBlock = finallyBlock.OuterFinallyBlock;
                int insIdx = finallyBlock.AfterExceptionCheck.Index + 1;

                if (targets.Count == 1)
                {
                    // primitive case
                    var target = targets[0];
                    TryCatchEmitTargetInstruction(target.Key, ref insIdx, outerFinallyBlock);
                }
                else if (targets.Count == 2)
                {
                    // use a single comparison
                    var target = targets[0].Key;
                    id = target.SetTarget.Code == RCode.Nop ? 0 : (int)target.SetTarget.Operand;

                    Instruction compare;
                    if (id == 0)
                    {
                        compare = new Instruction(RCode.If_nez, finallyBlock.TargetRegister);
                    }
                    else
                    {
                        var comparisonRegister = frame.AllocateTemp(PrimitiveType.Int);
                        var iConst = new Instruction(RCode.Const, id, comparisonRegister.Registers.ToArray());
                        instructions.Insert(insIdx++, iConst);
                        compare = new Instruction(RCode.If_ne, finallyBlock.TargetRegister, comparisonRegister);
                    }
                    instructions.Insert(insIdx++, compare);
                    TryCatchEmitTargetInstruction(target, ref insIdx, outerFinallyBlock);

                    target = targets[1].Key;
                    var def = TryCatchEmitTargetInstruction(target, ref insIdx, outerFinallyBlock);
                    compare.Operand = def;
                }
                else
                {
                    // use a sparse-switch
                    List<Tuple<int,Instruction>> sparseSwitchData = new List<Tuple<int, Instruction>>();
                    var ps = new Instruction(RCode.Sparse_switch, finallyBlock.TargetRegister);
                    instructions.Insert(insIdx++, ps);

                    // emit default.
                    TryCatchEmitTargetInstruction(targets.Last().Key, ref insIdx, outerFinallyBlock);

                    foreach (var targetGrouping in targets.Take(targets.Count - 1))
                    {
                        var target = targetGrouping.Key;
                        id = target.SetTarget.Code == RCode.Nop ? 0 : (int)target.SetTarget.Operand;
                        
                        var first = TryCatchEmitTargetInstruction(target, ref insIdx, outerFinallyBlock);
                        sparseSwitchData.Add(Tuple.Create(id, first));
                    }
                    ps.Operand = sparseSwitchData.ToArray();
                }
            }
        }
        /// <summary>
        /// Create the body of the equals method.
        /// </summary>
        private static MethodBody CreateEqualsBody(ISourceLocation sequencePoint, AssemblyCompiler compiler, DexTargetPackage targetPackage, XMethodDefinition equalsMethod, Prototype equalsPrototype, FieldDefinition instanceField, ClassReference delegateClass)
        {
            MethodBody body = new MethodBody(null);

            // This pointer and method argument.
            Register rthis = body.AllocateRegister(RCategory.Argument, RType.Object);
            Register rother = body.AllocateRegister(RCategory.Argument, RType.Object);

            // Create code.
            var ins = body.Instructions;

            // Temporary parameter result.
            Register result = body.AllocateRegister(RCategory.Temp, RType.Value);

            // Prepare the return instruction.
            Instruction returnInstruction = new Instruction(RCode.Return, result);

            // Check if other object can be casted.
            ins.Add(new Instruction(RCode.Instance_of, delegateClass, new[] { result, rother }));
            ins.Add(new Instruction(RCode.If_eqz, returnInstruction, new[] { result })); // compare instance members

            // Cast of the other object.
            ins.Add(new Instruction(RCode.Check_cast, delegateClass, new[] { rother }));

            // Get instance fields of this and other.
            var thisInstance = body.AllocateRegister(RCategory.Temp, RType.Object);
            var otherInstance = body.AllocateRegister(RCategory.Temp, RType.Object);

            // Load the instance fields.
            ins.Add(new Instruction(RCode.Iget_object, thisInstance, rthis) { Operand = instanceField });
            ins.Add(new Instruction(RCode.Iget_object, otherInstance, rother) { Operand = instanceField });

            // Compare the instance fields.
            ins.Add(new Instruction(RCode.If_eq, returnInstruction, new[] { thisInstance, otherInstance })); // compare instance members

            // Set result to false if not equal.
            ins.Add(new Instruction(RCode.Const, 0, new[] { result }));

            // Add return instructions
            ins.Add(returnInstruction);

            return body;
        }
            private RLRange BranchToFinally(FinallyTarget target, IEnumerable<RLRange> prefix, ISourceLocation seqp, ref int insIdx)
            {
                Debug.Assert(this.HasFinally);

                Targets.Add(target);

                var setTarget = new Instruction(RCode.Const) { SequencePoint = seqp }; // operand and register are set later
                _compiler.instructions.Insert(insIdx++, setTarget); 
                var branch = new Instruction(RCode.Goto, NonException) {SequencePoint = seqp};
                _compiler.instructions.Insert(insIdx++, branch);

                target.SetTarget = setTarget;
                target.GotoFinally = branch;
                target.State = this;

                return new RLRange(prefix, setTarget, branch, null);
            }
        /// <summary>
        /// Create the body of the invoke method.
        /// </summary>
        private static MethodBody CreateInvokeBody(ISourceLocation sequencePoint, AssemblyCompiler compiler, DexTargetPackage targetPackage, XMethodDefinition calledMethod, XMethodDefinition invokeMethod, Prototype invokePrototype, Prototype calledMethodPrototype, FieldDefinition instanceField, ClassReference delegateClass)
        {
            var body  = new MethodBody(null);
            var rthis = body.AllocateRegister(RCategory.Argument, RType.Object);

            foreach (var p in invokePrototype.Parameters)
            {
                if (p.Type.IsWide())
                {
                    body.AllocateWideRegister(RCategory.Argument);
                }
                else
                {
                    var type = (p.Type is PrimitiveType) ? RType.Value : RType.Object;
                    body.AllocateRegister(RCategory.Argument, type);
                }
            }
            var incomingMethodArgs = body.Registers.ToArray();

            // Create code
            var      ins      = body.Instructions;
            Register instance = null;

            if (!calledMethod.IsStatic)
            {
                // load instance
                instance = body.AllocateRegister(RCategory.Temp, RType.Object);
                ins.Add(new Instruction(RCode.Iget_object, instance, rthis)
                {
                    Operand = instanceField
                });
            }
            // Invoke
            var calledMethodRef = calledMethod.GetReference(targetPackage);
            var inputArgs       = calledMethod.IsStatic ? incomingMethodArgs.Skip(1).ToArray() : incomingMethodArgs;

            // Cast arguments (if needed)
            var outputArgs = new List <Register>();

            if (!calledMethod.IsStatic)
            {
                outputArgs.Add(instance);
            }
            var parameterIndex = 0;

            for (var i = calledMethod.IsStatic ? 0 : 1; i < inputArgs.Length;)
            {
                var invokeType  = invokePrototype.Parameters[parameterIndex].Type;
                var inputIsWide = invokeType.IsWide();
                var calledType  = calledMethodPrototype.Parameters[parameterIndex].Type;
                if (!invokeType.Equals(calledType))
                {
                    // Add cast / unbox
                    var source = inputIsWide
                                     ? new RegisterSpec(inputArgs[i], inputArgs[i + 1], invokeType)
                                     : new RegisterSpec(inputArgs[i], null, invokeType);
                    var tmp = ins.Unbox(sequencePoint, source, calledMethod.Parameters[parameterIndex].ParameterType, compiler, targetPackage, body);
                    outputArgs.Add(tmp.Result.Register);
                    if (calledType.IsWide())
                    {
                        outputArgs.Add(tmp.Result.Register2);
                    }
                }
                else
                {
                    outputArgs.Add(inputArgs[i]);
                    if (calledType.IsWide())
                    {
                        outputArgs.Add(inputArgs[i + 1]);
                    }
                }
                i += inputIsWide ? 2 : 1;
                parameterIndex++;
            }

            // Actual call
            ins.Add(new Instruction(calledMethod.Invoke(calledMethod, null), calledMethodRef, outputArgs.ToArray()));

            // Collect return value
            var         invokeReturnType = invokePrototype.ReturnType;
            var         calledReturnType = calledMethodPrototype.ReturnType;
            var         needsBoxing      = !invokeReturnType.Equals(calledReturnType);
            Instruction returnInstruction;

            if (calledReturnType.IsWide())
            {
                var r = body.AllocateWideRegister(RCategory.Temp);
                ins.Add(new Instruction(RCode.Move_result_wide, r.Item1));
                if (needsBoxing)
                {
                    // Box
                    var source = new RegisterSpec(r.Item1, r.Item2, calledReturnType);
                    var tmp    = ins.Box(sequencePoint, source, calledMethod.ReturnType, targetPackage, body);
                    returnInstruction = new Instruction(RCode.Return_object, tmp.Result.Register);
                }
                else
                {
                    // Return wide
                    returnInstruction = new Instruction(RCode.Return_wide, r.Item1);
                }
            }
            else if (calledMethod.ReturnType.IsVoid())
            {
                // Void return
                returnInstruction = new Instruction(RCode.Return_void);
            }
            else if (calledReturnType is PrimitiveType)
            {
                // Single register return
                var r = body.AllocateRegister(RCategory.Temp, RType.Value);
                ins.Add(new Instruction(RCode.Move_result, r));
                if (needsBoxing)
                {
                    // Box
                    var source = new RegisterSpec(r, null, invokeReturnType);
                    var tmp    = ins.Box(sequencePoint, source, calledMethod.ReturnType, targetPackage, body);
                    returnInstruction = new Instruction(RCode.Return_object, tmp.Result.Register);
                }
                else
                {
                    // Return
                    returnInstruction = new Instruction(RCode.Return, r);
                }
            }
            else
            {
                var r = body.AllocateRegister(RCategory.Temp, RType.Object);
                ins.Add(new Instruction(RCode.Move_result_object, r));
                if (needsBoxing)
                {
                    // Box
                    var source = new RegisterSpec(r, null, invokeReturnType);
                    var tmp    = ins.Box(sequencePoint, source, invokeMethod.ReturnType, targetPackage, body);
                    returnInstruction = new Instruction(RCode.Return_object, tmp.Result.Register);
                }
                else
                {
                    // Return
                    returnInstruction = new Instruction(RCode.Return_object, r);
                }
            }

            // Call next delegate (if any)
            var next = body.AllocateRegister(RCategory.Temp, RType.Object);
            var multicastDelegateType = new ClassReference(targetPackage.NameConverter.GetConvertedFullName("System.MulticastDelegate"));
            var nextReference         = new FieldReference(multicastDelegateType, "next", multicastDelegateType);

            ins.Add(new Instruction(RCode.Iget_object, nextReference, new[] { next, rthis })); // load this.next
            var afterCallNext = new Instruction(RCode.Nop);

            ins.Add(new Instruction(RCode.If_eqz, afterCallNext, new[] { next })); // if next == null, continue
            ins.Add(new Instruction(RCode.Check_cast, delegateClass, new[] { next }));
            var nextInvokeMethod = new MethodReference(delegateClass, "Invoke", invokePrototype);
            var nextInvokeArgs = new[] { next }.Concat(incomingMethodArgs.Skip(1)).ToArray();

            ins.Add(new Instruction(RCode.Invoke_virtual, nextInvokeMethod, nextInvokeArgs));
            ins.Add(afterCallNext);

            // Add return instructions
            ins.Add(returnInstruction);

            return(body);
        }
        /// <summary>
        /// Generate code for the given expression.
        /// </summary>
        private RLRange VisitExpression(AstExpression node, List<RLRange> args, AstNode parent)
        {

            switch (node.Code)
            {
                case AstCode.Nop:
                case AstCode.Endfinally:
                case AstCode.Endfilter:
                    return new RLRange(this.Add(node.SourceLocation, RCode.Nop), null);
                case AstCode.Ldexception:
                    throw new InvalidOperationException("ldexception should not occur");
                case AstCode.Dup:
                    return new RLRange(this.Add(node.SourceLocation, RCode.Nop), args[0].Result);

                    #region Constants

                case AstCode.Ldc_I4:
                case AstCode.Ldc_R4:
                    {
                        var type = node.GetResultType();
                        var converter = type.ConstValueConverter(true);
                        var value = converter(node.Operand);
                        var r = frame.AllocateTemp(type.IsFloat() ? PrimitiveType.Float : PrimitiveType.Int);
                        var first = this.Add(node.SourceLocation, RCode.Const, value, r);
                        return new RLRange(first, r);
                    }
                case AstCode.Ldc_I8:
                case AstCode.Ldc_R8:
                    {
                        var type = node.GetResultType();
                        var converter = type.ConstValueConverter(false);
                        var value = converter(node.Operand);
                        var r = frame.AllocateTemp(type.IsDouble() ? PrimitiveType.Double : PrimitiveType.Long);
                        return new RLRange(args, this.Add(node.SourceLocation, RCode.Const_wide, value, r), r);
                    }
                case AstCode.Ldnull:
                    {
                        //Debugger.Launch();
                        var r = frame.AllocateTemp(node.GetResultType().GetReference(targetPackage));
                        return new RLRange(args, this.Add(node.SourceLocation, RCode.Const, 0, r), r);
                    }
                case AstCode.Ldstr:
                    {
                        var str = (string) node.Operand;
                        var r = frame.AllocateTemp(node.GetResultType().GetReference(targetPackage));
                        return new RLRange(args, this.Add(node.SourceLocation, RCode.Const_string, str, r), r);
                    }
                case AstCode.DefaultValue:
                    {
                        var type = (XTypeReference) node.Operand;
                        if (type.IsPrimitive)
                        {
                            var r = frame.AllocateTemp(type.GetReference(targetPackage));
                            return new RLRange(args, this.Add(node.SourceLocation, node.Arguments[0].Const(), 0, r), r);
                        }
                        if (type.IsEnum())
                        {
                            var r = frame.AllocateTemp(type.GetReference(targetPackage));
                            var denumType = type.GetClassReference(targetPackage);
                            var defaultField = new FieldReference(denumType, NameConstants.Enum.DefaultFieldName, denumType);
                            return new RLRange(this.Add(node.SourceLocation, RCode.Sget_object, defaultField, r), r);
                        }
                        else
                        {
                            var r = frame.AllocateTemp(type.GetReference(targetPackage));
                            return new RLRange(args, this.Add(node.SourceLocation, RCode.Const, 0, r), r);
                        }
                    }
                case AstCode.TypeOf:
                    {
                        var type = (XTypeReference) node.Operand;
                        var dtype = type.IsVoid() ? PrimitiveType.Void : type.GetReference(targetPackage);
                        var typeReg = frame.AllocateTemp(FrameworkReferences.Class);
                        var first = this.Add(node.SourceLocation, RCode.Const_class, dtype, typeReg);
                        return new RLRange(first, typeReg);
                    }
                case AstCode.BoxedTypeOf:
                    {
                        var type = (XTypeReference) node.Operand;
                        var typeReg = frame.AllocateTemp(FrameworkReferences.Class);
                        var first = this.Add(node.SourceLocation, RCode.Const_class, type.GetBoxedType(), typeReg);
                        return new RLRange(first, typeReg);
                    }

                    #endregion

                    #region Arithmetic

                case AstCode.Neg:
                    {
                        var tmp = this.EnsureTemp(node.SourceLocation, args[0].Result, frame);
                        return new RLRange(args, this.Add(node.SourceLocation, node.Neg(), tmp.Result, tmp.Result),
                                           tmp.Result);
                    }
                case AstCode.Not:
                    {
                        var tmp = this.EnsureTemp(node.SourceLocation, args[0].Result, frame);
                        return new RLRange(args, this.Add(node.SourceLocation, node.Not(), tmp.Result, tmp.Result),
                                           tmp.Result);
                    }
                case AstCode.Add:
                    {
                        if (args[0].Result.Register.IsTemp)
                            return new RLRange(args,
                                               this.Add(node.SourceLocation, node.Add2Addr(), args[0].Result,
                                                        args[1].Result), args[0].Result);
                        var tmp = frame.AllocateTemp(args[0].Result.Type);
                        return new RLRange(args,
                                           this.Add(node.SourceLocation, node.Add(), tmp, args[0].Result, args[1].Result),
                                           tmp);
                    }
                case AstCode.Add_Ovf:
                    {
                        var isLong = node.Arguments[0].IsInt64();
                        var addMethods =
                            compiler.GetDot42InternalType("Checked").Resolve().Methods.Where(x => x.Name == "Add");
                        var ilMethod = addMethods.First(x => isLong ? x.ReturnType.IsInt64() : x.ReturnType.IsInt32());
                        var method = ilMethod.GetReference(targetPackage);
                        var tmp = this.EnsureTemp(node.SourceLocation, args[0].Result, frame);
                        var registers = tmp.Result.Registers.Concat(args[1].Result.Registers);
                        this.Add(node.SourceLocation, RCode.Invoke_static, method, registers);
                        return new RLRange(args,
                                           this.Add(node.SourceLocation,
                                                    isLong ? RCode.Move_result_wide : RCode.Move_result, tmp.Result),
                                           tmp.Result);
                    }
                case AstCode.CompoundAdd:
                    {
                        var localReg = frame.GetArgument((AstVariable) node.Operand);
                        return new RLRange(args,
                                           this.Add(node.SourceLocation, node.Add2Addr(), localReg, args[0].Result),
                                           localReg);
                    }
                case AstCode.Sub:
                    {
                        if (args[0].Result.Register.IsTemp)
                            return new RLRange(args,
                                               this.Add(node.SourceLocation, node.Sub2Addr(), args[0].Result,
                                                        args[1].Result), args[0].Result);
                        var tmp = frame.AllocateTemp(args[0].Result.Type);
                        return new RLRange(args,
                                           this.Add(node.SourceLocation, node.Sub(), tmp, args[0].Result, args[1].Result),
                                           tmp);
                    }
                case AstCode.Sub_Ovf:
                    {
                        var isLong = node.Arguments[0].IsInt64();
                        var addMethods =
                            compiler.GetDot42InternalType("Checked").Resolve().Methods.Where(x => x.Name == "Sub");
                        var ilMethod = addMethods.First(x => isLong ? x.ReturnType.IsInt64() : x.ReturnType.IsInt32());
                        var method = ilMethod.GetReference(targetPackage);
                        var tmp = this.EnsureTemp(node.SourceLocation, args[0].Result, frame);
                        var registers = tmp.Result.Registers.Concat(args[1].Result.Registers);
                        this.Add(node.SourceLocation, RCode.Invoke_static, method, registers);
                        return new RLRange(args,
                                           this.Add(node.SourceLocation,
                                                    isLong ? RCode.Move_result_wide : RCode.Move_result, tmp.Result),
                                           tmp.Result);
                    }
                case AstCode.CompoundSub:
                    {
                        var localReg = frame.GetArgument((AstVariable) node.Operand);
                        return new RLRange(args,
                                           this.Add(node.SourceLocation, node.Sub2Addr(), localReg, args[0].Result),
                                           localReg);
                    }
                case AstCode.Mul:
                    {
                        if (args[0].Result.Register.IsTemp)
                            return new RLRange(args,
                                               this.Add(node.SourceLocation, node.Mul2Addr(), args[0].Result,
                                                        args[1].Result), args[0].Result);
                        var tmp = frame.AllocateTemp(args[0].Result.Type);
                        return new RLRange(args,
                                           this.Add(node.SourceLocation, node.Mul(), tmp, args[0].Result, args[1].Result),
                                           tmp);
                    }
                case AstCode.Mul_Ovf:
                    {
                        var isLong = node.Arguments[0].IsInt64();
                        var addMethods =
                            compiler.GetDot42InternalType("Checked").Resolve().Methods.Where(x => x.Name == "Mul");
                        var ilMethod = addMethods.First(x => isLong ? x.ReturnType.IsInt64() : x.ReturnType.IsInt32());
                        var method = ilMethod.GetReference(targetPackage);
                        var tmp = this.EnsureTemp(node.SourceLocation, args[0].Result, frame);
                        var registers = tmp.Result.Registers.Concat(args[1].Result.Registers);
                        this.Add(node.SourceLocation, RCode.Invoke_static, method, registers);
                        return new RLRange(args,
                                           this.Add(node.SourceLocation,
                                                    isLong ? RCode.Move_result_wide : RCode.Move_result, tmp.Result),
                                           tmp.Result);
                    }
                case AstCode.CompoundMul:
                    {
                        var localReg = frame.GetArgument((AstVariable) node.Operand);
                        return new RLRange(args,
                                           this.Add(node.SourceLocation, node.Mul2Addr(), localReg, args[0].Result),
                                           localReg);
                    }
                case AstCode.Div:
                case AstCode.Div_Un:
                    {
                        if (args[0].Result.Register.IsTemp)
                            return new RLRange(args,
                                               this.Add(node.SourceLocation, node.Div2Addr(), args[0].Result,
                                                        args[1].Result), args[0].Result);
                        var tmp = frame.AllocateTemp(args[0].Result.Type);
                        return new RLRange(args,
                                           this.Add(node.SourceLocation, node.Div(), tmp, args[0].Result, args[1].Result),
                                           tmp);
                    }
                case AstCode.CompoundDiv:
                    {
                        var localReg = frame.GetArgument((AstVariable) node.Operand);
                        return new RLRange(args,
                                           this.Add(node.SourceLocation, node.Div2Addr(), localReg, args[0].Result),
                                           localReg);
                    }
                case AstCode.Rem:
                case AstCode.Rem_Un:
                    {
                        if (args[0].Result.Register.IsTemp)
                            return new RLRange(args,
                                               this.Add(node.SourceLocation, node.Rem2Addr(), args[0].Result,
                                                        args[1].Result), args[0].Result);
                        var tmp = frame.AllocateTemp(args[0].Result.Type);
                        return new RLRange(args,
                                           this.Add(node.SourceLocation, node.Rem(), tmp, args[0].Result, args[1].Result),
                                           tmp);
                    }
                case AstCode.CompoundRem:
                    {
                        var localReg = frame.GetArgument((AstVariable) node.Operand);
                        return new RLRange(args,
                                           this.Add(node.SourceLocation, node.Rem2Addr(), localReg, args[0].Result),
                                           localReg);
                    }
                case AstCode.And:
                    {
                        if (args[0].Result.Register.IsTemp)
                            return new RLRange(args,
                                               this.Add(node.SourceLocation, node.And2Addr(), args[0].Result,
                                                        args[1].Result), args[0].Result);
                        var tmp = frame.AllocateTemp(args[0].Result.Type);
                        return new RLRange(args,
                                           this.Add(node.SourceLocation, node.And(), tmp, args[0].Result, args[1].Result),
                                           tmp);
                    }
                case AstCode.CompoundAnd:
                    {
                        var localReg = frame.GetArgument((AstVariable) node.Operand);
                        return new RLRange(args,
                                           this.Add(node.SourceLocation, node.And2Addr(), localReg, args[0].Result),
                                           localReg);
                    }
                case AstCode.Or:
                    {
                        if (args[0].Result.Register.IsTemp)
                            return new RLRange(args,
                                               this.Add(node.SourceLocation, node.Or2Addr(), args[0].Result,
                                                        args[1].Result), args[0].Result);
                        var tmp = frame.AllocateTemp(args[0].Result.Type);
                        return new RLRange(args,
                                           this.Add(node.SourceLocation, node.Or(), tmp, args[0].Result, args[1].Result),
                                           tmp);
                    }
                case AstCode.CompoundOr:
                    {
                        var localReg = frame.GetArgument((AstVariable) node.Operand);
                        return new RLRange(args, this.Add(node.SourceLocation, node.Or2Addr(), localReg, args[0].Result),
                                           localReg);
                    }
                case AstCode.Xor:
                    {
                        if (args[0].Result.Register.IsTemp)
                            return new RLRange(args,
                                               this.Add(node.SourceLocation, node.Xor2Addr(), args[0].Result,
                                                        args[1].Result), args[0].Result);
                        var tmp = frame.AllocateTemp(args[0].Result.Type);
                        return new RLRange(args,
                                           this.Add(node.SourceLocation, node.Xor(), tmp, args[0].Result, args[1].Result),
                                           tmp);
                    }
                case AstCode.CompoundXor:
                    {
                        var localReg = frame.GetArgument((AstVariable) node.Operand);
                        return new RLRange(args,
                                           this.Add(node.SourceLocation, node.Xor2Addr(), localReg, args[0].Result),
                                           localReg);
                    }
                case AstCode.Shl:
                    {
                        var r = frame.AllocateTemp(args[0].Result.Type);
                        return new RLRange(args,
                                           this.Add(node.SourceLocation, node.Shl(), r, args[0].Result, args[1].Result),
                                           r);
                    }
                case AstCode.CompoundShl:
                    {
                        var localReg = frame.GetArgument((AstVariable) node.Operand);
                        return new RLRange(args,
                                           this.Add(node.SourceLocation, node.Shl2Addr(), localReg, args[0].Result),
                                           localReg);
                    }
                case AstCode.Shr:
                    {
                        var r = frame.AllocateTemp(args[0].Result.Type);
                        return new RLRange(args,
                                           this.Add(node.SourceLocation, node.Shr(), r, args[0].Result, args[1].Result),
                                           r);
                    }
                case AstCode.CompoundShr:
                    {
                        var localReg = frame.GetArgument((AstVariable) node.Operand);
                        return new RLRange(args,
                                           this.Add(node.SourceLocation, node.Shr2Addr(), localReg, args[0].Result),
                                           localReg);
                    }
                case AstCode.Shr_Un:
                    {
                        var r = frame.AllocateTemp(args[0].Result.Type);
                        return new RLRange(args,
                                           this.Add(node.SourceLocation, node.UShr(), r, args[0].Result, args[1].Result),
                                           r);
                    }
                case AstCode.CompoundShr_Un:
                    {
                        var localReg = frame.GetArgument((AstVariable) node.Operand);
                        return new RLRange(args,
                                           this.Add(node.SourceLocation, node.UShr2Addr(), localReg, args[0].Result),
                                           localReg);
                    }
                case AstCode.Conditional: // arg[0] ? arg[1] : arg[2]
                    {
                        var valueType = (XTypeReference) node.Operand;
                        var result = frame.AllocateTemp(valueType.GetReference(targetPackage));
                        var move = node.Arguments[1].Move();
                        var move2 = node.Arguments[2].Move();
                        if (move2 == RCode.Move_object) move = move2;

                        // condition
                        var gotoArg2 = this.Add(node.SourceLocation, RCode.If_eqz, null, args[0].Result.Registers);

                        // Generate code for arg[1]
                        var arg1 = node.Arguments[1].Accept(this, node);
                        this.Add(node.SourceLocation, move, result, arg1.Result);
                        var gotoEnd = this.Add(node.SourceLocation, RCode.Goto, null);

                        // Generate code for arg[2]
                        var arg2Start = this.Add(node.SourceLocation, RCode.Nop);
                        var arg2 = node.Arguments[2].Accept(this, node);
                        this.Add(node.SourceLocation, move, result, arg2.Result);

                        var end = this.Add(node.SourceLocation, RCode.Nop);
                        // Set branch targets
                        gotoArg2.Operand = arg2Start;
                        gotoEnd.Operand = end;

                        return new RLRange(gotoArg2, end, result);
                    }

                    #endregion

                    #region Conversion

                    /*case AstCode.Conv_U4:
                case AstCode.Conv_Ovf_U4:
                    {
                        return new RLRange(this.Add(node.SourceLocation, RCode.Nop), args[0].Result);
                    }*/
                case AstCode.Conv_I1:
                case AstCode.Conv_Ovf_I1:
                case AstCode.Conv_Ovf_I1_Un:
                    return ConvX(node.SourceLocation, RCode.Int_to_byte, PrimitiveType.Byte,
                                 ConvToInt(node.SourceLocation, args[0]));
                case AstCode.Conv_U1:
                case AstCode.Conv_Ovf_U1:
                case AstCode.Conv_Ovf_U1_Un:
                    {
                        var result = ConvX(node.SourceLocation, RCode.Int_to_byte, PrimitiveType.Byte,
                                           ConvToInt(node.SourceLocation, args[0]));
                        var last = this.Add(node.SourceLocation, RCode.And_int_lit, 0xFF, result.Result, result.Result);
                        return new RLRange(result.First, last, result.Result);
                    }
                case AstCode.Conv_I2:
                case AstCode.Conv_Ovf_I2:
                case AstCode.Conv_Ovf_I2_Un:
                    return ConvX(node.SourceLocation, RCode.Int_to_short, PrimitiveType.Short,
                                 ConvToInt(node.SourceLocation, args[0]));
                case AstCode.Conv_U2:
                case AstCode.Conv_Ovf_U2:
                case AstCode.Conv_Ovf_U2_Un:
                    {
                        if (node.GetResultType().IsUInt16())
                        {
                            var result = ConvX(node.SourceLocation, RCode.Int_to_short, PrimitiveType.Short,
                                               ConvToInt(node.SourceLocation, args[0]));
                            var r2 = frame.AllocateTemp(PrimitiveType.Int);
                            this.Add(node.SourceLocation, RCode.Const, 0xFFFF, r2);
                            var last = this.Add(node.SourceLocation, RCode.And_int_2addr, result.Result, r2);
                            return new RLRange(result.First, last, result.Result);
                        }
                        else
                        {
                            return ConvX(node.SourceLocation, RCode.Int_to_char, PrimitiveType.Char,
                                         ConvToInt(node.SourceLocation, args[0]));
                        }
                    }
                case AstCode.Conv_I4:
                case AstCode.Conv_Ovf_I4:
                case AstCode.Conv_Ovf_I4_Un:
                case AstCode.Conv_I: // Convert to native int
                case AstCode.Conv_Ovf_I: // Convert to native with overflow check
                case AstCode.Conv_Ovf_I_Un: // Convert to native without overflow check
                case AstCode.Conv_U: // Convert to native uint
                case AstCode.Conv_Ovf_U: // Convert to native uint with overflow check
                case AstCode.Conv_Ovf_U_Un: // Convert to native uint without overflow check
                case AstCode.Conv_U4:
                case AstCode.Conv_Ovf_U4:
                case AstCode.Conv_Ovf_U4_Un:
                    return ConvX(node.SourceLocation, node.Arguments[0].ConvI4(), PrimitiveType.Int, args[0]);
                case AstCode.Conv_I8:
                case AstCode.Conv_Ovf_I8:
                case AstCode.Conv_Ovf_I8_Un:
                case AstCode.Conv_U8:
                case AstCode.Conv_Ovf_U8:
                case AstCode.Conv_Ovf_U8_Un:
                    return ConvX(node.SourceLocation, node.Arguments[0].ConvI8(), PrimitiveType.Long, args[0]);
                case AstCode.Conv_R4:
                    return ConvX(node.SourceLocation, node.Arguments[0].ConvR4(), PrimitiveType.Float, args[0]);
                case AstCode.Conv_R8:
                case AstCode.Conv_R_Un:
                    return ConvX(node.SourceLocation, node.Arguments[0].ConvR8(), PrimitiveType.Double, args[0]);
                case AstCode.Int_to_ubyte:
                    {
                        var tmp = this.EnsureTemp(node.SourceLocation, args[0].Result, frame);
                        var last = this.Add(node.SourceLocation, RCode.And_int_lit, 0xFF, tmp.Result, tmp.Result);
                        return new RLRange(args, tmp.First, last, tmp.Result);
                    }
                case AstCode.Int_to_ushort:
                    {
                        var tmp = this.EnsureTemp(node.SourceLocation, args[0].Result, frame);
                        var r2 = frame.AllocateTemp(PrimitiveType.Int);
                        this.Add(node.SourceLocation, RCode.Const, 0xFFFF, r2);
                        var last = this.Add(node.SourceLocation, RCode.And_int_2addr, tmp.Result, r2);
                        return new RLRange(args, tmp.First, last, tmp.Result);
                    }
                case AstCode.Box:
                    {
                        var type = (XTypeReference) node.Operand;
                        var tmp = this.EnsureTemp(node.SourceLocation, args[0].Result, frame);
                        return this.Box(node.SourceLocation, tmp.Result, type, targetPackage, frame);
                    }
                case AstCode.Unbox:
                case AstCode.Unbox_Any:
                    {
                        var type = (XTypeReference) node.Operand;
                        var tmp = this.EnsureTemp(node.SourceLocation, args[0].Result, frame);
                        return this.Unbox(node.SourceLocation, tmp.Result, type, compiler, targetPackage, frame);
                    }
                case AstCode.AddressOf:
                    {
                        return args[0];
                    }
                case AstCode.Enum_to_int:
                case AstCode.Enum_to_long:
                    {
                        var enumType = node.Arguments[0].GetResultType().Resolve();
                        var isWide = enumType.GetEnumUnderlyingType().IsWide();
                        var internalEnumType = compiler.GetDot42InternalType("Enum").GetClassReference(targetPackage);
                        var methodName = isWide ? "LongValue" : "IntValue";
                        var enumNumericType = isWide ? PrimitiveType.Long : PrimitiveType.Int;
                        var rvalue = frame.AllocateTemp(enumNumericType);
                        var convMethodDex = new MethodReference(internalEnumType, methodName,
                                                                new Prototype(enumNumericType));
                        var call = this.Add(node.SourceLocation, RCode.Invoke_virtual, convMethodDex, args[0].Result);
                        var last = this.Add(node.SourceLocation, isWide ? RCode.Move_result_wide : RCode.Move_result,
                                            rvalue);
                        return new RLRange(call, last, rvalue);
                    }

                case AstCode.Int_to_enum:
                case AstCode.Long_to_enum:
                    {
                        var enumType = node.GetResultType().Resolve();
                        var denumType = enumType.GetClassReference(targetPackage);
                        var isWide = enumType.GetEnumUnderlyingType().IsWide();
                        var literalIsWide = node.Arguments[0].GetResultType().IsWide();
                        var internalEnumType = compiler.GetDot42InternalType("Enum").GetClassReference(targetPackage);
                        var internalEnumInfoType = compiler.GetDot42InternalType("EnumInfo").GetClassReference(targetPackage);
                        var enumNumericType = isWide ? PrimitiveType.Long : PrimitiveType.Int;
                        var getValueMethod = new MethodReference(internalEnumInfoType, "GetValue",
                                                                 new Prototype(internalEnumType,
                                                                               new Parameter(enumNumericType, "value")));
                        var rinfo = frame.AllocateTemp(internalEnumInfoType);
                        var infoField = new FieldReference(denumType, NameConstants.Enum.InfoFieldName,
                                                           internalEnumInfoType);
                        var getInfo = this.Add(node.SourceLocation, RCode.Sget_object, infoField, rinfo);
                        var valueR = args[0].Result;
                        if (isWide != literalIsWide)
                        {
                            var convValueR = frame.AllocateTemp(isWide ? PrimitiveType.Long : PrimitiveType.Int);
                            var convCode = isWide ? RCode.Int_to_long : RCode.Long_to_int;
                            this.Add(node.SourceLocation, convCode, convValueR, valueR);
                            valueR = convValueR;
                        }
                        this.Add(node.SourceLocation, RCode.Invoke_virtual, getValueMethod,
                                 rinfo.Registers.Concat(valueR.Registers));
                        var renum = frame.AllocateTemp(denumType);
                        this.Add(node.SourceLocation, RCode.Move_result_object, renum);
                        var castClass = this.Add(node.SourceLocation, RCode.Check_cast, denumType, renum);
                        return new RLRange(getInfo, castClass, renum);
                    }

                    #endregion

                    #region Branching

                case AstCode.Cle:
                case AstCode.Cle_Un:
                case AstCode.Clt:
                case AstCode.Clt_Un:
                case AstCode.Ceq:
                case AstCode.Cne:
                case AstCode.Cgt:
                case AstCode.Cgt_Un:
                case AstCode.Cge:
                case AstCode.Cge_Un:
                    {
                        var r = frame.AllocateTemp(PrimitiveType.Int);
                        var start = this.Add(node.SourceLocation, RCode.Const, 0, r);
                        Instruction test;
                        var narg0 = node.Arguments[0];
                        var narg1 = node.Arguments[1];
                        if (narg0.IsWide() || narg1.IsWide())
                        {
                            var r2 = frame.AllocateTemp(PrimitiveType.Int);
                            var code = narg0.IsDouble() ? RCode.Cmpg_double : RCode.Cmp_long;
                            this.Add(node.SourceLocation, code, r2, args[0].Result, args[1].Result);
                            test = this.Add(node.SourceLocation, node.Code.Reverse().ToIfTestZ(), r2);
                        }
                        else if (narg0.IsFloat() || narg1.IsFloat())
                        {
                            var r2 = frame.AllocateTemp(PrimitiveType.Int);
                            this.Add(node.SourceLocation, RCode.Cmpg_float, r2, args[0].Result, args[1].Result);
                            test = this.Add(node.SourceLocation, node.Code.Reverse().ToIfTestZ(), r2);
                        }
                        else
                        {
                            test = this.Add(node.SourceLocation, node.Code.Reverse().ToIfTest(), args[0].Result,
                                            args[1].Result);
                        }
                        this.Add(node.SourceLocation, RCode.Const, 1, r);
                        var end = this.Add(node.SourceLocation, RCode.Nop, null);
                        test.Operand = end;
                        return new RLRange(args, start, end, r);
                    }
                case AstCode.CmpLFloat:
                case AstCode.CmpGFloat:
                case AstCode.CmpLong:
                    {
                        var r = frame.AllocateTemp(PrimitiveType.Int);
                        var code = node.CmpFloatOrLong();
                        var test = this.Add(node.SourceLocation, code, r, args[0].Result, args[1].Result);
                        return new RLRange(args, test, r);
                    }
                case AstCode.CIsNotNull:
                case AstCode.CIsNull:
                    {
                        var r = frame.AllocateTemp(PrimitiveType.Int);
                        var start = this.Add(node.SourceLocation, RCode.Const, 0, r);
                        var test = this.Add(node.SourceLocation, node.Code.Reverse().ToIfTestZ(), args[0].Result);
                        this.Add(node.SourceLocation, RCode.Const, 1, r);
                        var end = this.Add(node.SourceLocation, RCode.Nop, null);
                        test.Operand = end;
                        return new RLRange(args, start, end, r);
                    }
                case AstCode.Brtrue:
                case AstCode.Brfalse:
                case AstCode.BrIfEq:
                case AstCode.BrIfNe:
                case AstCode.BrIfGe:
                case AstCode.BrIfGt:
                case AstCode.BrIfLe:
                case AstCode.BrIfLt:
                    {
                        var label = (AstLabel) node.Operand;
                        var opcode = node.Code.ToIfTestZ();
                        var branch = this.Add(node.SourceLocation, opcode, args[0].Result);
                        labelManager.AddResolveAction(label, x => branch.Operand = x);
                        return new RLRange(branch, null);
                    }
                case AstCode.__Beq:
                case AstCode.__Bne_Un:
                case AstCode.__Ble:
                case AstCode.__Ble_Un:
                case AstCode.__Blt:
                case AstCode.__Blt_Un:
                case AstCode.__Bgt:
                case AstCode.__Bgt_Un:
                case AstCode.__Bge:
                case AstCode.__Bge_Un:
                    {
                        var label = (AstLabel) node.Operand;
                        var opcode = node.Code.ToIfTest();
                        var branch = this.Add(node.SourceLocation, opcode, args[0].Result, args[1].Result);
                        labelManager.AddResolveAction(label, x => branch.Operand = x);
                        return new RLRange(branch, null);
                    }
                case AstCode.Br:
                    {
                        var label = (AstLabel) node.Operand;
                        var branch = this.Add(node.SourceLocation, RCode.Goto, null);
                        labelManager.AddResolveAction(label, x => branch.Operand = x);
                        return new RLRange(branch, null);
                    }
                case AstCode.Leave:
                    {
                        var label = (AstLabel) node.Operand;
                        var branch = this.Add(node.SourceLocation, RCode.Leave, null);
                        labelManager.AddResolveAction(label, x => branch.Operand = x);
                        return new RLRange(branch, null);
                    }
                case AstCode.Switch:
                    {
                        var labels = (AstLabel[]) node.Operand;
                        var targets = new Instruction[labels.Length];
                        for (var i = 0; i < labels.Length; i++)
                        {
                            var index = i;
                            labelManager.AddResolveAction(labels[i], x => targets[index] = x);
                        }
                        var @switch = this.Add(node.SourceLocation, RCode.Packed_switch, targets, args[0].Result);
                        return new RLRange(@switch, null);
                    }
                case AstCode.LookupSwitch:
                    {
                        var labelKeyPairs = (AstLabelKeyPair[]) node.Operand;
                        var targetPairs = new Tuple<int, Instruction>[labelKeyPairs.Length];
                        for (var i = 0; i < labelKeyPairs.Length; i++)
                        {
                            var index = i;
                            var key = labelKeyPairs[i].Key;
                            var label = labelKeyPairs[i].Label;
                            labelManager.AddResolveAction(label, x => targetPairs[index] = Tuple.Create(key, x));
                        }
                        var @switch = this.Add(node.SourceLocation, RCode.Sparse_switch, targetPairs, args[0].Result);
                        return new RLRange(@switch, null);
                    }
                case AstCode.NullCoalescing:
                    {
                        var r = frame.AllocateTemp(node.InferredType.GetReference(targetPackage));
                        var first = this.Add(node.SourceLocation, RCode.Move_object, r, args[0].Result);
                        // r := leftExpr
                        var if_nez = this.Add(node.SourceLocation, RCode.If_nez, r); // if r not null, skip
                        this.Add(node.SourceLocation, RCode.Move_object, r, args[1].Result); // r := rightExpr
                        var end = this.Add(node.SourceLocation, RCode.Nop);
                        if_nez.Operand = end;
                        return new RLRange(first, end, r);
                    }

                    #endregion

                    #region Call

                case AstCode.Call:
                case AstCode.Callvirt:
                case AstCode.CallIntf:
                case AstCode.CallSpecial:
                    {
                        return VisitCallExpression(node, args, parent);
                    }
                case AstCode.Ret:
                    if (currentMethod.ReturnsVoid)
                        return new RLRange(args, this.Add(node.SourceLocation, RCode.Return_void), null);
                    return new RLRange(args, this.Add(node.SourceLocation, node.Return(currentMethod), args[0].Result),
                                       null);

                    #endregion

                    #region Array

                case AstCode.Newarr:
                    {
                        var type = (XTypeReference) node.Operand;
                        var dType = new ArrayType(type.GetReference(targetPackage));
                        var r = frame.AllocateTemp(dType);
                        var newArray = this.Add(node.SourceLocation, RCode.New_array, dType, r, args[0].Result);
                        return new RLRange(newArray, r);
                    }
                case AstCode.ArrayNewInstance:
                    {
                        // Resolve type to a Class<?>
                        var newInstance = FrameworkReferences.ArrayNewInstance;
                        var first = this.Add(node.SourceLocation, RCode.Invoke_static, newInstance, args[0].Result,
                                             args[1].Result);
                        var r = frame.AllocateTemp(newInstance.Prototype.ReturnType);
                        var last = this.Add(node.SourceLocation, RCode.Move_result_object, r);
                        return new RLRange(first, last, r);
                    }
                case AstCode.ArrayNewInstance2:
                    {
                        // Call java.lang.reflect.Array.newInstance(type, int[])
                        var newInstance = FrameworkReferences.ArrayNewInstance2;
                        var first = this.Add(node.SourceLocation, RCode.Invoke_static, newInstance, args[0].Result,
                                             args[1].Result);
                        var resultReg = frame.AllocateTemp(FrameworkReferences.Object);
                        var last = this.Add(node.SourceLocation, RCode.Move_result_object, resultReg);
                        return new RLRange(args, first, last, resultReg);
                    }
                case AstCode.InitEnumArray:
                    {
                        var type = (XTypeReference) node.Operand;
                        var typeDef = type.Resolve();
                        // Initialize array
                        var fillMethod = FrameworkReferences.ArraysFillObject;
                        var denumType = typeDef.GetClassReference(targetPackage);
                        var defaultField = new FieldReference(denumType, NameConstants.Enum.DefaultFieldName, denumType);
                        var rdefault = frame.AllocateTemp(denumType);
                        var first = this.Add(node.SourceLocation, RCode.Sget_object, defaultField, rdefault);
                        var arrayR = args[0].Result;
                        var last = this.Add(node.SourceLocation, RCode.Invoke_static, fillMethod, arrayR, rdefault);
                        return new RLRange(first, last, arrayR);
                    }
                case AstCode.InitStructArray:
                    {
                        var defaultCtor = (XMethodReference) node.Operand;
                        var dDefaultCtor = defaultCtor.GetReference(targetPackage);
                        var arrayR = args[0].Result;
                        var indexR = frame.AllocateTemp(PrimitiveType.Int);
                        var oneR = frame.AllocateTemp(PrimitiveType.Int);
                        var elementR = frame.AllocateTemp(dDefaultCtor.Owner);
                        var first = this.Add(node.SourceLocation, RCode.Array_length, indexR, arrayR);
                        this.Add(node.SourceLocation, RCode.Const, 1, oneR);
                        var ifZero = this.Add(node.SourceLocation, RCode.If_eqz, indexR);
                            // if (index == 0) goto end;  (Operand set later)
                        this.Add(node.SourceLocation, RCode.Sub_int_2addr, indexR, oneR); // index--;
                        this.Add(node.SourceLocation, RCode.New_instance, dDefaultCtor.Owner, elementR);
                            // element = new Struct;
                        this.Add(node.SourceLocation, RCode.Invoke_direct, dDefaultCtor, elementR);
                            // invoke element.ctor()
                        this.Add(node.SourceLocation, RCode.Aput_object, elementR, arrayR, indexR);
                            // Store element in array
                        this.Add(node.SourceLocation, RCode.Goto, ifZero); // End of loop
                        var end = this.Add(node.SourceLocation, RCode.Nop);
                        ifZero.Operand = end;
                        return new RLRange(first, end, arrayR);
                    }
                case AstCode.MultiNewarr:
                    {
                        var arrType = (XTypeReference) node.Operand;
                        var darrType = arrType.GetReference(targetPackage);
                        var compType = arrType;
                        // Unwind array type to component type
                        for (var i = 0; i < node.Arguments.Count; i++)
                        {
                            compType = compType.ElementType;
                        }
                        var dcompType = new ArrayType(compType.GetReference(targetPackage));
                        var dimArrayR = frame.AllocateTemp(new ArrayType(PrimitiveType.Int));
                        var lengthR = frame.AllocateTemp(PrimitiveType.Int);
                        var first = this.Add(node.SourceLocation, RCode.Nop);
                        // Allocate dimensions array
                        this.Add(node.SourceLocation, RCode.Const, node.Arguments.Count, lengthR);
                        this.Add(node.SourceLocation, RCode.New_array, PrimitiveType.Int, dimArrayR, lengthR);
                        var indexR = lengthR;
                        // Initialize dimensions array
                        for (var i = 0; i < node.Arguments.Count; i++)
                        {
                            this.Add(node.SourceLocation, RCode.Const, i, indexR);
                            this.Add(node.SourceLocation, RCode.Aput, args[i], dimArrayR, indexR);
                        }

                        // Load component type
                        var compTypeR = frame.AllocateTemp(new ClassReference("java/lang/Class"));
                        this.Add(node.SourceLocation, RCode.Const_class, dcompType, compTypeR);

                        // Call java/lang/reflect/Array/newInstance
                        var reflectArrayType = new ClassReference("java/lang/reflect/Array");
                        var prototype = PrototypeBuilder.ParseMethodSignature("(Ljava/lang/Class;[I)Ljava/lang/Object;");
                        var methodRef = new MethodReference(reflectArrayType, "newInstance", prototype);
                        var arrayR = frame.AllocateTemp(darrType);
                        this.Add(node.SourceLocation, RCode.Invoke_static, methodRef, compTypeR, dimArrayR);
                        var last = this.Add(node.SourceLocation, RCode.Move_result_object, arrayR);

                        return new RLRange(first, last, arrayR);
                    }
                case AstCode.ByRefArray:
                case AstCode.ByRefOutArray:
                    {
                        // Create array
                        var type = (XTypeReference) node.Operand;
                        var dType = new ArrayType(type.GetReference(targetPackage));
                        var arrayR = frame.AllocateTemp(dType);
                        var lengthR = frame.AllocateTemp(PrimitiveType.Int);
                        // length=1
                        var initLength = this.Add(node.SourceLocation, RCode.Const, 1, lengthR);
                        // newarray
                        this.Add(node.SourceLocation, RCode.New_array, dType, arrayR, lengthR);
                        if (node.Code == AstCode.ByRefArray)
                        {
                            var valueR = args[0].Result;
                            var arrayType = node.InferredType;

                            // Perform type conversion if needed
                            bool isConverted;
                            var converted = this.ConvertTypeBeforeStore(node.SourceLocation, type, arrayType.ElementType,
                                                                        valueR, targetPackage, frame, compiler,
                                                                        out isConverted);
                            if (isConverted) valueR = converted.Result;

                            // array[0]=value
                            var indexR = frame.AllocateTemp(PrimitiveType.Int);
                            this.Add(node.SourceLocation, RCode.Const, 0, indexR);
                            this.Add(node.SourceLocation, arrayType.APut(), valueR, arrayR, indexR);
                        }
                        var end = this.Add(node.SourceLocation, RCode.Nop);
                        return new RLRange(initLength, end, arrayR);
                    }
                case AstCode.Ldlen:
                    {
                        var r = frame.AllocateTemp(PrimitiveType.Int);
                        return new RLRange(this.Add(node.SourceLocation, RCode.Array_length, r, args[0].Result), r);
                    }
                case AstCode.Stelem_I:
                case AstCode.Stelem_I1:
                case AstCode.Stelem_I2:
                case AstCode.Stelem_I4:
                case AstCode.Stelem_I8:
                case AstCode.Stelem_R4:
                case AstCode.Stelem_R8:
                case AstCode.Stelem_Ref:
                case AstCode.Stelem_Any:
                    {
                        var first = this.Add(node.SourceLocation, RCode.Nop);
                        var arrayR = args[0].Result;
                        var indexR = args[1].Result;
                        var valueR = args[2].Result;
                        var valueType = node.Arguments[2].GetResultType();
                        var arrayType = node.Arguments[0].GetResultType();

                        // Perform type conversion if needed
                        bool isConverted;
                        var converted = this.ConvertTypeBeforeStore(node.SourceLocation, valueType,
                                                                    arrayType.ElementType, valueR, targetPackage,
                                                                    frame, compiler, out isConverted);
                        if (isConverted) valueR = converted.Result;

                        // Store in array
                        var aput = this.Add(node.SourceLocation, node.APut(), valueR, arrayR, indexR);
                        return new RLRange(first, aput, valueR);
                    }
                case AstCode.Ldelem_I:
                case AstCode.Ldelem_I1:
                case AstCode.Ldelem_I2:
                case AstCode.Ldelem_I4:
                case AstCode.Ldelem_I8:
                case AstCode.Ldelem_R4:
                case AstCode.Ldelem_R8:
                case AstCode.Ldelem_U1:
                case AstCode.Ldelem_U2:
                case AstCode.Ldelem_U4:
                case AstCode.Ldelem_Ref:
                case AstCode.Ldelem_Any:
                    {
                        var arrayR = args[0].Result;
                        var indexR = args[1].Result;
                        var arrayType = node.Arguments[0].GetResultType();
                        var elementType = arrayType.ElementType;

                        // Allocate registry for value
                        var valueR = frame.AllocateTemp(elementType.GetReference(targetPackage));

                        // Get from array
                        var first = this.Add(node.SourceLocation, node.AGet(), valueR, arrayR, indexR);
                        return new RLRange(first, valueR);
                    }

                    #endregion

                    #region Local variables / arguments

                case AstCode.Ldloc:
                    {
                        var variable = (AstVariable) node.Operand;
                        var valueR = frame.GetArgument(variable);
                        return new RLRange(args, valueR);
                    }
                case AstCode.Ldthis:
                    if (frame.ThisArgument == null)
                        throw new ArgumentException("No this in current method");
                    return new RLRange(args, frame.ThisArgument);
                case AstCode.Stloc:
                    {
                        var variable = (AstVariable) node.Operand;
                        var resultType = node.Arguments[0].GetResultType();
                        var valueR = args[0].Result;
                        var first = this.Add(node.SourceLocation, RCode.Nop);
                        // Convert value if needed
                        bool isConverted;
                        var converted = this.ConvertTypeBeforeStore(node.SourceLocation, resultType, variable.Type,
                                                                    valueR, targetPackage, frame, compiler,
                                                                    out isConverted);
                        if (isConverted) valueR = converted.Result;
                        // Store in variable
                        var variableR = frame.GetArgument(variable);
                        return new RLRange(args, first, this.Add(node.SourceLocation, node.Move(), variableR, valueR),
                                           variableR);
                    }

                    #endregion

                    #region Fields

                case AstCode.Ldfld:
                    {
                        var fieldRef = (XFieldReference) node.Operand;
                        var field = fieldRef.Resolve();
                        var dField = field.GetReference(targetPackage);
                        var fieldType = field.FieldType;
                        // Allocate register
                        var valueR = frame.AllocateTemp(fieldType.GetReference(targetPackage));
                        // Get from field
                        var iget = this.Add(node.SourceLocation, field.IGet(), dField, valueR, args[0].Result);
                        return new RLRange(iget, valueR);
                    }
                case AstCode.Stfld:
                    {
                        var fieldRef = (XFieldReference) node.Operand;
                        var field = fieldRef.Resolve();
                        var dField = field.GetReference(targetPackage);
                        var type = node.InferredType;
                        var first = this.Add(node.SourceLocation, RCode.Nop);
                        var valueR = args[1].Result;

                        // Perform type conversion if needed
                        bool isConverted;
                        var converted = this.ConvertTypeBeforeStore(node.SourceLocation, type, field.FieldType, valueR,
                                                                    targetPackage, frame, compiler,
                                                                    out isConverted);
                        if (isConverted) valueR = converted.Result;

                        // Store in field
                        var iput = this.Add(node.SourceLocation, field.IPut(), dField, valueR, args[0].Result);
                        return new RLRange(first, iput, valueR);
                    }
                case AstCode.Ldsfld:
                    {
                        var field = (XFieldReference) node.Operand;
                        XFieldDefinition fieldDef;
                        field.TryResolve(out fieldDef);
                        var fieldType = field.FieldType;
                        // Allocate register
                        var valueR = frame.AllocateTemp(fieldType.GetReference(targetPackage));

                        string resourceName;
                        if ((fieldDef != null) && fieldDef.TryGetResourceIdAttribute(out resourceName))
                        {
                            // Replace by resource id.
                            var id = FindResourceId(compiler.ResourceTable, resourceName);
                            return new RLRange(args, this.Add(node.SourceLocation, RCode.Const, id, valueR), valueR);
                        }
                        else
                        {
                            // Normal get from field
                            var dField = field.GetReference(targetPackage);
                            var iget = this.Add(node.SourceLocation, node.SGet(), dField, valueR);
                            return new RLRange(iget, valueR);
                        }
                    }
                case AstCode.Stsfld:
                    {
                        var field = (XFieldReference) node.Operand;
                        var dField = field.GetReference(targetPackage);
                        var type = node.InferredType;
                        var first = this.Add(node.SourceLocation, RCode.Nop);
                        var valueR = args[0].Result;

                        // Perform type conversion if needed
                        bool isConverted;
                        var converted = this.ConvertTypeBeforeStore(node.SourceLocation, type, field.FieldType, valueR,
                                                                    targetPackage, frame, compiler,
                                                                    out isConverted);
                        if (isConverted) valueR = converted.Result;

                        // Store in field
                        var sput = this.Add(node.SourceLocation, node.SPut(), dField, valueR);
                        return new RLRange(first, sput, valueR);
                    }

                    #endregion

                    #region Object model

                case AstCode.Newobj: // IL new
                    {
                        var ilCtorRef = (XMethodReference) node.Operand;
                        var ilType = ilCtorRef.DeclaringType;

                        // New normal object
                        var ilCtor = ilCtorRef.Resolve();
                        var dCtor = ilCtor.GetReference(targetPackage);
                        var dType = ilType.GetReference(targetPackage);

                        // Create instance
                        var r = frame.AllocateTemp(dType);
                        var first = this.Add(node.SourceLocation, RCode.New_instance, dType, r);

                        // Prepare arguments
                        int argsOffset;
                        List<RLRange> originalArgs;
                        ConvertParametersBeforeCall(node, args, ilCtor, out argsOffset, out originalArgs);

                        // Collect arguments
                        var arguments = args.SelectMany(x => x.Result.Registers).ToList();
                        // Insert this argument
                        arguments.Insert(0, r);

                        // Invoke ctor
                        this.Add(node.SourceLocation, RCode.Invoke_direct, dCtor, arguments);

                        // Post process arguments
                        ConvertParametersAfterCall(node, args, ilCtor, argsOffset, originalArgs);
                        var last = this.Add(node.SourceLocation, RCode.Nop);
                        return new RLRange(args, first, last, r);
                    }
                case AstCode.New: // Java new
                    {
                        var ilType = (XTypeReference) node.Operand;
                        // New normal object
                        var dType = ilType.GetReference(targetPackage);
                        // Create instance
                        var r = frame.AllocateTemp(dType);
                        var first = this.Add(node.SourceLocation, RCode.New_instance, dType, r);
                        return new RLRange(args, first, r);
                    }
                case AstCode.CallBaseCtor:
                    {
                        var dtype = currentDexMethod.Owner;
                        var dBaseType = dtype.SuperClass as ClassDefinition;
                        if (dBaseType == null)
                            throw new CompilerException(string.Format("Type {0} base no superclass as definition", dtype.Fullname));
                        var paramCount = node.Arguments.Count - 1;
                        var baseCtor = dBaseType.Methods.Single(x => x.IsConstructor && (x.Prototype.Parameters.Count == paramCount));
                        var call = this.Add(node.SourceLocation, RCode.Invoke_direct, baseCtor, args.SelectMany(x => x.Result.Registers));
                        return new RLRange(call, args[0].Result);
                    }
            case AstCode.Castclass:
                    {
                        throw new NotSupportedException("castclass is not supported");
                    }
                case AstCode.SimpleCastclass:
                    {
                        var type = (XTypeReference) node.Operand;
                        var dType = type.GetReference(targetPackage);
                        if (type.IsPrimitive)
                        {
                            // Convert type to boxed type
                            dType = BoxInfo.GetBoxedType(type);
                        }

                        // Normal cast
                        var tmp = this.EnsureTemp(node.SourceLocation, args[0].Result, frame);
                        var checkCast = this.Add(node.SourceLocation, RCode.Check_cast, dType, tmp.Result);
                        return new RLRange(tmp, checkCast, tmp.Result);
                    }
                case AstCode.Isinst: // "as" operator
                    {
                        throw new NotSupportedException("isinst is not supported");
                    }
                case AstCode.InstanceOf: // "is" operator
                    {
                        throw new NotSupportedException("instanceof is not supported");
                    }
                case AstCode.SimpleInstanceOf: // "is" operator
                    {
                        var type = (XTypeReference) node.Operand;
                        var dType = type.GetReference(targetPackage);
                        if (type.IsPrimitive)
                        {
                            // Convert type to boxed type
                            dType = BoxInfo.GetBoxedType(type);
                        }
                        // Normal "is"
                        var rResult = frame.AllocateTemp(PrimitiveType.Boolean);
                        var instanceOf = this.Add(node.SourceLocation, RCode.Instance_of, dType, rResult, args[0].Result);
                        return new RLRange(instanceOf, rResult);
                    }

                    #endregion

#region Generics
                case AstCode.LdGenericInstanceField:
                    {
                        var giField = GetGenericInstanceField();
                        var r = frame.AllocateTemp(FrameworkReferences.ClassArray);
                        var iget = this.Add(node.SourceLocation, RCode.Iget_object, giField, r, frame.ThisArgument);
                        return new RLRange(iget, r);
                    }
                case AstCode.StGenericInstanceField:
                    {
                        var giField = GetGenericInstanceField();
                        var r = args[0].Result;
                        var iput = this.Add(node.SourceLocation, RCode.Iput_object, giField, r, frame.ThisArgument);
                        return new RLRange(iput, r);
                    }
                case AstCode.LdGenericInstanceTypeArgument:
                    {
                        if (frame.GenericInstanceTypeArgument == null)
                        {
                            throw new CompilerException(string.Format("Method {0} has no generic instance type argument", currentMethod.FullName));
                        }
                        return new RLRange(frame.GenericInstanceTypeArgument);
                    }
                case AstCode.LdGenericInstanceMethodArgument:
                    {
                        if (frame.GenericInstanceMethodArgument == null)
                        {
                            throw new CompilerException(string.Format("Method {0} has no generic instance method argument", currentMethod.FullName));
                        }
                        return new RLRange(frame.GenericInstanceMethodArgument);
                    }
                case AstCode.UnboxFromGeneric:
                    {
                        // Get result
                        var elementType = (XTypeReference) node.Operand;
                        var r = args[0].Result;
                        var start = this.Add(node.SourceLocation, RCode.Nop);
                        var last = start;

                        // Convert result when needed
                        if (elementType.IsGenericParameter)
                        {
                            var returnType = node.GetResultType();
                            var tmp = this.Unbox(node.SourceLocation, r, returnType, compiler, targetPackage, frame);
                            last = tmp.Last;
                            r = tmp.Result;
                        }
                        else if (elementType.IsGenericParameterArray())
                        {
                            var returnType = node.GetResultType();
                            if (returnType.IsPrimitiveArray())
                            {
                                var tmp = this.UnboxGenericArrayResult(node.SourceLocation, r, returnType, targetPackage, frame, compiler);
                                last = tmp.Last;
                                r = tmp.Result;
                            }
                            else
                            {
                                var tmp = this.Unbox(node.SourceLocation, r, returnType, compiler, targetPackage, frame);
                                last = tmp.Last;
                                r = tmp.Result;
                            }
                        }
                        return new RLRange(start, last, r);
                    }
#endregion

                case AstCode.Throw:
                    {
                        var @throw = this.Add(node.SourceLocation, RCode.Throw, args[0].Result);
                        return new RLRange(@throw, null);
                    }
                case AstCode.Rethrow:
                    {
                        if (currentExceptionRegister == null)
                            throw new CompilerException("retrow outside catch block");
                        var @throw = this.Add(node.SourceLocation, RCode.Throw, currentExceptionRegister);
                        return new RLRange(@throw, null);                        
                    }
                case AstCode.Delegate:
                    {
                        //Debugger.Launch();
                        var delegateInfo = (Tuple<XTypeDefinition, XMethodDefinition>)node.Operand;
                        var delegateType = compiler.GetDelegateType(delegateInfo.Item1);
                        var delegateInstanceType = delegateType.GetOrCreateInstance(node.SourceLocation, targetPackage, delegateInfo.Item2);

                        // Create instance
                        var r = frame.AllocateTemp(delegateInstanceType.InstanceDefinition);
                        var newobj = this.Add(node.SourceLocation, RCode.New_instance, delegateInstanceType.InstanceDefinition, r);
                        // Call ctor
                        if (delegateInstanceType.CalledMethodIsStatic)
                        {
                            // Call without instance argument
                            var invokeCtor = this.Add(node.SourceLocation, RCode.Invoke_direct, delegateInstanceType.InstanceCtor, r);
                            return new RLRange(newobj, invokeCtor, r);
                        }
                        else
                        {
                            // Call with instance argument
                            var invokeCtor = this.Add(node.SourceLocation, RCode.Invoke_direct, delegateInstanceType.InstanceCtor, r, args[0].Result);
                            return new RLRange(newobj, invokeCtor, r);
                        }
                    }
                case AstCode.InitArray:
                    {
                        var arrayData = (InitArrayData) node.Operand;
                        var size = arrayData.Length;
                        var type = arrayData.ArrayType;
                        var dType = type.GetReference(targetPackage);

                        // Allocate new array
                        var rArray = frame.AllocateTemp(dType);
                        var rSize = frame.AllocateTemp(PrimitiveType.Int);
                        var start = this.Add(node.SourceLocation, RCode.Const, size, rSize);
                        this.Add(node.SourceLocation, RCode.New_array, dType, rArray, rSize);

                        // Initialize array
                        if (arrayData.IsSupportedByFillArrayData())
                        {
                            // Use fill-array-data
                            this.Add(node.SourceLocation, RCode.Fill_array_data, arrayData.Values, rArray);
                        }
                        else
                        {
                            // Use const/aput sequence
                            var rValue = frame.AllocateTemp(arrayData.ArrayType.ElementType.GetReference(targetPackage));
                            var rIndex = frame.AllocateTemp(PrimitiveType.Int);
                            var isWide = arrayData.ArrayType.ElementType.IsWide();
                            var constCode = isWide ? RCode.Const_wide : RCode.Const;
                            var aputCode = arrayData.ArrayType.APut();
                            var convertCode = arrayData.ArrayType.AConstConvertBeforePut();
                            var valueConverter = arrayData.ArrayType.ElementType.ConstValueConverter(false);

                            // Initialize index
                            this.Add(node.SourceLocation, RCode.Const, 0, rIndex);

                            // aput for each
                            for (var i = 0; i < arrayData.Length; i++)
                            {
                                var value = valueConverter(arrayData.Values.GetValue(i));
                                this.Add(node.SourceLocation, constCode, value, rValue);
                                if (convertCode != RCode.Nop)
                                {
                                    this.Add(node.SourceLocation, convertCode, rValue, rValue);                                    
                                }
                                this.Add(node.SourceLocation, aputCode, rValue, rArray, rIndex);
                                if (i + 1 < arrayData.Length)
                                {
                                    // Increment index
                                    this.Add(node.SourceLocation, RCode.Add_int_lit, 1, rIndex, rIndex);
                                }
                            }
                        }


                        //throw new NotImplementedException();
                        return new RLRange(this.Add(node.SourceLocation, RCode.Nop), rArray);
                    }
                case AstCode.InitArrayFromArguments:
                    {
                        var size = args.Count;
                        var type = (XArrayType) node.Operand;
                        var dType = type.GetReference(targetPackage);

                        // Allocate new array
                        var rArray = frame.AllocateTemp(dType);
                        var rSize = frame.AllocateTemp(PrimitiveType.Int);
                        var start = this.Add(node.SourceLocation, RCode.Nop);
                        this.Add(node.SourceLocation, RCode.Const, size, rSize);
                        this.Add(node.SourceLocation, RCode.New_array, dType, rArray, rSize);

                        // Use const/aput sequence
                        var rIndex = frame.AllocateTemp(PrimitiveType.Int);
                        var aputCode = type.APut();

                        // Initialize index
                        this.Add(node.SourceLocation, RCode.Const, 0, rIndex);

                        // aput for each
                        for (var i = 0; i < size; i++)
                        {
                            this.Add(node.SourceLocation, aputCode, args[i].Result, rArray, rIndex);
                            if (i + 1 < size)
                            {
                                // Increment index
                                this.Add(node.SourceLocation, RCode.Add_int_lit, 1, rIndex, rIndex);
                            }
                        }
                        return new RLRange(start, this.Add(node.SourceLocation, RCode.Nop), rArray);
                    }
                case AstCode.LdClass:
                    {
                        var ilType = (XTypeReference)node.Operand;
                        var dType = ilType.GetReference(targetPackage);
                        // Load type
                        var r = frame.AllocateTemp(FrameworkReferences.Class);
                        var first = this.Add(node.SourceLocation, RCode.Const_class, dType, r);
                        return new RLRange(args, first, r);                        
                    }
                case AstCode.Ldtoken:
                    {
                        throw new NotSupportedException("ldtoken is not supported");
                    }
                default:
                    string opcodeName;
#if DEBUG
                    //Debugger.Launch();
                    opcodeName = node.Code.ToString();
#else
                    opcodeName = ((int)node.Code).ToString();
#endif
                    var source = FormatSource(node);
                    var msg = string.Format("Unexpected opcode {0} in {1}.", opcodeName, source);
                    throw new ArgumentException(msg);
            }
        }
 public RLRange BranchToFinally_Leave(Instruction insTarget, ref int insIdx)
 {
     var target = new FinallyTarget { IsLeave = true, Destination = insTarget};
     return BranchToFinally(target, null, AstNode.NoSource, ref insIdx);
 }
 public RLRange BranchToFinally_FallOut(ISourceLocation sourceLocation, Instruction insTarget, List<RLRange> args)
 {
     int insIdx = _compiler.instructions.Count;
     var target = new FinallyTarget {Destination = insTarget, IsFallOut = true};
     return BranchToFinally(target, args, sourceLocation, ref insIdx);
 }
        /// <summary>
        /// Create the body of the invoke method.
        /// </summary>
        private static MethodBody CreateInvokeBody(ISourceLocation sequencePoint, AssemblyCompiler compiler, DexTargetPackage targetPackage, XMethodDefinition calledMethod, XMethodDefinition invokeMethod, Prototype invokePrototype, Prototype calledMethodPrototype, FieldDefinition instanceField, ClassReference delegateClass)
        {
            var body = new MethodBody(null);
            var rthis = body.AllocateRegister(RCategory.Argument, RType.Object);
            foreach (var p in invokePrototype.Parameters)
            {
                if (p.Type.IsWide())
                {
                    body.AllocateWideRegister(RCategory.Argument);
                }
                else
                {
                    var type = (p.Type is PrimitiveType) ? RType.Value : RType.Object;
                    body.AllocateRegister(RCategory.Argument, type);
                }
            }
            var incomingMethodArgs = body.Registers.ToArray();

            // Create code
            var ins = body.Instructions;
            Register instance = null;
            if (!calledMethod.IsStatic)
            {
                // load instance
                instance = body.AllocateRegister(RCategory.Temp, RType.Object);
                ins.Add(new Instruction(RCode.Iget_object, instance, rthis) { Operand = instanceField });
            }
            // Invoke
            var calledMethodRef = calledMethod.GetReference(targetPackage);
            var inputArgs = calledMethod.IsStatic ? incomingMethodArgs.Skip(1).ToArray() : incomingMethodArgs;
            
            // Cast arguments (if needed)
            var outputArgs = new List<Register>();
            if (!calledMethod.IsStatic)
            {
                outputArgs.Add(instance);
            }
            var parameterIndex = 0;
            for (var i = calledMethod.IsStatic ? 0 : 1; i < inputArgs.Length; )
            {
                var invokeType = invokePrototype.Parameters[parameterIndex].Type;
                var inputIsWide = invokeType.IsWide();
                var calledType = calledMethodPrototype.Parameters[parameterIndex].Type;
                if (!invokeType.Equals(calledType))
                {
                    // Add cast / unbox
                    var source = inputIsWide
                                     ? new RegisterSpec(inputArgs[i], inputArgs[i + 1], invokeType)
                                     : new RegisterSpec(inputArgs[i], null, invokeType);
                    var tmp = ins.Unbox(sequencePoint, source, calledMethod.Parameters[parameterIndex].ParameterType, compiler, targetPackage, body);
                    outputArgs.Add(tmp.Result.Register);
                    if (calledType.IsWide())
                    {
                        outputArgs.Add(tmp.Result.Register2);
                    }
                }
                else
                {
                    outputArgs.Add(inputArgs[i]);
                    if (calledType.IsWide())
                    {
                        outputArgs.Add(inputArgs[i + 1]);
                    }
                }
                i += inputIsWide ? 2 : 1;
                parameterIndex++;
            }

            // Actual call
            ins.Add(new Instruction(calledMethod.Invoke(calledMethod, null), calledMethodRef, outputArgs.ToArray()));

            // Collect return value
            var invokeReturnType = invokePrototype.ReturnType;
            var calledReturnType = calledMethodPrototype.ReturnType;
            var needsBoxing = !invokeReturnType.Equals(calledReturnType);
            Instruction returnInstruction;

            if (calledReturnType.IsWide())
            {
                var r = body.AllocateWideRegister(RCategory.Temp);
                ins.Add(new Instruction(RCode.Move_result_wide, r.Item1));
                if (needsBoxing)
                {
                    // Box
                    var source = new RegisterSpec(r.Item1, r.Item2, calledReturnType);
                    var tmp = ins.Box(sequencePoint, source, calledMethod.ReturnType, targetPackage, body);
                    returnInstruction = new Instruction(RCode.Return_object, tmp.Result.Register);
                }
                else
                {
                    // Return wide
                    returnInstruction = new Instruction(RCode.Return_wide, r.Item1);
                }
            }
            else if (calledMethod.ReturnType.IsVoid())
            {
                // Void return
                returnInstruction = new Instruction(RCode.Return_void);
            }
            else if (calledReturnType is PrimitiveType)
            {
                // Single register return
                var r = body.AllocateRegister(RCategory.Temp, RType.Value);
                ins.Add(new Instruction(RCode.Move_result, r));
                if (needsBoxing)
                {
                    // Box
                    var source = new RegisterSpec(r, null, invokeReturnType);
                    var tmp = ins.Box(sequencePoint, source, calledMethod.ReturnType, targetPackage, body);
                    returnInstruction = new Instruction(RCode.Return_object, tmp.Result.Register);
                }
                else
                {
                    // Return 
                    returnInstruction = new Instruction(RCode.Return, r);
                }
            }
            else
            {
                var r = body.AllocateRegister(RCategory.Temp, RType.Object);
                ins.Add(new Instruction(RCode.Move_result_object, r));
                if (needsBoxing)
                {
                    // Box
                    var source = new RegisterSpec(r, null, invokeReturnType);
                    var tmp = ins.Box(sequencePoint, source, invokeMethod.ReturnType, targetPackage, body);
                    returnInstruction = new Instruction(RCode.Return_object, tmp.Result.Register);
                }
                else
                {
                    // Return 
                    returnInstruction = new Instruction(RCode.Return_object, r);
                }
            }

            // Call next delegate (if any)
            var next = body.AllocateRegister(RCategory.Temp, RType.Object);
            var multicastDelegateType = new ClassReference(targetPackage.NameConverter.GetConvertedFullName("System.MulticastDelegate"));
            var nextReference = new FieldReference(multicastDelegateType, "next", multicastDelegateType);
            ins.Add(new Instruction(RCode.Iget_object, nextReference, new[] { next, rthis })); // load this.next
            var afterCallNext = new Instruction(RCode.Nop);
            ins.Add(new Instruction(RCode.If_eqz, afterCallNext, new[] { next })); // if next == null, continue
            ins.Add(new Instruction(RCode.Check_cast, delegateClass, new[] { next }));
            var nextInvokeMethod = new MethodReference(delegateClass, "Invoke", invokePrototype);
            var nextInvokeArgs = new[] { next }.Concat(incomingMethodArgs.Skip(1)).ToArray();
            ins.Add(new Instruction(RCode.Invoke_virtual, nextInvokeMethod, nextInvokeArgs));
            ins.Add(afterCallNext);

            // Add return instructions
            ins.Add(returnInstruction);

            return body;
        }
        private Instruction TryCatchGotoEnd(FinallyBlockState state, Instruction last)
        {
            if (state.HasFinally)
            {
                var rlrange = state.BranchToFinally_FallOut(AstNode.NoSource, last, new List<RLRange>());
                return rlrange.Last;
            }

            // goto end 
            return this.Add(AstNode.NoSource, RCode.Goto, last);    
        }