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(); } } }
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)); }
public FinallyBlockState(FinallyBlockState outerBlock, int depth) { Depth = depth; OuterFinallyBlock = outerBlock; }
/// <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)); }
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(); } }
/// <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); }
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); }