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 @@ -276,6 +276,7 @@ namespace ICSharpCode.Decompiler.Ast
case ILCode.LogicAnd: return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.ConditionalAnd, 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.NullCoalescing: return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.NullCoalescing, arg2);
#endregion
#region Branch
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 @@ -19,6 +19,10 @@ namespace ICSharpCode.Decompiler.ILAst
SplitToMovableBlocks,
TypeInference,
PeepholeOptimizations,
SimplifyShortCircuit,
SimplifyTernaryOperator,
SimplifyNullCoalescing,
MoreSimplifyPasses,
FindLoops,
FindConditions,
FlattenNestedMovableBlocks,
@ -79,8 +83,17 @@ namespace ICSharpCode.Decompiler.ILAst @@ -79,8 +83,17 @@ namespace ICSharpCode.Decompiler.ILAst
bool modified;
do {
modified = false;
if (abortBeforeStep == ILAstOptimizationStep.SimplifyShortCircuit) return;
modified |= block.RunPeepholeOptimization(TrySimplifyShortCircuit);
if (abortBeforeStep == ILAstOptimizationStep.SimplifyTernaryOperator) return;
modified |= block.RunPeepholeOptimization(TrySimplifyTernaryOperator);
if (abortBeforeStep == ILAstOptimizationStep.SimplifyNullCoalescing) return;
modified |= block.RunPeepholeOptimization(TrySimplifyNullCoalescing);
if (abortBeforeStep == ILAstOptimizationStep.MoreSimplifyPasses) return;
} while(modified);
}
@ -160,7 +173,9 @@ namespace ICSharpCode.Decompiler.ILAst @@ -160,7 +173,9 @@ namespace ICSharpCode.Decompiler.ILAst
i++; // Ignore the label as well
} else if (body[i].Match(ILCode.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
} else {
newBody.Add(body[i]);
@ -323,7 +338,10 @@ namespace ICSharpCode.Decompiler.ILAst @@ -323,7 +338,10 @@ namespace ICSharpCode.Decompiler.ILAst
labelToBasicBlock[trueLabel].Match(ILCode.Stloc, out trueLocVar, out trueExpr, out trueFall) &&
labelToBasicBlock[falseLabel].Match(ILCode.Stloc, out falseLocVar, out falseExpr, out falseFall) &&
trueLocVar == falseLocVar &&
trueFall == falseFall)
trueFall == falseFall &&
scope.Contains(labelToBasicBlock[trueLabel]) &&
scope.Contains(labelToBasicBlock[falseLabel])
)
{
int boolVal;
ILExpression newExpr;
@ -353,18 +371,75 @@ namespace ICSharpCode.Decompiler.ILAst @@ -353,18 +371,75 @@ namespace ICSharpCode.Decompiler.ILAst
head.FallthoughGoto = new ILExpression(ILCode.Br, trueFall);
// Remove the old basic blocks
scope.RemoveOrThrow(labelToBasicBlock[trueLabel]);
scope.RemoveOrThrow(labelToBasicBlock[falseLabel]);
labelToBasicBlock.RemoveOrThrow(trueLabel);
labelToBasicBlock.RemoveOrThrow(falseLabel);
labelGlobalRefCount.RemoveOrThrow(trueLabel);
labelGlobalRefCount.RemoveOrThrow(falseLabel);
foreach(ILLabel deleteLabel in new [] { trueLabel, falseLabel }) {
scope.RemoveOrThrow(labelToBasicBlock[deleteLabel]);
labelGlobalRefCount.RemoveOrThrow(deleteLabel);
labelToBasicBlock.RemoveOrThrow(deleteLabel);
}
return true;
}
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
bool TrySimplifyShortCircuit(List<ILNode> scope, ILBasicBlock head, int index)
{
@ -1093,6 +1168,15 @@ namespace ICSharpCode.Decompiler.ILAst @@ -1093,6 +1168,15 @@ namespace ICSharpCode.Decompiler.ILAst
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)
{
if (!collection.Remove(item))

1
ICSharpCode.Decompiler/ILAst/ILCodes.cs

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

5
ICSharpCode.Decompiler/ILAst/TypeAnalysis.cs

@ -138,6 +138,11 @@ namespace ICSharpCode.Decompiler.ILAst @@ -138,6 +138,11 @@ namespace ICSharpCode.Decompiler.ILAst
InferTypeForExpression(expr.Arguments[1], 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
#region Variable load/store
case ILCode.Stloc:

Loading…
Cancel
Save