/// <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)); }
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; }
public virtual TReturn Visit(AstTryCatchBlock.CatchBlock node, TData data) { return(Visit((AstBlock)node, data)); }
/// <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; }
/// <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); }
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); }
/// <summary> /// Is the given catch block a catch all? /// </summary> public static bool IsCatchAll(this AstTryCatchBlock.CatchBlock catchBlock) { return(catchBlock.ExceptionType.IsSystemObject()); }