Browse Source

Don't use checked/unchecked expressions within an ExpressionStatement (#90)

pull/100/head
Daniel Grunwald 15 years ago
parent
commit
8770d54506
  1. 60
      ICSharpCode.Decompiler/Ast/Transforms/AddCheckedBlocks.cs
  2. 5
      ICSharpCode.Decompiler/Ast/Transforms/IntroduceUsingDeclarations.cs
  3. 31
      ICSharpCode.Decompiler/Ast/Transforms/ReplaceMethodCallsWithOperators.cs
  4. 10
      ICSharpCode.Decompiler/Tests/CheckedUnchecked.cs

60
ICSharpCode.Decompiler/Ast/Transforms/AddCheckedBlocks.cs

@ -140,6 +140,28 @@ namespace ICSharpCode.Decompiler.Ast.Transforms @@ -140,6 +140,28 @@ namespace ICSharpCode.Decompiler.Ast.Transforms
}
}
class ConvertCompoundAssignment : InsertedNode
{
readonly Expression expression;
readonly bool isChecked;
public ConvertCompoundAssignment(Expression expression, bool isChecked)
{
this.expression = expression;
this.isChecked = isChecked;
}
public override void Insert()
{
AssignmentExpression assign = expression.Annotation<ReplaceMethodCallsWithOperators.RestoreOriginalAssignOperatorAnnotation>().Restore(expression);
expression.ReplaceWith(assign);
if (isChecked)
assign.Right = new CheckedExpression { Expression = assign.Right.Detach() };
else
assign.Right = new UncheckedExpression { Expression = assign.Right.Detach() };
}
}
class InsertedBlock : InsertedNode
{
readonly Statement firstStatement; // inclusive
@ -279,20 +301,38 @@ namespace ICSharpCode.Decompiler.Ast.Transforms @@ -279,20 +301,38 @@ namespace ICSharpCode.Decompiler.Ast.Transforms
if (expr != null) {
CheckedUncheckedAnnotation annotation = expr.Annotation<CheckedUncheckedAnnotation>();
if (annotation != null) {
// If the annotation requires this node to be in a specific context, set the cost of the other context to infinite.
// If the annotation requires this node to be in a specific context, add a huge cost to the other context
// That huge cost gives us the option to ignore a required checked/unchecked expression when there wouldn't be any
// solution otherwise. (e.g. "for (checked(M().x += 1); true; unchecked(M().x += 2)) {}")
if (annotation.IsChecked)
result.CostInUncheckedContext = Cost.Infinite;
result.CostInUncheckedContext += new Cost(10000, 0);
else
result.CostInCheckedContext = Cost.Infinite;
result.CostInCheckedContext += new Cost(10000, 0);
}
// Embed this node in an checked/unchecked expression:
// We use '<' so that expressions are introduced on the deepest level possible (goal 3)
if (result.CostInCheckedContext + new Cost(0, 1) < result.CostInUncheckedContext) {
result.CostInUncheckedContext = result.CostInCheckedContext + new Cost(0, 1);
result.NodesToInsertInUncheckedContext = result.NodesToInsertInCheckedContext + new InsertedExpression(expr, true);
} else if (result.CostInUncheckedContext + new Cost(0, 1) < result.CostInCheckedContext) {
result.CostInCheckedContext = result.CostInUncheckedContext + new Cost(0, 1);
result.NodesToInsertInCheckedContext = result.NodesToInsertInUncheckedContext + new InsertedExpression(expr, false);
if (expr.Parent is ExpressionStatement) {
// We cannot use checked/unchecked for top-level-expressions.
// However, we could try converting a compound assignment (checked(a+=b);) or unary operator (checked(a++);)
// back to its old form.
if (expr.Annotation<ReplaceMethodCallsWithOperators.RestoreOriginalAssignOperatorAnnotation>() != null) {
// We use '<' so that expressions are introduced on the deepest level possible (goal 3)
if (result.CostInCheckedContext + new Cost(1, 1) < result.CostInUncheckedContext) {
result.CostInUncheckedContext = result.CostInCheckedContext + new Cost(1, 1);
result.NodesToInsertInUncheckedContext = result.NodesToInsertInCheckedContext + new ConvertCompoundAssignment(expr, true);
} else if (result.CostInUncheckedContext + new Cost(1, 1) < result.CostInCheckedContext) {
result.CostInCheckedContext = result.CostInUncheckedContext + new Cost(1, 1);
result.NodesToInsertInCheckedContext = result.NodesToInsertInUncheckedContext + new ConvertCompoundAssignment(expr, false);
}
}
} else {
// We use '<' so that expressions are introduced on the deepest level possible (goal 3)
if (result.CostInCheckedContext + new Cost(0, 1) < result.CostInUncheckedContext) {
result.CostInUncheckedContext = result.CostInCheckedContext + new Cost(0, 1);
result.NodesToInsertInUncheckedContext = result.NodesToInsertInCheckedContext + new InsertedExpression(expr, true);
} else if (result.CostInUncheckedContext + new Cost(0, 1) < result.CostInCheckedContext) {
result.CostInCheckedContext = result.CostInUncheckedContext + new Cost(0, 1);
result.NodesToInsertInCheckedContext = result.NodesToInsertInUncheckedContext + new InsertedExpression(expr, false);
}
}
}
return result;

5
ICSharpCode.Decompiler/Ast/Transforms/IntroduceUsingDeclarations.cs

@ -27,6 +27,11 @@ namespace ICSharpCode.Decompiler.Ast.Transforms @@ -27,6 +27,11 @@ namespace ICSharpCode.Decompiler.Ast.Transforms
// First determine all the namespaces that need to be imported:
compilationUnit.AcceptVisitor(this, null);
if (importedNamespaces.Count == 0) {
// abort when no namespaces have to be imported
return;
}
importedNamespaces.Add("System"); // always import System, even when not necessary
// Now add using declarations for those namespaces:

31
ICSharpCode.Decompiler/Ast/Transforms/ReplaceMethodCallsWithOperators.cs

@ -140,6 +140,36 @@ namespace ICSharpCode.Decompiler.Ast.Transforms @@ -140,6 +140,36 @@ namespace ICSharpCode.Decompiler.Ast.Transforms
}
}
/// <summary>
/// This annotation is used to convert a compound assignment "a += 2;" or increment operator "a++;"
/// back to the original "a = a + 2;". This is sometimes necessary when the checked/unchecked semantics
/// cannot be guaranteed otherwise (see CheckedUnchecked.ForWithCheckedInitializerAndUncheckedIterator test)
/// </summary>
public class RestoreOriginalAssignOperatorAnnotation
{
readonly BinaryOperatorExpression binaryOperatorExpression;
public RestoreOriginalAssignOperatorAnnotation(BinaryOperatorExpression binaryOperatorExpression)
{
this.binaryOperatorExpression = binaryOperatorExpression;
}
public AssignmentExpression Restore(Expression expression)
{
expression.RemoveAnnotations<RestoreOriginalAssignOperatorAnnotation>();
AssignmentExpression assign = expression as AssignmentExpression;
if (assign == null) {
UnaryOperatorExpression uoe = (UnaryOperatorExpression)expression;
assign = new AssignmentExpression(uoe.Expression.Detach(), new PrimitiveExpression(1));
} else {
assign.Operator = AssignmentOperatorType.Assign;
}
binaryOperatorExpression.Right = assign.Right.Detach();
assign.Right = binaryOperatorExpression;
return assign;
}
}
public override object VisitAssignmentExpression(AssignmentExpression assignment, object data)
{
base.VisitAssignmentExpression(assignment, data);
@ -183,6 +213,7 @@ namespace ICSharpCode.Decompiler.Ast.Transforms @@ -183,6 +213,7 @@ namespace ICSharpCode.Decompiler.Ast.Transforms
// If we found a shorter operator, get rid of the BinaryOperatorExpression:
assignment.CopyAnnotationsFrom(binary);
assignment.Right = binary.Right;
assignment.AddAnnotation(new RestoreOriginalAssignOperatorAnnotation(binary));
}
}
}

10
ICSharpCode.Decompiler/Tests/CheckedUnchecked.cs

@ -37,4 +37,14 @@ public class CheckedUnchecked @@ -37,4 +37,14 @@ public class CheckedUnchecked
}
}
}
public void ForWithCheckedInitializerAndUncheckedIterator(int n)
{
checked {
int i = n;
for (i -= 10; i < n; i = unchecked(i + 1)) {
n--;
}
}
}
}

Loading…
Cancel
Save