Skip to content

Commit

Permalink
Add filter clause translation
Browse files Browse the repository at this point in the history
  • Loading branch information
yck1509 committed Apr 9, 2015
1 parent ae879ca commit 20c40b0
Show file tree
Hide file tree
Showing 7 changed files with 223 additions and 59 deletions.
4 changes: 3 additions & 1 deletion ICSharpCode.Decompiler/Ast/Annotations.cs
Expand Up @@ -18,8 +18,10 @@ public TypeInformation(TypeSig inferredType, TypeSig expectedType)
this.ExpectedType = expectedType;
}
}

public class LdTokenAnnotation {}

public class FilterClauseAnnotation { }

/// <summary>
/// Annotation that is applied to the body expression of an Expression.Lambda() call.
Expand Down
27 changes: 16 additions & 11 deletions ICSharpCode.Decompiler/Ast/AstMethodBodyBuilder.cs
Expand Up @@ -200,18 +200,22 @@ select val
var tryCatchStmt = new TryCatchStatement();
tryCatchStmt.TryBlock = TransformBlock(tryCatchNode.TryBlock);
foreach (var catchClause in tryCatchNode.CatchBlocks) {
if (catchClause.ExceptionVariable == null
&& (catchClause.ExceptionType == null || catchClause.ExceptionType.IsCorLibType("System", "Object")))
CatchClause clause = new CatchClause { Body = TransformBlock(catchClause) };
if (catchClause.ExceptionVariable != null
|| (catchClause.ExceptionType != null && !catchClause.ExceptionType.IsCorLibType("System", "Object")))
{
tryCatchStmt.CatchClauses.Add(new CatchClause { Body = TransformBlock(catchClause) });
} else {
tryCatchStmt.CatchClauses.Add(
new CatchClause {
Type = AstBuilder.ConvertType(catchClause.ExceptionType),
VariableName = catchClause.ExceptionVariable == null ? null : catchClause.ExceptionVariable.Name,
Body = TransformBlock(catchClause)
}.WithAnnotation(catchClause.ExceptionVariable));
clause.Type = AstBuilder.ConvertType(catchClause.ExceptionType);
clause.VariableName = catchClause.ExceptionVariable == null ? null : catchClause.ExceptionVariable.Name;
clause.AddAnnotation(catchClause.ExceptionVariable);
}
if (catchClause.FilterBlock != null) {
clause.Filter = new FilterClause {
Expression = new LambdaExpression {
Body = TransformBlock(catchClause.FilterBlock)
}.WithAnnotation(new FilterClauseAnnotation())
};
}
tryCatchStmt.CatchClauses.Add(clause);
}
if (tryCatchNode.FinallyBlock != null)
tryCatchStmt.FinallyBlock = TransformBlock(tryCatchNode.FinallyBlock);
Expand Down Expand Up @@ -618,7 +622,8 @@ AstNode TransformByteCode(ILExpression byteCode)
case ILCode.Cpblk: return InlineAssembly(byteCode, args);
case ILCode.Cpobj: return InlineAssembly(byteCode, args);
case ILCode.Dup: return arg1;
case ILCode.Endfilter: return InlineAssembly(byteCode, args);
case ILCode.Endfilter:
return new ReturnStatement { Expression = arg1 };
case ILCode.Endfinally: return null;
case ILCode.Initblk: return InlineAssembly(byteCode, args);
case ILCode.Initobj: return InlineAssembly(byteCode, args);
Expand Down
30 changes: 30 additions & 0 deletions ICSharpCode.Decompiler/Ast/Transforms/PatternStatementTransform.cs
Expand Up @@ -136,6 +136,11 @@ public override AstNode VisitTryCatchStatement(TryCatchStatement tryCatchStateme
{
return TransformTryCatchFinally(tryCatchStatement) ?? base.VisitTryCatchStatement(tryCatchStatement, data);
}

public override AstNode VisitFilterClause(FilterClause clause, object data)
{
return TransformFilterExpression(clause) ?? base.VisitFilterClause(clause, data);
}
#endregion

/// <summary>
Expand Down Expand Up @@ -1089,6 +1094,31 @@ TryCatchStatement TransformTryCatchFinally(TryCatchStatement tryFinally)
}
#endregion

#region Exception Filter Expression
static readonly AstNode expFilterExprPattern = new FilterClause(
new NamedNode("lambda",
new LambdaExpression() {
Body = new BlockStatement {
new ReturnStatement(new NamedNode("expr", new AnyNode()))
}
}
)
);

public FilterClause TransformFilterExpression(FilterClause clause) {
Match m = expFilterExprPattern.Match(clause);
if (m.Success) {
var lambda = m.Get<LambdaExpression>("lambda").Single();
var expr = m.Get<Expression>("expr").Single();
if (lambda.Annotation<FilterClauseAnnotation>() != null) {
clause.Expression = expr;
return clause;
}
}
return null;
}
#endregion

#region Simplify cascading if-else-if statements
static readonly IfElseStatement cascadingIfElsePattern = new IfElseStatement
{
Expand Down
119 changes: 76 additions & 43 deletions ICSharpCode.Decompiler/ILAst/ILAstBuilder.cs
Expand Up @@ -281,27 +281,33 @@ List<ByteCode> StackAnalysis(MethodDef methodDef)
handlerStart.VariablesBefore = VariableSlot.MakeUknownState(varCount);
if (ex.HandlerType == ExceptionHandlerType.Catch || ex.HandlerType == ExceptionHandlerType.Filter) {
// Catch and Filter handlers start with the exeption on the stack
ByteCode ldexception = new ByteCode() {
Code = ILCode.Ldexception,
Operand = ex.CatchType,
PopCount = 0,
PushCount = 1
};
ldexceptions[ex] = ldexception;
ByteCode ldexception;
if (!ldexceptions.TryGetValue(ex, out ldexception)) {
ldexception = new ByteCode() {
Code = ILCode.Ldexception,
Operand = ex.CatchType,
PopCount = 0,
PushCount = 1
};
ldexceptions[ex] = ldexception;
}
handlerStart.StackBefore = new StackSlot[] { new StackSlot(new [] { ldexception }, null) };
}
agenda.Push(handlerStart);

if (ex.HandlerType == ExceptionHandlerType.Filter)
{
ByteCode filterStart = instrToByteCode[ex.FilterStart];
ByteCode ldexception = new ByteCode() {
Code = ILCode.Ldexception,
Operand = ex.CatchType,
PopCount = 0,
PushCount = 1
};
// TODO: ldexceptions[ex] = ldexception;
ByteCode ldexception;
if (!ldexceptions.TryGetValue(ex, out ldexception)) {
ldexception = new ByteCode() {
Code = ILCode.Ldexception,
Operand = ex.CatchType,
PopCount = 0,
PushCount = 1
};
ldexceptions[ex] = ldexception;
}
filterStart.StackBefore = new StackSlot[] { new StackSlot(new [] { ldexception }, null) };
filterStart.VariablesBefore = VariableSlot.MakeUknownState(varCount);
agenda.Push(filterStart);
Expand Down Expand Up @@ -685,40 +691,37 @@ List<ILNode> ConvertToAst(List<ByteCode> body, HashSet<ExceptionHandler> ehs)
};
// Handle the automatically pushed exception on the stack
ByteCode ldexception = ldexceptions[eh];
if (ldexception.StoreTo == null || ldexception.StoreTo.Count == 0) {
// Exception is not used
catchBlock.ExceptionVariable = null;
} else if (ldexception.StoreTo.Count == 1) {
ILExpression first = catchBlock.Body[0] as ILExpression;
if (first != null &&
first.Code == ILCode.Pop &&
first.Arguments[0].Code == ILCode.Ldloc &&
first.Arguments[0].Operand == ldexception.StoreTo[0])
{
// The exception is just poped - optimize it all away;
if (context.Settings.AlwaysGenerateExceptionVariableForCatchBlocks)
catchBlock.ExceptionVariable = new ILVariable() { Name = "ex_" + eh.HandlerStart.Offset.ToString("X2"), IsGenerated = true };
else
catchBlock.ExceptionVariable = null;
catchBlock.Body.RemoveAt(0);
} else {
catchBlock.ExceptionVariable = ldexception.StoreTo[0];
}
} else {
ILVariable exTemp = new ILVariable() { Name = "ex_" + eh.HandlerStart.Offset.ToString("X2"), IsGenerated = true };
catchBlock.ExceptionVariable = exTemp;
foreach(ILVariable storeTo in ldexception.StoreTo) {
catchBlock.Body.Insert(0, new ILExpression(ILCode.Stloc, storeTo, new ILExpression(ILCode.Ldloc, exTemp)));
}
}
ConvertExceptionVariable(eh, catchBlock, ldexception);
tryCatchBlock.CatchBlocks.Add(catchBlock);
} else if (eh.HandlerType == ExceptionHandlerType.Finally) {
tryCatchBlock.FinallyBlock = new ILBlock(handlerAst);
} else if (eh.HandlerType == ExceptionHandlerType.Fault) {
tryCatchBlock.FaultBlock = new ILBlock(handlerAst);
} else {
// TODO: ExceptionHandlerType.Filter
}
} else if (eh.HandlerType == ExceptionHandlerType.Filter) {
ILTryCatchBlock.CatchBlock catchBlock = new ILTryCatchBlock.CatchBlock() {
ExceptionType = eh.CatchType,
Body = handlerAst
};

// Extract the filter part
startIdx = 0;
while (startIdx < body.Count && body[startIdx].Offset < eh.FilterStart.Offset) startIdx++;
endIdx = 0;
while (endIdx < body.Count && body[endIdx].Offset < eh.HandlerStart.Offset) endIdx++;

List<ILNode> filterAst = ConvertToAst(body.CutRange(startIdx, endIdx - startIdx));
var filterBlock = new ILTryCatchBlock.FilterBlock() {
CatchBlock = catchBlock,
Body = filterAst
};
catchBlock.FilterBlock = filterBlock;

// Handle the automatically pushed exception on the stack
ByteCode ldexception = ldexceptions[eh];
ConvertExceptionVariable(eh, catchBlock, ldexception);

tryCatchBlock.CatchBlocks.Add(catchBlock);
}
}

ehs.ExceptWith(handlers);
Expand Down Expand Up @@ -783,6 +786,36 @@ List<ILNode> ConvertToAst(List<ByteCode> body)

return ast;
}

private void ConvertExceptionVariable(ExceptionHandler eh, ILTryCatchBlock.CatchBlock catchBlock, ByteCode ldexception)
{
if (ldexception.StoreTo == null || ldexception.StoreTo.Count == 0) {
// Exception is not used
catchBlock.ExceptionVariable = null;
} else if (ldexception.StoreTo.Count == 1 || (catchBlock.FilterBlock != null && ldexception.StoreTo.Count == 2)) {
ILExpression first = catchBlock.Body[0] as ILExpression;
if (first != null &&
first.Code == ILCode.Pop &&
first.Arguments[0].Code == ILCode.Ldloc &&
first.Arguments[0].Operand == ldexception.StoreTo[0])
{
// The exception is just poped - optimize it all away;
if (context.Settings.AlwaysGenerateExceptionVariableForCatchBlocks || catchBlock.FilterBlock != null)
catchBlock.ExceptionVariable = new ILVariable() { Name = "ex_" + eh.HandlerStart.Offset.ToString("X2"), IsGenerated = true };
else
catchBlock.ExceptionVariable = null;
catchBlock.Body.RemoveAt(0);
} else {
catchBlock.ExceptionVariable = ldexception.StoreTo[0];
}
} else {
ILVariable exTemp = new ILVariable() { Name = "ex_" + eh.HandlerStart.Offset.ToString("X2"), IsGenerated = true };
catchBlock.ExceptionVariable = exTemp;
foreach(ILVariable storeTo in ldexception.StoreTo) {
catchBlock.Body.Insert(0, new ILExpression(ILCode.Stloc, storeTo, new ILExpression(ILCode.Ldloc, exTemp)));
}
}
}
}

public static class ILAstBuilderExtensionMethods
Expand Down
64 changes: 64 additions & 0 deletions ICSharpCode.Decompiler/ILAst/ILAstOptimizer.cs
Expand Up @@ -34,6 +34,7 @@ public enum ILAstOptimizationStep
ReduceBranchInstructionSet,
InlineVariables,
CopyPropagation,
FilterExceptionType,
YieldReturn,
AsyncAwait,
PropertyAccessInstructions,
Expand Down Expand Up @@ -106,6 +107,9 @@ public void Optimize(DecompilerContext context, ILBlock method, ILAstOptimizatio

if (abortBeforeStep == ILAstOptimizationStep.CopyPropagation) return;
inlining1.CopyPropagation();

if (abortBeforeStep == ILAstOptimizationStep.FilterExceptionType) return;
FilterExceptionType(method);

if (abortBeforeStep == ILAstOptimizationStep.YieldReturn) return;
YieldReturnDecompiler.Run(context, method);
Expand Down Expand Up @@ -648,6 +652,66 @@ void ReduceIfNesting(ILNode node)
ReduceIfNesting(child);
}
}

void FilterExceptionType(ILBlock method) {
foreach (var filter in method.GetSelfAndChildrenRecursive<ILTryCatchBlock.FilterBlock>()) {
ITypeDefOrRef exType;
ILVariable exVar;
ILVariable exTemp;
ILExpression isinst;
ILLabel exitLbl;

// exTemp = isinst([mscorlib]System.Exception, exVar)
if (filter.Body.Count > 8 &&
filter.Body[0].Match(ILCode.Stloc, out exTemp, out isinst)) {

ILExpression v1, v2;
if (!isinst.Match(ILCode.Isinst, out exType, out v1) || !v1.Match(ILCode.Ldloc, out exVar))
continue;

// brtrue(next, exTemp)
ILLabel next;
if (!filter.Body[1].Match(ILCode.Brtrue, out next, out v2) || !v2.MatchLdloc(exTemp))
continue;

// retVar = ldc.i4(0)
ILVariable retVar;
ILExpression lit;
if (!filter.Body[2].Match(ILCode.Stloc, out retVar, out lit) || !lit.MatchLdcI4(0))
continue;

// br(exitLbl)
// next:
if (!filter.Body[3].Match(ILCode.Br, out exitLbl) || filter.Body[4] != next)
continue;

// stloc(trueExVar, exprTmp)
ILVariable trueExVar;
ILExpression exprTmp;
if (!filter.Body[5].Match(ILCode.Stloc, out trueExVar, out exprTmp) ||
!exprTmp.MatchLdloc(exTemp) || trueExVar.OriginalVariable == null)
continue;

// exitLbl:
var index = filter.Body.IndexOf(exitLbl);
if (index < 0 || index + 1 >= filter.Body.Count)
continue;

// endfilter(retVar)
ILExpression retExpr;
if (!filter.Body[index + 1].Match(ILCode.Endfilter, out retExpr) || !retExpr.MatchLdloc(retVar))
continue;

filter.Body.RemoveRange(0, 6);

filter.CatchBlock.ExceptionType = exType;
filter.CatchBlock.ExceptionVariable.Type = exType.ToTypeSig();
trueExVar.IsGenerated = true;

ReplaceVariables(filter, var => (var == trueExVar) ? exVar : var);
}
}
}

void RecombineVariables(ILBlock method)
{
Expand Down
37 changes: 33 additions & 4 deletions ICSharpCode.Decompiler/ILAst/ILAstTypes.cs
Expand Up @@ -134,14 +134,25 @@ public class ILTryCatchBlock: ILNode
public class CatchBlock: ILBlock
{
public ITypeDefOrRef ExceptionType;
public FilterBlock FilterBlock;
public ILVariable ExceptionVariable;

public override void WriteTo(ITextOutput output)
{
output.Write("catch ");
output.WriteReference(ExceptionType.FullName, ExceptionType);
if (ExceptionVariable != null) {
output.Write(' ');
if (FilterBlock != null) {
FilterBlock.WriteTo(output);
}

if (ExceptionType != null) {
output.Write("catch ");
output.WriteReference(ExceptionType.FullName, ExceptionType);
if (ExceptionVariable != null) {
output.Write(' ');
output.Write(ExceptionVariable.Name);
}
}
else {
output.Write("handler ");
output.Write(ExceptionVariable.Name);
}
output.WriteLine(" {");
Expand All @@ -151,6 +162,21 @@ public override void WriteTo(ITextOutput output)
output.WriteLine("}");
}
}

public class FilterBlock: ILBlock
{
public CatchBlock CatchBlock;

public override void WriteTo(ITextOutput output)
{
output.Write("filter ");
output.WriteLine(" {");
output.Indent();
base.WriteTo(output);
output.Unindent();
output.WriteLine("}");
}
}

public ILBlock TryBlock;
public List<CatchBlock> CatchBlocks;
Expand All @@ -163,6 +189,9 @@ public override IEnumerable<ILNode> GetChildren()
yield return this.TryBlock;
foreach (var catchBlock in this.CatchBlocks) {
yield return catchBlock;
if (catchBlock.FilterBlock != null) {
yield return catchBlock.FilterBlock;
}
}
if (this.FaultBlock != null)
yield return this.FaultBlock;
Expand Down

0 comments on commit 20c40b0

Please sign in to comment.