// Copyright (c) 2011 AlphaSierraPapa for the SharpDevelop Team
//
// Permission is hereby granted, free of charge, to any person obtaining a copy of this
// software and associated documentation files (the "Software"), to deal in the Software
// without restriction, including without limitation the rights to use, copy, modify, merge,
// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
// to whom the Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all copies or
// substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using ICSharpCode.Decompiler.CSharp.Syntax;
using ICSharpCode.Decompiler.CSharp.Syntax.PatternMatching;
using ICSharpCode.Decompiler.CSharp.TypeSystem;
using ICSharpCode.Decompiler.TypeSystem;
using Mono.Cecil;
using ICSharpCode.Decompiler.Semantics;
namespace ICSharpCode.Decompiler.CSharp.Transforms
{
///
/// Finds the expanded form of using statements using pattern matching and replaces it with a UsingStatement.
///
public sealed class PatternStatementTransform : ContextTrackingVisitor, IAstTransform
{
readonly DeclareVariables declareVariables = new DeclareVariables();
TransformContext context;
public void Run(AstNode rootNode, TransformContext context)
{
if (this.context != null)
throw new InvalidOperationException("Reentrancy in PatternStatementTransform.Run?");
try {
this.context = context;
base.Initialize(context);
declareVariables.Analyze(rootNode);
rootNode.AcceptVisitor(this);
} finally {
this.context = null;
base.Uninitialize();
declareVariables.ClearAnalysisResults();
}
}
#region Visitor Overrides
protected override AstNode VisitChildren(AstNode node)
{
// Go through the children, and keep visiting a node as long as it changes.
// Because some transforms delete/replace nodes before and after the node being transformed, we rely
// on the transform's return value to know where we need to keep iterating.
for (AstNode child = node.FirstChild; child != null; child = child.NextSibling) {
AstNode oldChild;
do {
oldChild = child;
child = child.AcceptVisitor(this);
Debug.Assert(child != null && child.Parent == node);
} while (child != oldChild);
}
return node;
}
public override AstNode VisitExpressionStatement(ExpressionStatement expressionStatement)
{
AstNode result;
result = TransformFor(expressionStatement);
if (result != null)
return result;
if (context.Settings.AutomaticProperties) {
result = ReplaceBackingFieldUsage(expressionStatement);
if (result != null)
return result;
}
if (context.Settings.AutomaticEvents) {
result = ReplaceEventFieldAnnotation(expressionStatement);
if (result != null)
return result;
}
return base.VisitExpressionStatement(expressionStatement);
}
public override AstNode VisitWhileStatement(WhileStatement whileStatement)
{
return TransformDoWhile(whileStatement) ?? base.VisitWhileStatement(whileStatement);
}
public override AstNode VisitIfElseStatement(IfElseStatement ifElseStatement)
{
AstNode simplifiedIfElse = SimplifyCascadingIfElseStatements(ifElseStatement);
if (simplifiedIfElse != null)
return simplifiedIfElse;
return base.VisitIfElseStatement(ifElseStatement);
}
public override AstNode VisitPropertyDeclaration(PropertyDeclaration propertyDeclaration)
{
if (context.Settings.AutomaticProperties) {
AstNode result = TransformAutomaticProperties(propertyDeclaration);
if (result != null)
return result;
}
return base.VisitPropertyDeclaration(propertyDeclaration);
}
public override AstNode VisitCustomEventDeclaration(CustomEventDeclaration eventDeclaration)
{
// first apply transforms to the accessor bodies
base.VisitCustomEventDeclaration(eventDeclaration);
if (context.Settings.AutomaticEvents) {
AstNode result = TransformAutomaticEvents(eventDeclaration);
if (result != null)
return result;
}
return eventDeclaration;
}
public override AstNode VisitMethodDeclaration(MethodDeclaration methodDeclaration)
{
return TransformDestructor(methodDeclaration) ?? base.VisitMethodDeclaration(methodDeclaration);
}
public override AstNode VisitTryCatchStatement(TryCatchStatement tryCatchStatement)
{
return TransformTryCatchFinally(tryCatchStatement) ?? base.VisitTryCatchStatement(tryCatchStatement);
}
#endregion
///
/// $variable = $initializer;
///
static readonly AstNode variableAssignPattern = new ExpressionStatement(
new AssignmentExpression(
new NamedNode("variable", new IdentifierExpression(Pattern.AnyString)),
new AnyNode("initializer")
));
#region for
static readonly WhileStatement forPattern = new WhileStatement {
Condition = new BinaryOperatorExpression {
Left = new NamedNode("ident", new IdentifierExpression(Pattern.AnyString)),
Operator = BinaryOperatorType.Any,
Right = new AnyNode("endExpr")
},
EmbeddedStatement = new BlockStatement {
Statements = {
new Repeat(new AnyNode("statement")),
new NamedNode(
"increment",
new ExpressionStatement(
new AssignmentExpression {
Left = new Backreference("ident"),
Operator = AssignmentOperatorType.Any,
Right = new AnyNode()
}))
}
}};
public ForStatement TransformFor(ExpressionStatement node)
{
Match m1 = variableAssignPattern.Match(node);
if (!m1.Success) return null;
var variableName = m1.Get("variable").Single().Identifier;
AstNode next = node.NextSibling;
if (next is ForStatement forStatement) {
if ((forStatement.Iterators.FirstOrDefault() is ExpressionStatement stmt
&& stmt.Expression is AssignmentExpression assign
&& variableName == assign.Left.ToString())
|| (forStatement.Condition is BinaryOperatorExpression cond
&& variableName == cond.Left.ToString()))
{
node.Remove();
forStatement.InsertChildAfter(null, node, ForStatement.InitializerRole);
return forStatement;
}
}
Match m3 = forPattern.Match(next);
if (!m3.Success) return null;
// ensure the variable in the for pattern is the same as in the declaration
if (variableName != m3.Get("ident").Single().Identifier)
return null;
WhileStatement loop = (WhileStatement)next;
node.Remove();
BlockStatement newBody = new BlockStatement();
foreach (Statement stmt in m3.Get("statement"))
newBody.Add(stmt.Detach());
forStatement = new ForStatement();
forStatement.CopyAnnotationsFrom(loop);
forStatement.Initializers.Add(node);
forStatement.Condition = loop.Condition.Detach();
forStatement.Iterators.Add(m3.Get("increment").Single().Detach());
forStatement.EmbeddedStatement = newBody;
loop.ReplaceWith(forStatement);
return forStatement;
}
#endregion
#region doWhile
static readonly WhileStatement doWhilePattern = new WhileStatement {
Condition = new PrimitiveExpression(true),
EmbeddedStatement = new BlockStatement {
Statements = {
new Repeat(new AnyNode("statement")),
new IfElseStatement {
Condition = new AnyNode("condition"),
TrueStatement = new BlockStatement { new BreakStatement() }
}
}
}};
public DoWhileStatement TransformDoWhile(WhileStatement whileLoop)
{
Match m = doWhilePattern.Match(whileLoop);
if (m.Success) {
DoWhileStatement doLoop = new DoWhileStatement();
doLoop.Condition = new UnaryOperatorExpression(UnaryOperatorType.Not, m.Get("condition").Single().Detach());
//doLoop.Condition.AcceptVisitor(new PushNegation(), null);
BlockStatement block = (BlockStatement)whileLoop.EmbeddedStatement;
block.Statements.Last().Remove(); // remove if statement
doLoop.EmbeddedStatement = block.Detach();
doLoop.CopyAnnotationsFrom(whileLoop);
whileLoop.ReplaceWith(doLoop);
// we may have to extract variable definitions out of the loop if they were used in the condition:
foreach (var varDecl in block.Statements.OfType()) {
VariableInitializer v = varDecl.Variables.Single();
if (doLoop.Condition.DescendantsAndSelf.OfType().Any(i => i.Identifier == v.Name)) {
AssignmentExpression assign = new AssignmentExpression(new IdentifierExpression(v.Name), v.Initializer.Detach());
// move annotations from v to assign:
assign.CopyAnnotationsFrom(v);
v.RemoveAnnotations