Browse Source

Support for the null coalescing operator

pull/100/head
David Srbecký 15 years ago
parent
commit
a58cdc6a6e
  1. 1
      ICSharpCode.Decompiler/Ast/AstMethodBodyBuilder.cs
  2. 100
      ICSharpCode.Decompiler/ILAst/ILAstOptimizer.cs
  3. 1
      ICSharpCode.Decompiler/ILAst/ILCodes.cs
  4. 5
      ICSharpCode.Decompiler/ILAst/TypeAnalysis.cs

1
ICSharpCode.Decompiler/Ast/AstMethodBodyBuilder.cs

@ -276,6 +276,7 @@ namespace ICSharpCode.Decompiler.Ast
case ILCode.LogicAnd: return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.ConditionalAnd, arg2); case ILCode.LogicAnd: return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.ConditionalAnd, arg2);
case ILCode.LogicOr: return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.ConditionalOr, arg2); case ILCode.LogicOr: return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.ConditionalOr, arg2);
case ILCode.TernaryOp: return new Ast.ConditionalExpression() { Condition = arg1, TrueExpression = arg2, FalseExpression = arg3 }; case ILCode.TernaryOp: return new Ast.ConditionalExpression() { Condition = arg1, TrueExpression = arg2, FalseExpression = arg3 };
case ILCode.NullCoalescing: return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.NullCoalescing, arg2);
#endregion #endregion
#region Branch #region Branch
case ILCode.Br: return new Ast.GotoStatement(((ILLabel)byteCode.Operand).Name); case ILCode.Br: return new Ast.GotoStatement(((ILLabel)byteCode.Operand).Name);

100
ICSharpCode.Decompiler/ILAst/ILAstOptimizer.cs

@ -19,6 +19,10 @@ namespace ICSharpCode.Decompiler.ILAst
SplitToMovableBlocks, SplitToMovableBlocks,
TypeInference, TypeInference,
PeepholeOptimizations, PeepholeOptimizations,
SimplifyShortCircuit,
SimplifyTernaryOperator,
SimplifyNullCoalescing,
MoreSimplifyPasses,
FindLoops, FindLoops,
FindConditions, FindConditions,
FlattenNestedMovableBlocks, FlattenNestedMovableBlocks,
@ -79,8 +83,17 @@ namespace ICSharpCode.Decompiler.ILAst
bool modified; bool modified;
do { do {
modified = false; modified = false;
if (abortBeforeStep == ILAstOptimizationStep.SimplifyShortCircuit) return;
modified |= block.RunPeepholeOptimization(TrySimplifyShortCircuit); modified |= block.RunPeepholeOptimization(TrySimplifyShortCircuit);
if (abortBeforeStep == ILAstOptimizationStep.SimplifyTernaryOperator) return;
modified |= block.RunPeepholeOptimization(TrySimplifyTernaryOperator); modified |= block.RunPeepholeOptimization(TrySimplifyTernaryOperator);
if (abortBeforeStep == ILAstOptimizationStep.SimplifyNullCoalescing) return;
modified |= block.RunPeepholeOptimization(TrySimplifyNullCoalescing);
if (abortBeforeStep == ILAstOptimizationStep.MoreSimplifyPasses) return;
} while(modified); } while(modified);
} }
@ -160,7 +173,9 @@ namespace ICSharpCode.Decompiler.ILAst
i++; // Ignore the label as well i++; // Ignore the label as well
} else if (body[i].Match(ILCode.Nop)){ } else if (body[i].Match(ILCode.Nop)){
// Ignore nop // Ignore nop
} else if (body[i].Match(ILCode.Pop, out popExpr) && popExpr.HasNoSideEffects()) { } else if (body[i].Match(ILCode.Pop, out popExpr)) {
if (!popExpr.HasNoSideEffects())
throw new Exception("Pop should have just ldloc at this stage");
// Ignore pop // Ignore pop
} else { } else {
newBody.Add(body[i]); newBody.Add(body[i]);
@ -323,7 +338,10 @@ namespace ICSharpCode.Decompiler.ILAst
labelToBasicBlock[trueLabel].Match(ILCode.Stloc, out trueLocVar, out trueExpr, out trueFall) && labelToBasicBlock[trueLabel].Match(ILCode.Stloc, out trueLocVar, out trueExpr, out trueFall) &&
labelToBasicBlock[falseLabel].Match(ILCode.Stloc, out falseLocVar, out falseExpr, out falseFall) && labelToBasicBlock[falseLabel].Match(ILCode.Stloc, out falseLocVar, out falseExpr, out falseFall) &&
trueLocVar == falseLocVar && trueLocVar == falseLocVar &&
trueFall == falseFall) trueFall == falseFall &&
scope.Contains(labelToBasicBlock[trueLabel]) &&
scope.Contains(labelToBasicBlock[falseLabel])
)
{ {
int boolVal; int boolVal;
ILExpression newExpr; ILExpression newExpr;
@ -353,18 +371,75 @@ namespace ICSharpCode.Decompiler.ILAst
head.FallthoughGoto = new ILExpression(ILCode.Br, trueFall); head.FallthoughGoto = new ILExpression(ILCode.Br, trueFall);
// Remove the old basic blocks // Remove the old basic blocks
scope.RemoveOrThrow(labelToBasicBlock[trueLabel]); foreach(ILLabel deleteLabel in new [] { trueLabel, falseLabel }) {
scope.RemoveOrThrow(labelToBasicBlock[falseLabel]); scope.RemoveOrThrow(labelToBasicBlock[deleteLabel]);
labelToBasicBlock.RemoveOrThrow(trueLabel); labelGlobalRefCount.RemoveOrThrow(deleteLabel);
labelToBasicBlock.RemoveOrThrow(falseLabel); labelToBasicBlock.RemoveOrThrow(deleteLabel);
labelGlobalRefCount.RemoveOrThrow(trueLabel); }
labelGlobalRefCount.RemoveOrThrow(falseLabel);
return true; return true;
} }
return false; return false;
} }
bool TrySimplifyNullCoalescing(List<ILNode> scope, ILBasicBlock head, int index)
{
// ...
// v = ldloc(leftVar)
// br(condBBLabel)
//
// condBBLabel:
// brtrue(endBBLabel, ldloc(leftVar))
// br(rightBBLabel)
//
// rightBBLabel:
// v = rightExpr
// br(endBBLabel)
//
// endBBLabel:
// ...
ILVariable v, v2;
ILExpression leftExpr, leftExpr2;
ILVariable leftVar, leftVar2;
ILLabel condBBLabel;
ILBasicBlock condBB;
ILLabel endBBLabel, endBBLabel2;
ILLabel rightBBLabel;
ILBasicBlock rightBB;
ILExpression rightExpr;
ILBasicBlock endBB;
if (head.Body.LastOrDefault().Match(ILCode.Stloc, out v, out leftExpr) &&
leftExpr.Match(ILCode.Ldloc, out leftVar) &&
head.FallthoughGoto.Match(ILCode.Br, out condBBLabel) &&
labelToBasicBlock.TryGetValue(condBBLabel, out condBB) &&
condBB.Match(ILCode.Brtrue, out endBBLabel, out leftExpr2, out rightBBLabel) &&
leftExpr2.Match(ILCode.Ldloc, out leftVar2) &&
leftVar == leftVar2 &&
labelToBasicBlock.TryGetValue(rightBBLabel, out rightBB) &&
rightBB.Match(ILCode.Stloc, out v2, out rightExpr, out endBBLabel2) &&
v == v2 &&
endBBLabel == endBBLabel2 &&
labelToBasicBlock.TryGetValue(endBBLabel, out endBB) &&
labelGlobalRefCount.GetOrDefault(condBBLabel) == 1 &&
labelGlobalRefCount.GetOrDefault(rightBBLabel) == 1 &&
labelGlobalRefCount.GetOrDefault(endBBLabel) == 2 &&
scope.ContainsAll(condBB, rightBB, endBB)
)
{
head.Body[head.Body.Count - 1] = new ILExpression(ILCode.Stloc, v, new ILExpression(ILCode.NullCoalescing, null, leftExpr, rightExpr));
head.FallthoughGoto = new ILExpression(ILCode.Br, endBBLabel);
foreach(ILLabel deleteLabel in new [] { condBBLabel, rightBBLabel }) {
scope.RemoveOrThrow(labelToBasicBlock[deleteLabel]);
labelGlobalRefCount.RemoveOrThrow(deleteLabel);
labelToBasicBlock.RemoveOrThrow(deleteLabel);
}
return true;
}
return false;
}
// scope is modified if successful // scope is modified if successful
bool TrySimplifyShortCircuit(List<ILNode> scope, ILBasicBlock head, int index) bool TrySimplifyShortCircuit(List<ILNode> scope, ILBasicBlock head, int index)
{ {
@ -1093,6 +1168,15 @@ namespace ICSharpCode.Decompiler.ILAst
return ret; return ret;
} }
public static bool ContainsAll<T>(this List<T> list, params T[] items)
{
foreach (T item in items) {
if (!list.Contains(item))
return false;
}
return true;
}
public static void RemoveOrThrow<T>(this ICollection<T> collection, T item) public static void RemoveOrThrow<T>(this ICollection<T> collection, T item)
{ {
if (!collection.Remove(item)) if (!collection.Remove(item))

1
ICSharpCode.Decompiler/ILAst/ILCodes.cs

@ -258,6 +258,7 @@ namespace ICSharpCode.Decompiler.ILAst
LogicNot, LogicNot,
LogicAnd, LogicAnd,
LogicOr, LogicOr,
NullCoalescing,
InitArray, // Array Initializer InitArray, // Array Initializer
InitCollection, // Collection Initializer: first arg is newobj, remaining args are InitCollectionAddMethod method calls InitCollection, // Collection Initializer: first arg is newobj, remaining args are InitCollectionAddMethod method calls
InitCollectionAddMethod, InitCollectionAddMethod,

5
ICSharpCode.Decompiler/ILAst/TypeAnalysis.cs

@ -138,6 +138,11 @@ namespace ICSharpCode.Decompiler.ILAst
InferTypeForExpression(expr.Arguments[1], expectedType, forceInferChildren), InferTypeForExpression(expr.Arguments[1], expectedType, forceInferChildren),
InferTypeForExpression(expr.Arguments[2], expectedType, forceInferChildren) InferTypeForExpression(expr.Arguments[2], expectedType, forceInferChildren)
); );
case ILCode.NullCoalescing:
return TypeWithMoreInformation(
InferTypeForExpression(expr.Arguments[0], expectedType, forceInferChildren),
InferTypeForExpression(expr.Arguments[1], expectedType, forceInferChildren)
);
#endregion #endregion
#region Variable load/store #region Variable load/store
case ILCode.Stloc: case ILCode.Stloc:

Loading…
Cancel
Save