コード例 #1
0
        private static void FixTryCatchFinally(AstTryCatchBlock node)
        {
            // this of course is all rather brittle and tied to the 
            // internal workings of the C# compiler.

            if (node.FinallyBlock == null)
                return;
            var firstExpr = node.FinallyBlock.GetChildren().First() as AstExpression;
            if (firstExpr == null)
                return;
            if (!firstExpr.Code.IsConditionalControlFlow())
                return;

            if (firstExpr.Arguments[0].Code != AstCode.Ldloc) // should read a local variable
                return;
            var condVar = firstExpr.Arguments[0].Operand as AstVariable;
            if (condVar == null)  // safety check needed?
                return;
            // make sure the variable is not written to in the try block or any of the catch blocks
            if(IsModified(node.TryBlock, condVar) || node.CatchBlocks.Any(c=>IsModified(c, condVar)))
                throw  new CompilerException("Unable to fix complex locking in async method.");

            // remove the conditional branch.
            firstExpr.Arguments.Clear();
            firstExpr.Operand = null;
            firstExpr.Code = AstCode.Nop;
        }
コード例 #2
0
        /// <summary>
        /// Insert the finally block of the given try/catch block before every "exit" opcode in the given range.
        /// </summary>
        private void InsertFinallyBeforeReturns(Instruction first, Instruction last, AstTryCatchBlock tryCatchBlock)
        {
            if (tryCatchBlock.FinallyBlock == null)
            {
                return;
            }

            // Find all return instructions
            var firstIndex       = first.Index;
            var lastIndex        = last.Index;
            var exitInstructions = new List <Instruction>();

            for (var index = firstIndex; index <= lastIndex; index++)
            {
                var ins = instructions[index];
                if (ins.Code.IsReturn() /*|| (ins.Code == RCode.Leave)*/)
                {
                    exitInstructions.Add(ins);
                }
            }
            if (exitInstructions.Count == 0)
            {
                return;
            }

            // Insert finally block before each return instruction
            foreach (var ins in exitInstructions)
            {
                // Clone the exit instruction
                var clone = new Instruction(ins);

                // Emit finally block
                var finallyStart = this.Add(tryCatchBlock.FinallyBlock.SourceLocation, RCode.Nop);
                using (labelManager.CreateContext())
                {
                    tryCatchBlock.FinallyBlock.Accept(this, tryCatchBlock);
                    tryCatchBlock.FinallyBlock.CleanResults();
                }

                // Convert instruction -> jump to finally block
                ins.Code    = RCode.Goto;
                ins.Operand = finallyStart;
                ins.Registers.Clear();

                // Append exit instruction
                instructions.Add(clone);
            }
        }
コード例 #3
0
        private static void FixTryCatchFinally(AstTryCatchBlock node)
        {
            // this of course is all rather brittle and tied to the
            // internal workings of the C# compiler.

            if (node.FinallyBlock == null)
            {
                return;
            }
            var firstExpr = node.FinallyBlock.GetChildren().First() as AstExpression;

            if (firstExpr == null)
            {
                return;
            }
            if (!firstExpr.Code.IsConditionalControlFlow())
            {
                return;
            }

            if (firstExpr.Arguments[0].Code != AstCode.Ldloc) // should read a local variable
            {
                return;
            }
            var condVar = firstExpr.Arguments[0].Operand as AstVariable;

            if (condVar == null)  // safety check needed?
            {
                return;
            }
            // make sure the variable is not written to in the try block or any of the catch blocks
            if (IsModified(node.TryBlock, condVar) || node.CatchBlocks.Any(c => IsModified(c, condVar)))
            {
                throw  new CompilerException("Unable to fix complex locking in async method.");
            }

            // remove the conditional branch.
            firstExpr.Arguments.Clear();
            firstExpr.Operand = null;
            firstExpr.Code    = AstCode.Nop;
        }
コード例 #4
0
        /// <summary>
        /// Convert the given method to a list of Ast nodes.
        /// </summary>
        public AstBlock Build()
        {
            if ((codeAttr == null) || (codeAttr.Code.Length == 0))
            {
                return(new AstBlock((ISourceLocation)null));
            }

            var body            = StackAnalysis();
            var offset2ByteCode = new Dictionary <int, ByteCode>();

            foreach (var bc in body)
            {
                offset2ByteCode[bc.Offset] = bc;
            }
            var blockStarts = SplitInBlocks(body, validExceptionHandlers);
            var list        = ConvertToAst(body, new HashSet <ExceptionHandler>(validExceptionHandlers), blockStarts, 0, offset2ByteCode);
            var ast         = new AstBlock(list.Select(x => x.SourceLocation).FirstOrDefault(), list);

            if (methodDef.IsSynchronized)
            {
                // Wrap in synchronization block
                var sl       = ast.SourceLocation;
                var tryCatch = new AstTryCatchBlock(sl);
                // try-lock(this)
                var lockExpr = methodDef.IsStatic ?
                               new AstExpression(sl, AstCode.TypeOf, declaringType) :
                               new AstExpression(sl, AstCode.Ldthis, null);
                ast.Body.Insert(0, new AstExpression(sl, AstCode.Call, MonitorMethodReference("Enter"), new AstExpression(lockExpr)));
                tryCatch.TryBlock = ast;
                // finally-unlock(this)
                tryCatch.FinallyBlock = new AstBlock(sl,
                                                     new AstExpression(sl, AstCode.Call, MonitorMethodReference("Exit"), new AstExpression(lockExpr)),
                                                     new AstExpression(sl, AstCode.Endfinally, null));
                // Wrap try/catch in block
                ast = new AstBlock(sl, tryCatch);
            }

            return(ast);
        }
コード例 #5
0
ファイル: AstBuilder.cs プロジェクト: Xtremrules/dot42
        /// <summary>
        /// Convert the given method to a list of Ast nodes.
        /// </summary>
        public AstBlock Build()
        {
            if ((codeAttr == null) || (codeAttr.Code.Length == 0))
                return new AstBlock((ISourceLocation)null);

            var body = StackAnalysis();
            var offset2ByteCode = new Dictionary<int, ByteCode>();
            foreach (var bc in body)
            {
                offset2ByteCode[bc.Offset] = bc;
            }
            var blockStarts = SplitInBlocks(body, validExceptionHandlers);
            var list = ConvertToAst(body, new HashSet<ExceptionHandler>(validExceptionHandlers), blockStarts, 0, offset2ByteCode);
            var ast = new AstBlock(list.Select(x => x.SourceLocation).FirstOrDefault(), list);

            if (methodDef.IsSynchronized)
            {
                // Wrap in synchronization block
                var sl = ast.SourceLocation;
                var tryCatch = new AstTryCatchBlock(sl);
                // try-lock(this)
                var lockExpr = methodDef.IsStatic ?
                    new AstExpression(sl, AstCode.TypeOf, declaringType) :
                    new AstExpression(sl, AstCode.Ldthis, null);
                ast.Body.Insert(0, new AstExpression(sl, AstCode.Call, MonitorMethodReference("Enter"), new AstExpression(lockExpr)));
                tryCatch.TryBlock = ast;
                // finally-unlock(this)
                tryCatch.FinallyBlock = new AstBlock(sl, 
                    new AstExpression(sl, AstCode.Call, MonitorMethodReference("Exit"), new AstExpression(lockExpr)),
                    new AstExpression(sl, AstCode.Endfinally, null));
                // Wrap try/catch in block
                ast = new AstBlock(sl, tryCatch);
            }

            return ast;
        }
コード例 #6
0
        /// <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));
        }
コード例 #7
0
        /// <summary>
        /// Get the first expression to be excecuted if the instruction pointer is at the start of the given node.
        /// Try blocks may not be entered in any way.  If possible, the try block is returned as the node to be executed.
        /// </summary>
        AstNode Enter(AstNode node, HashSet <AstNode> visitedNodes)
        {
            if (node == null)
            {
                throw new ArgumentNullException();
            }

            if (!visitedNodes.Add(node))
            {
                return(null);                 // Infinite loop
            }
            AstLabel label = node as AstLabel;

            if (label != null)
            {
                return(Exit(label, visitedNodes));
            }

            AstExpression expr = node as AstExpression;

            if (expr != null)
            {
                if (expr.Code == AstCode.Br || expr.Code == AstCode.Leave)
                {
                    AstLabel target = (AstLabel)expr.Operand;
                    // Early exit - same try-block
                    if (GetParents(expr).OfType <AstTryCatchBlock>().FirstOrDefault() == GetParents(target).OfType <AstTryCatchBlock>().FirstOrDefault())
                    {
                        return(Enter(target, visitedNodes));
                    }
                    // Make sure we are not entering any try-block
                    var srcTryBlocks = GetParents(expr).OfType <AstTryCatchBlock>().Reverse().ToList();
                    var dstTryBlocks = GetParents(target).OfType <AstTryCatchBlock>().Reverse().ToList();
                    // Skip blocks that we are already in
                    int i = 0;
                    while (i < srcTryBlocks.Count && i < dstTryBlocks.Count && srcTryBlocks[i] == dstTryBlocks[i])
                    {
                        i++;
                    }
                    if (i == dstTryBlocks.Count)
                    {
                        return(Enter(target, visitedNodes));
                    }
                    else
                    {
                        AstTryCatchBlock dstTryBlock = dstTryBlocks[i];
                        // Check that the goto points to the start
                        AstTryCatchBlock current = dstTryBlock;
                        while (current != null)
                        {
                            foreach (AstNode n in current.TryBlock.Body)
                            {
                                if (n is AstLabel)
                                {
                                    if (n == target)
                                    {
                                        return(dstTryBlock);
                                    }
                                }
                                else if (!n.Match(AstCode.Nop))
                                {
                                    current = n as AstTryCatchBlock;
                                    break;
                                }
                            }
                        }
                        return(null);
                    }
                }
                else if (expr.Code == AstCode.Nop)
                {
                    return(Exit(expr, visitedNodes));
                }
                else if (expr.Code == AstCode.LoopOrSwitchBreak)
                {
                    AstNode breakBlock = GetParents(expr).First(n => n is AstSwitch);
                    return(Exit(breakBlock, new HashSet <AstNode>()
                    {
                        expr
                    }));
                }
                else
                {
                    return(expr);
                }
            }

            AstBlock block = node as AstBlock;

            if (block != null)
            {
                if (block.EntryGoto != null)
                {
                    return(Enter(block.EntryGoto, visitedNodes));
                }
                else if (block.Body.Count > 0)
                {
                    return(Enter(block.Body[0], visitedNodes));
                }
                else
                {
                    return(Exit(block, visitedNodes));
                }
            }

            AstTryCatchBlock tryCatch = node as AstTryCatchBlock;

            if (tryCatch != null)
            {
                return(tryCatch);
            }

            AstSwitch astSwitch = node as AstSwitch;

            if (astSwitch != null)
            {
                return(astSwitch.Condition);
            }

            throw new NotSupportedException(node.GetType().ToString());
        }
コード例 #8
0
        private static void FailsafeInterlockedUsingLocking(XFieldReference field, AstExpression expr,
                                                            AstExpression targetExpr, AstBlock block, int idx,
                                                            AssemblyCompiler compiler, MethodSource currentMethod)
        {
            if (currentMethod.IsDotNet && !currentMethod.ILMethod.DeclaringType.HasSuppressMessageAttribute("InterlockedFallback"))
            {
                bool isStatic = field != null && field.Resolve().IsStatic;

                DLog.Warning(DContext.CompilerCodeGenerator,
                             "Emulating Interlocked call using failsafe locking mechanism in {0}{1}. Consider using AtomicXXX classes instead. You can suppress this message with an [SuppressMessage(\"dot42\", \"InterlockedFallback\")] attribute on the class.",
                             currentMethod.Method.FullName, !isStatic ? "" : " because a static field is referenced");
            }

            // replace with:
            //    Monitor.Enter();
            //    try { <original expression> } finally { Monitor.Exit(); }
            //
            // note that the lock is larger than it has to be, since it also sourrounds
            // the parameter evaluation.
            // It must sourround the storing and loading of the reference parameters though.
            var typeSystem = compiler.Module.TypeSystem;

            var monitorType = compiler.GetDot42InternalType("System.Threading", "Monitor");
            var enterMethod = monitorType.Resolve().Methods.Single(p => p.Name == "Enter");
            var exitMethod  = monitorType.Resolve().Methods.Single(p => p.Name == "Exit");

            AstExpression loadLockTarget = null;

            if (field != null)
            {
                if (field.Resolve().IsStatic)
                {
                    // lock on the field's class typedef.
                    // but always the element type, not on a generic instance (until Dot42 implements proper generic static field handling)
                    loadLockTarget = new AstExpression(expr.SourceLocation, AstCode.LdClass, field.DeclaringType.GetElementType())
                                     .SetType(typeSystem.Type);
                }
                else
                {
                    // lock on the fields object
                    loadLockTarget = targetExpr.Arguments[0];
                }
            }

            if (loadLockTarget == null)
            {
                // something went wrong. use a global lock.
                DLog.Warning(DContext.CompilerCodeGenerator, "unable to infer target of Interlocked call. using global lock.");
                var interlockedType = compiler.GetDot42InternalType("System.Threading", "Interlocked");
                loadLockTarget = new AstExpression(expr.SourceLocation, AstCode.LdClass, interlockedType)
                                 .SetType(typeSystem.Type);
            }

            var lockVar = new AstGeneratedVariable("lockTarget$", "")
            {
                Type = typeSystem.Object
            };
            var storeLockVar = new AstExpression(expr.SourceLocation, AstCode.Stloc, lockVar, loadLockTarget);
            var loadLockVar  = new AstExpression(expr.SourceLocation, AstCode.Ldloc, lockVar);
            var enterCall    = new AstExpression(expr.SourceLocation, AstCode.Call, enterMethod, storeLockVar);

            var replacementBlock = new AstBlock(expr.SourceLocation);

            replacementBlock.Body.Add(enterCall);

            var tryCatch = new AstTryCatchBlock(expr.SourceLocation)
            {
                TryBlock     = new AstBlock(expr.SourceLocation, expr),
                FinallyBlock = new AstBlock(expr.SourceLocation,
                                            new AstExpression(block.SourceLocation, AstCode.Call, exitMethod, loadLockVar))
            };

            replacementBlock.Body.Add(tryCatch);

            if (block.EntryGoto == expr)
            {
                block.EntryGoto = enterCall;
            }

            block.Body[idx] = replacementBlock;
        }
コード例 #9
0
        private static void FailsafeInterlockedUsingLocking(XFieldReference field, AstExpression expr, 
                                                            AstExpression targetExpr, AstBlock block, int idx, 
                                                            AssemblyCompiler compiler, MethodSource currentMethod)
        {
            if (currentMethod.IsDotNet && !currentMethod.ILMethod.DeclaringType.HasSuppressMessageAttribute("InterlockedFallback"))
            {
                bool isStatic = field != null && field.Resolve().IsStatic;

                DLog.Warning(DContext.CompilerCodeGenerator,
                    "Emulating Interlocked call using failsafe locking mechanism in {0}{1}. Consider using AtomicXXX classes instead. You can suppress this message with an [SuppressMessage(\"dot42\", \"InterlockedFallback\")] attribute on the class.",
                    currentMethod.Method.FullName, !isStatic ? "" : " because a static field is referenced");
            }

            // replace with:
            //    Monitor.Enter();
            //    try { <original expression> } finally { Monitor.Exit(); } 
            //   
            // note that the lock is larger than it has to be, since it also sourrounds
            // the parameter evaluation.
            // It must sourround the storing and loading of the reference parameters though.
            var typeSystem = compiler.Module.TypeSystem;

            var monitorType = compiler.GetDot42InternalType("System.Threading", "Monitor");
            var enterMethod = monitorType.Resolve().Methods.Single(p => p.Name == "Enter");
            var exitMethod = monitorType.Resolve().Methods.Single(p => p.Name == "Exit");

            AstExpression loadLockTarget = null;
            
            if (field != null)
            {
                if (field.Resolve().IsStatic)
                {
                    // lock on the field's class typedef.
                    // but always the element type, not on a generic instance (until Dot42 implements proper generic static field handling)
                    loadLockTarget = new AstExpression(expr.SourceLocation, AstCode.LdClass, field.DeclaringType.GetElementType())
                        .SetType(typeSystem.Type);
                }
                else
                {
                    // lock on the fields object
                    loadLockTarget = targetExpr.Arguments[0];
                }
            }

            if (loadLockTarget == null)
            {
                // something went wrong. use a global lock.
                DLog.Warning(DContext.CompilerCodeGenerator, "unable to infer target of Interlocked call. using global lock.");
                var interlockedType = compiler.GetDot42InternalType("System.Threading", "Interlocked");
                loadLockTarget = new AstExpression(expr.SourceLocation, AstCode.LdClass, interlockedType)
                                        .SetType(typeSystem.Type);
            }

            var lockVar = new AstGeneratedVariable("lockTarget$", "") {Type = typeSystem.Object};
            var storeLockVar = new AstExpression(expr.SourceLocation, AstCode.Stloc, lockVar, loadLockTarget);
            var loadLockVar = new AstExpression(expr.SourceLocation, AstCode.Ldloc, lockVar);
            var enterCall = new AstExpression(expr.SourceLocation, AstCode.Call, enterMethod, storeLockVar);

            var replacementBlock = new AstBlock(expr.SourceLocation);
            replacementBlock.Body.Add(enterCall);

            var tryCatch = new AstTryCatchBlock(expr.SourceLocation)
            {
                TryBlock = new AstBlock(expr.SourceLocation, expr),
                FinallyBlock = new AstBlock(expr.SourceLocation,
                    new AstExpression(block.SourceLocation, AstCode.Call, exitMethod, loadLockVar))
            };

            replacementBlock.Body.Add(tryCatch);

            if (block.EntryGoto == expr)
                block.EntryGoto = enterCall;

            block.Body[idx] = replacementBlock;
        }
コード例 #10
0
ファイル: AstBuilder.cs プロジェクト: Xtremrules/dot42
        /// <summary>
        /// Convert the given set of bytecodes to an Ast node list.
        /// Split exception handlers into Ast try/catch blocks.
        /// </summary>
        private List<AstNode> ConvertToAst(List<ByteCode> body, HashSet<ExceptionHandler> ehs, List<ByteCodeBlock> blockStarts, int nestingLevel, Dictionary<int, ByteCode> offset2ByteCode)
        {
            var ast = new List<AstNode>();

            // Split body in blocks

            while (ehs.Any())
            {
                var tryCatchBlock = new AstTryCatchBlock(null);

                // Find the first and widest scope
                var tryStart = ehs.Min(eh => eh.StartPc);
                var tryEnd = ehs.Where(eh => eh.StartPc == tryStart).Max(eh => eh.EndPc);
                var handlers = ehs.Where(eh => (eh.StartPc == tryStart) && (eh.EndPc == tryEnd)).OrderBy(eh => eh.HandlerPc).ToList();

                // Remember that any part of the body migt have been removed due to unreachability

                // Cut all instructions up to the try block
                {
                    var tryStartIdx = 0;
                    while ((tryStartIdx < body.Count) && (body[tryStartIdx].Offset < tryStart))
                    {
                        tryStartIdx++;
                    }
                    if (tryStartIdx > 0)
                    {
                        ast.AddRange(ConvertRangeToAst(body.CutRange(0, tryStartIdx)));
                        // Make sure the block before the try block ends with an unconditional control flow
                        AddUnconditionalBranchToNext(ast, body, 0);
                    }
                }

                // Cut the try block
                {
                    var nestedEHs = new HashSet<ExceptionHandler>(
                        ehs.Where(eh => ((tryStart <= eh.StartPc) && (eh.EndPc < tryEnd)) || ((tryStart < eh.StartPc) && (eh.EndPc <= tryEnd))));
                    ehs.ExceptWith(nestedEHs);
                    var tryEndIdx = 0;
                    while ((tryEndIdx < body.Count) && (body[tryEndIdx].Offset < tryEnd))
                    {
                        tryEndIdx++;
                    }
                    var converted = ConvertToAst(body.CutRange(0, tryEndIdx), nestedEHs, blockStarts, nestingLevel + 1, offset2ByteCode);
                    tryCatchBlock.TryBlock = new AstBlock(converted.Select(x => x.SourceLocation).FirstOrDefault(), converted);
                    // Make sure the try block ends with an unconditional control flow
                    AddUnconditionalBranchToNext(tryCatchBlock.TryBlock.Body, body, 0);
                }

                // Cut all handlers
                tryCatchBlock.CatchBlocks.Clear();
                foreach (var iterator in handlers)
                {
                    var eh = iterator;
                    var handler = offset2ByteCode[eh.HandlerPc]; // body.First(x => x.Offset == eh.HandlerPc);
                    var catchType = eh.IsCatchAll ? typeSystem.Object : AsTypeReference(eh.CatchType, XTypeUsageFlags.CatchType);
                    var catchBlock = new AstTryCatchBlock.CatchBlock(handler.SourceLocation, tryCatchBlock)
                    {
                        ExceptionType = catchType,
                        Body = new List<AstNode>()
                    };

                    // Create catch "body" (actually a jump to the handler)
                    // Handle the automatically pushed exception on the stack
                    var ldexception = ldexceptions[eh];
                    catchBlock.Body.Add(new AstExpression(handler.SourceLocation, AstCode.Br, handler.Label(true)));
                    if (ldexception.StoreTo == null || ldexception.StoreTo.Count == 0)
                    {
                        // Exception is not used
                        catchBlock.ExceptionVariable = null;
                    }
                    else if (ldexception.StoreTo.Count == 1)
                    {
                        /*var first = catchBlock.Body.FirstOrDefault() as AstExpression;
                        if (first != null &&
                            first.Code == AstCode.Pop &&
                            first.Arguments[0].Code == AstCode.Ldloc &&
                            first.Arguments[0].Operand == ldexception.StoreTo[0])
                        {
                            // The exception is just poped - optimize it all away;
                            catchBlock.ExceptionVariable = new AstGeneratedVariable("ex_" + eh.HandlerPc.ToString("X2"));
                            catchBlock.Body.RemoveAt(0);
                        }
                        else*/
                        {
                            catchBlock.ExceptionVariable = ldexception.StoreTo[0];
                        }
                    }
                    else
                    {
                        var exTemp = new AstGeneratedVariable("ex_" + eh.HandlerPc.ToString("X2"), null);
                        catchBlock.ExceptionVariable = exTemp;
                        foreach (var storeTo in ldexception.StoreTo)
                        {
                            catchBlock.Body.Insert(0, new AstExpression(catchBlock.SourceLocation, AstCode.Stloc, storeTo, new AstExpression(catchBlock.SourceLocation, AstCode.Ldloc, exTemp)));
                        }
                    }
                    tryCatchBlock.CatchBlocks.Add(catchBlock);
                }

                ehs.ExceptWith(handlers);

                ast.Add(tryCatchBlock);
            }

            // Add whatever is left
            ast.AddRange(ConvertRangeToAst(body));

            return ast;
        }
コード例 #11
0
        /// <summary>
        /// Convert the given set of bytecodes to an Ast node list.
        /// Split exception handlers into Ast try/catch blocks.
        /// </summary>
        private List <AstNode> ConvertToAst(List <ByteCode> body, HashSet <ExceptionHandler> ehs, List <ByteCodeBlock> blockStarts, int nestingLevel, Dictionary <int, ByteCode> offset2ByteCode)
        {
            var ast = new List <AstNode>();

            // Split body in blocks

            while (ehs.Any())
            {
                var tryCatchBlock = new AstTryCatchBlock(null);

                // Find the first and widest scope
                var tryStart = ehs.Min(eh => eh.StartPc);
                var tryEnd   = ehs.Where(eh => eh.StartPc == tryStart).Max(eh => eh.EndPc);
                var handlers = ehs.Where(eh => (eh.StartPc == tryStart) && (eh.EndPc == tryEnd)).OrderBy(eh => eh.HandlerPc).ToList();

                // Remember that any part of the body migt have been removed due to unreachability

                // Cut all instructions up to the try block
                {
                    var tryStartIdx = 0;
                    while ((tryStartIdx < body.Count) && (body[tryStartIdx].Offset < tryStart))
                    {
                        tryStartIdx++;
                    }
                    if (tryStartIdx > 0)
                    {
                        ast.AddRange(ConvertRangeToAst(body.CutRange(0, tryStartIdx)));
                        // Make sure the block before the try block ends with an unconditional control flow
                        AddUnconditionalBranchToNext(ast, body, 0);
                    }
                }

                // Cut the try block
                {
                    var nestedEHs = new HashSet <ExceptionHandler>(
                        ehs.Where(eh => ((tryStart <= eh.StartPc) && (eh.EndPc < tryEnd)) || ((tryStart < eh.StartPc) && (eh.EndPc <= tryEnd))));
                    ehs.ExceptWith(nestedEHs);
                    var tryEndIdx = 0;
                    while ((tryEndIdx < body.Count) && (body[tryEndIdx].Offset < tryEnd))
                    {
                        tryEndIdx++;
                    }
                    var converted = ConvertToAst(body.CutRange(0, tryEndIdx), nestedEHs, blockStarts, nestingLevel + 1, offset2ByteCode);
                    tryCatchBlock.TryBlock = new AstBlock(converted.Select(x => x.SourceLocation).FirstOrDefault(), converted);
                    // Make sure the try block ends with an unconditional control flow
                    AddUnconditionalBranchToNext(tryCatchBlock.TryBlock.Body, body, 0);
                }

                // Cut all handlers
                tryCatchBlock.CatchBlocks.Clear();
                foreach (var iterator in handlers)
                {
                    var eh         = iterator;
                    var handler    = offset2ByteCode[eh.HandlerPc]; // body.First(x => x.Offset == eh.HandlerPc);
                    var catchType  = eh.IsCatchAll ? typeSystem.Object : AsTypeReference(eh.CatchType, XTypeUsageFlags.CatchType);
                    var catchBlock = new AstTryCatchBlock.CatchBlock(handler.SourceLocation, tryCatchBlock)
                    {
                        ExceptionType = catchType,
                        Body          = new List <AstNode>()
                    };

                    // Create catch "body" (actually a jump to the handler)
                    // Handle the automatically pushed exception on the stack
                    var ldexception = ldexceptions[eh];
                    catchBlock.Body.Add(new AstExpression(handler.SourceLocation, AstCode.Br, handler.Label(true)));
                    if (ldexception.StoreTo == null || ldexception.StoreTo.Count == 0)
                    {
                        // Exception is not used
                        catchBlock.ExceptionVariable = null;
                    }
                    else if (ldexception.StoreTo.Count == 1)
                    {
                        /*var first = catchBlock.Body.FirstOrDefault() as AstExpression;
                         * if (first != null &&
                         *  first.Code == AstCode.Pop &&
                         *  first.Arguments[0].Code == AstCode.Ldloc &&
                         *  first.Arguments[0].Operand == ldexception.StoreTo[0])
                         * {
                         *  // The exception is just poped - optimize it all away;
                         *  catchBlock.ExceptionVariable = new AstGeneratedVariable("ex_" + eh.HandlerPc.ToString("X2"));
                         *  catchBlock.Body.RemoveAt(0);
                         * }
                         * else*/
                        {
                            catchBlock.ExceptionVariable = ldexception.StoreTo[0];
                        }
                    }
                    else
                    {
                        var exTemp = new AstGeneratedVariable("ex_" + eh.HandlerPc.ToString("X2"), null);
                        catchBlock.ExceptionVariable = exTemp;
                        foreach (var storeTo in ldexception.StoreTo)
                        {
                            catchBlock.Body.Insert(0, new AstExpression(catchBlock.SourceLocation, AstCode.Stloc, storeTo, new AstExpression(catchBlock.SourceLocation, AstCode.Ldloc, exTemp)));
                        }
                    }
                    tryCatchBlock.CatchBlocks.Add(catchBlock);
                }

                ehs.ExceptWith(handlers);

                ast.Add(tryCatchBlock);
            }

            // Add whatever is left
            ast.AddRange(ConvertRangeToAst(body));

            return(ast);
        }
コード例 #12
0
ファイル: AstBuilder.cs プロジェクト: yuva2achieve/dot42
        List <AstNode> ConvertToAst(List <ByteCode> body, HashSet <ExceptionHandler> ehs)
        {
            var ast = new List <AstNode>();

            while (ehs.Any())
            {
                var tryCatchBlock = new AstTryCatchBlock(null);

                // Find the first and widest scope
                var tryStart = ehs.Min(eh => eh.TryStart.Offset);
                var tryEnd   = ehs.Where(eh => eh.TryStart.Offset == tryStart).Max(eh => eh.TryEnd.Offset);
                var handlers = ehs.Where(eh => eh.TryStart.Offset == tryStart && eh.TryEnd.Offset == tryEnd).OrderBy(eh => eh.TryStart.Offset).ToList();

                // Remember that any part of the body migt have been removed due to unreachability

                // Cut all instructions up to the try block
                {
                    var tryStartIdx = 0;
                    while (tryStartIdx < body.Count && body[tryStartIdx].Offset < tryStart)
                    {
                        tryStartIdx++;
                    }
                    ast.AddRange(ConvertToAst(CollectionExtensions.CutRange(body, 0, tryStartIdx)));
                }

                // Cut the try block
                {
                    var nestedEHs = new HashSet <ExceptionHandler>(
                        ehs.Where(eh => (tryStart <= eh.TryStart.Offset && eh.TryEnd.Offset < tryEnd) || (tryStart < eh.TryStart.Offset && eh.TryEnd.Offset <= tryEnd)));
                    ehs.ExceptWith(nestedEHs);
                    var tryEndIdx = 0;
                    while (tryEndIdx < body.Count && body[tryEndIdx].Offset < tryEnd)
                    {
                        tryEndIdx++;
                    }
                    var converted = ConvertToAst(CollectionExtensions.CutRange(body, 0, tryEndIdx), nestedEHs);
                    tryCatchBlock.TryBlock = new AstBlock(converted.Select(x => x.SourceLocation).FirstOrDefault(), converted);
                }

                // Cut all handlers
                tryCatchBlock.CatchBlocks.Clear();
                foreach (var eh in handlers)
                {
                    var handlerEndOffset = eh.HandlerEnd == null ? methodDef.Body.CodeSize : eh.HandlerEnd.Offset;
                    var startIdx         = 0;
                    while (startIdx < body.Count && body[startIdx].Offset < eh.HandlerStart.Offset)
                    {
                        startIdx++;
                    }
                    var endIdx = 0;
                    while (endIdx < body.Count && body[endIdx].Offset < handlerEndOffset)
                    {
                        endIdx++;
                    }
                    var nestedEHs = new HashSet <ExceptionHandler>(ehs.Where(e => (eh.HandlerStart.Offset <= e.TryStart.Offset && e.TryEnd.Offset < handlerEndOffset) || (eh.HandlerStart.Offset < e.TryStart.Offset && e.TryEnd.Offset <= handlerEndOffset)));
                    ehs.ExceptWith(nestedEHs);
                    var handlerAst = ConvertToAst(CollectionExtensions.CutRange(body, startIdx, endIdx - startIdx), nestedEHs);
                    if (eh.HandlerType == ExceptionHandlerType.Catch)
                    {
                        var catchType  = eh.CatchType.IsSystemObject() ? module.TypeSystem.Exception : XBuilder.AsTypeReference(module, eh.CatchType);
                        var catchBlock = new AstTryCatchBlock.CatchBlock(handlerAst.Select(x => x.SourceLocation).FirstOrDefault(), tryCatchBlock)
                        {
                            ExceptionType = catchType,
                            Body          = handlerAst
                        };
                        // Handle the automatically pushed exception on the stack
                        var ldexception = ldexceptions[eh];
                        if (ldexception.StoreTo == null || ldexception.StoreTo.Count == 0)
                        {
                            // Exception is not used
                            catchBlock.ExceptionVariable = null;
                        }
                        else if (ldexception.StoreTo.Count == 1)
                        {
                            var first = catchBlock.Body[0] as AstExpression;
                            if (first != null &&
                                first.Code == AstCode.Pop &&
                                first.Arguments[0].Code == AstCode.Ldloc &&
                                first.Arguments[0].Operand == ldexception.StoreTo[0])
                            {
                                // The exception is just poped - optimize it all away;
                                if (context.Settings.AlwaysGenerateExceptionVariableForCatchBlocks)
                                {
                                    catchBlock.ExceptionVariable = new AstGeneratedVariable("ex_" + eh.HandlerStart.Offset.ToString("X2"), null);
                                }
                                else
                                {
                                    catchBlock.ExceptionVariable = null;
                                }
                                catchBlock.Body.RemoveAt(0);
                            }
                            else
                            {
                                catchBlock.ExceptionVariable = ldexception.StoreTo[0];
                            }
                        }
                        else
                        {
                            var exTemp = new AstGeneratedVariable("ex_" + eh.HandlerStart.Offset.ToString("X2"), null);
                            catchBlock.ExceptionVariable = exTemp;
                            foreach (var storeTo in ldexception.StoreTo)
                            {
                                catchBlock.Body.Insert(0, new AstExpression(catchBlock.SourceLocation, AstCode.Stloc, storeTo, new AstExpression(catchBlock.SourceLocation, AstCode.Ldloc, exTemp)));
                            }
                        }
                        tryCatchBlock.CatchBlocks.Add(catchBlock);
                    }
                    else if (eh.HandlerType == ExceptionHandlerType.Finally)
                    {
                        tryCatchBlock.FinallyBlock = new AstBlock(handlerAst);
                    }
                    else if (eh.HandlerType == ExceptionHandlerType.Fault)
                    {
                        tryCatchBlock.FaultBlock = new AstBlock(handlerAst);
                    }
                    else
                    {
                        // TODO: ExceptionHandlerType.Filter
                    }
                }

                ehs.ExceptWith(handlers);

                ast.Add(tryCatchBlock);
            }

            // Add whatever is left
            ast.AddRange(ConvertToAst(body));

            return(ast);
        }
コード例 #13
0
        /// <summary>
        /// Create code for given catch block
        /// </summary>
        public override RLRange Visit(AstTryCatchBlock.CatchBlock node, AstNode parent)
        {
            // Allocate exception register
            var r = frame.AllocateTemp(node.ExceptionType.GetReference(targetPackage));
            var first = this.Add(node.SourceLocation, RCode.Move_exception, r);
            var last = first;

            currentExceptionRegister.Push(r);

            // Store exception in exception variable (if any)
            if (node.ExceptionVariable != null)
            {
                var rVar = frame.GetArgument(node.ExceptionVariable);
                this.Add(node.SourceLocation, RCode.Move_object, rVar, r);
            }

            // Generate code for actual catch block.
            var result = Visit((AstBlock)node, parent);
            if (result != null)
            {
                last = result.Last;
            }

            currentExceptionRegister.Pop();

            // Combine result
            return new RLRange(first, last, null);
        }
コード例 #14
0
        /// <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);
        }
コード例 #15
0
 public virtual TReturn Visit(AstTryCatchBlock node, TData data)
 {
     return(default(TReturn));
 }
コード例 #16
0
ファイル: AstBuilder.cs プロジェクト: Xtremrules/dot42
        List<AstNode> ConvertToAst(List<ByteCode> body, HashSet<ExceptionHandler> ehs)
        {
            var ast = new List<AstNode>();

            while (ehs.Any())
            {
                var tryCatchBlock = new AstTryCatchBlock(null);

                // Find the first and widest scope
                var tryStart = ehs.Min(eh => eh.TryStart.Offset);
                var tryEnd = ehs.Where(eh => eh.TryStart.Offset == tryStart).Max(eh => eh.TryEnd.Offset);
                var handlers = ehs.Where(eh => eh.TryStart.Offset == tryStart && eh.TryEnd.Offset == tryEnd).OrderBy(eh => eh.TryStart.Offset).ToList();

                // Remember that any part of the body migt have been removed due to unreachability

                // Cut all instructions up to the try block
                {
                    var tryStartIdx = 0;
                    while (tryStartIdx < body.Count && body[tryStartIdx].Offset < tryStart) tryStartIdx++;
                    ast.AddRange(ConvertToAst(CollectionExtensions.CutRange(body, 0, tryStartIdx)));
                }

                // Cut the try block
                {
                    var nestedEHs = new HashSet<ExceptionHandler>(
                        ehs.Where(eh => (tryStart <= eh.TryStart.Offset && eh.TryEnd.Offset < tryEnd) || (tryStart < eh.TryStart.Offset && eh.TryEnd.Offset <= tryEnd)));
                    ehs.ExceptWith(nestedEHs);
                    var tryEndIdx = 0;
                    while (tryEndIdx < body.Count && body[tryEndIdx].Offset < tryEnd) tryEndIdx++;
                    var converted = ConvertToAst(CollectionExtensions.CutRange(body, 0, tryEndIdx), nestedEHs);
                    tryCatchBlock.TryBlock = new AstBlock(converted.Select(x => x.SourceLocation).FirstOrDefault(), converted);
                }

                // Cut all handlers
                tryCatchBlock.CatchBlocks.Clear();
                foreach (var eh in handlers)
                {
                    var handlerEndOffset = eh.HandlerEnd == null ? methodDef.Body.CodeSize : eh.HandlerEnd.Offset;
                    var startIdx = 0;
                    while (startIdx < body.Count && body[startIdx].Offset < eh.HandlerStart.Offset) startIdx++;
                    var endIdx = 0;
                    while (endIdx < body.Count && body[endIdx].Offset < handlerEndOffset) endIdx++;
                    var nestedEHs = new HashSet<ExceptionHandler>(ehs.Where(e => (eh.HandlerStart.Offset <= e.TryStart.Offset && e.TryEnd.Offset < handlerEndOffset) || (eh.HandlerStart.Offset < e.TryStart.Offset && e.TryEnd.Offset <= handlerEndOffset)));
                    ehs.ExceptWith(nestedEHs);
                    var handlerAst = ConvertToAst(CollectionExtensions.CutRange(body, startIdx, endIdx - startIdx), nestedEHs);
                    if (eh.HandlerType == ExceptionHandlerType.Catch)
                    {
                        var catchType = eh.CatchType.IsSystemObject() ? module.TypeSystem.Exception : XBuilder.AsTypeReference(module, eh.CatchType);
                        var catchBlock = new AstTryCatchBlock.CatchBlock(handlerAst.Select(x => x.SourceLocation).FirstOrDefault(), tryCatchBlock)
                        {
                            ExceptionType = catchType,
                            Body = handlerAst
                        };
                        // Handle the automatically pushed exception on the stack
                        var ldexception = ldexceptions[eh];
                        if (ldexception.StoreTo == null || ldexception.StoreTo.Count == 0)
                        {
                            // Exception is not used
                            catchBlock.ExceptionVariable = null;
                        }
                        else if (ldexception.StoreTo.Count == 1)
                        {
                            var first = catchBlock.Body[0] as AstExpression;
                            if (first != null &&
                                first.Code == AstCode.Pop &&
                                first.Arguments[0].Code == AstCode.Ldloc &&
                                first.Arguments[0].Operand == ldexception.StoreTo[0])
                            {
                                // The exception is just poped - optimize it all away;
                                if (context.Settings.AlwaysGenerateExceptionVariableForCatchBlocks)
                                    catchBlock.ExceptionVariable = new AstGeneratedVariable("ex_" + eh.HandlerStart.Offset.ToString("X2"), null);
                                else
                                    catchBlock.ExceptionVariable = null;
                                catchBlock.Body.RemoveAt(0);
                            }
                            else
                            {
                                catchBlock.ExceptionVariable = ldexception.StoreTo[0];
                            }
                        }
                        else
                        {
                            var exTemp = new AstGeneratedVariable("ex_" + eh.HandlerStart.Offset.ToString("X2"), null);
                            catchBlock.ExceptionVariable = exTemp;
                            foreach (var storeTo in ldexception.StoreTo)
                            {
                                catchBlock.Body.Insert(0, new AstExpression(catchBlock.SourceLocation, AstCode.Stloc, storeTo, new AstExpression(catchBlock.SourceLocation, AstCode.Ldloc, exTemp)));
                            }
                        }
                        tryCatchBlock.CatchBlocks.Add(catchBlock);
                    }
                    else if (eh.HandlerType == ExceptionHandlerType.Finally)
                    {
                        tryCatchBlock.FinallyBlock = new AstBlock(handlerAst);
                    }
                    else if (eh.HandlerType == ExceptionHandlerType.Fault)
                    {
                        tryCatchBlock.FaultBlock = new AstBlock(handlerAst);
                    }
                    else
                    {
                        // TODO: ExceptionHandlerType.Filter
                    }
                }

                ehs.ExceptWith(handlers);

                ast.Add(tryCatchBlock);
            }

            // Add whatever is left
            ast.AddRange(ConvertToAst(body));

            return ast;
        }
コード例 #17
0
        /// <summary>
        /// Create try/catch/finally/fault block
        /// </summary>
        public override RLRange Visit(AstTryCatchBlock node, AstNode parent)
        {
            var handler = new ExceptionHandler();

            // Setup instruction after my node.
            var last = new Instruction(RCode.Nop);

            // Emit try block
            handler.TryStart = this.Add(node.SourceLocation, RCode.Nop);
            node.TryBlock.AcceptOrDefault(this, node);
            handler.TryEnd = this.Add(node.SourceLocation, RCode.Nop);

            // Emit finally code
            if (node.FinallyBlock != null)
            {
                using (labelManager.CreateContext())
                {
                    node.FinallyBlock.Accept(this, node);
                    node.FinallyBlock.CleanResults();
                }
            }

            // Jump to end
            this.Add(node.SourceLocation, RCode.Goto, last);

            // Replace returns in the try block
            InsertFinallyBeforeReturns(handler.TryStart, handler.TryEnd, node);

            // Prepare for finally handler
            var finallyStart = (node.FinallyBlock != null) ? new Instruction(RCode.Move_exception) : null;

            // 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);
                var catchEnd = this.Add(catchBlock.SourceLocation, RCode.Nop);
                c.Instruction = catchStart;

                // Add finally block
                if (node.FinallyBlock != null)
                {
                    // Emit finally code
                    using (labelManager.CreateContext())
                    {
                        node.FinallyBlock.Accept(this, node);
                        node.FinallyBlock.CleanResults();
                    }

                    // Write catch block in finally handler
                    var catchFinallyHandler = new ExceptionHandler {
                        TryStart = catchStart, TryEnd = catchEnd, CatchAll = finallyStart
                    };
                    body.Exceptions.Add(catchFinallyHandler);
                }

                // Jump to end
                this.Add(node.SourceLocation, RCode.Goto, last);

                // Replace returns in the catch block
                InsertFinallyBeforeReturns(catchStart, catchEnd, node);
            }

            // 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);
                var catchEnd = this.Add(catchAllBlock.SourceLocation, RCode.Nop);
                handler.CatchAll = catchStart;

                // Add finally block
                if (node.FinallyBlock != null)
                {
                    // Emit finally code
                    using (labelManager.CreateContext())
                    {
                        node.FinallyBlock.Accept(this, node);
                        node.FinallyBlock.CleanResults();
                    }

                    // Write catch block in finally handler
                    var catchFinallyHandler = new ExceptionHandler {
                        TryStart = catchStart, TryEnd = catchEnd, CatchAll = finallyStart
                    };
                    body.Exceptions.Add(catchFinallyHandler);
                }

                // Jump to end
                this.Add(node.SourceLocation, RCode.Goto, last);

                // Replace returns in the catch block
                InsertFinallyBeforeReturns(catchStart, catchEnd, node);
            }

            // Emit finally block (if any)
            if (node.FinallyBlock != null)
            {
                // Create finally handler
                var finallyHandler = new ExceptionHandler {
                    TryStart = handler.TryStart, TryEnd = handler.TryEnd /*body.Instructions.Last()*/, CatchAll = finallyStart
                };
                body.Exceptions.Add(finallyHandler);

                // Update move_exception instructions
                var r = frame.AllocateTemp(new ClassReference("java.lang.Throwable"));
                finallyStart.Registers.Add(r);
                instructions.Add(finallyStart);

                // Emit finally code
                using (labelManager.CreateContext())
                {
                    node.FinallyBlock.Accept(this, node);
                    node.FinallyBlock.CleanResults();
                }

                // Re-throw exception
                this.Add(node.SourceLocation, RCode.Throw, r);
            }

            // Add end
            instructions.Add(last);

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

            return(new RLRange(handler.TryStart, last, null));
        }