.NET Decompiler with support for PDB generation, ReadyToRun, Metadata (&more) - cross-platform!
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 

194 lines
6.1 KiB

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using ICSharpCode.Decompiler.CSharp.Syntax;
using ICSharpCode.Decompiler.CSharp.Syntax.PatternMatching;
namespace ICSharpCode.Decompiler.CSharp.Transforms
{
class NormalizeBlockStatements : DepthFirstAstVisitor, IAstTransform
{
TransformContext context;
public override void VisitIfElseStatement(IfElseStatement ifElseStatement)
{
base.VisitIfElseStatement(ifElseStatement);
DoTransform(ifElseStatement.TrueStatement, ifElseStatement);
DoTransform(ifElseStatement.FalseStatement, ifElseStatement);
}
public override void VisitWhileStatement(WhileStatement whileStatement)
{
base.VisitWhileStatement(whileStatement);
InsertBlock(whileStatement.EmbeddedStatement);
}
public override void VisitDoWhileStatement(DoWhileStatement doWhileStatement)
{
base.VisitDoWhileStatement(doWhileStatement);
InsertBlock(doWhileStatement.EmbeddedStatement);
}
public override void VisitForeachStatement(ForeachStatement foreachStatement)
{
base.VisitForeachStatement(foreachStatement);
InsertBlock(foreachStatement.EmbeddedStatement);
}
public override void VisitForStatement(ForStatement forStatement)
{
base.VisitForStatement(forStatement);
InsertBlock(forStatement.EmbeddedStatement);
}
public override void VisitFixedStatement(FixedStatement fixedStatement)
{
base.VisitFixedStatement(fixedStatement);
InsertBlock(fixedStatement.EmbeddedStatement);
}
public override void VisitLockStatement(LockStatement lockStatement)
{
base.VisitLockStatement(lockStatement);
InsertBlock(lockStatement.EmbeddedStatement);
}
public override void VisitUsingStatement(UsingStatement usingStatement)
{
base.VisitUsingStatement(usingStatement);
DoTransform(usingStatement.EmbeddedStatement, usingStatement);
}
void DoTransform(Statement statement, Statement parent)
{
if (statement.IsNull) return;
if (context.Settings.AlwaysUseBraces) {
if (!IsElseIf(statement, parent)) {
InsertBlock(statement);
}
} else {
if (statement is BlockStatement b && b.Statements.Count == 1 && IsAllowedAsEmbeddedStatement(b.Statements.First(), parent)) {
statement.ReplaceWith(b.Statements.First().Detach());
} else if (!IsAllowedAsEmbeddedStatement(statement, parent)) {
InsertBlock(statement);
}
}
}
bool IsElseIf(Statement statement, Statement parent)
{
return parent is IfElseStatement && statement.Role == IfElseStatement.FalseRole;
}
static void InsertBlock(Statement statement)
{
if (statement.IsNull) return;
if (!(statement is BlockStatement)) {
var b = new BlockStatement();
statement.ReplaceWith(b);
if (statement is EmptyStatement && !statement.Children.Any()) {
b.CopyAnnotationsFrom(statement);
} else {
b.Add(statement);
}
}
}
bool IsAllowedAsEmbeddedStatement(Statement statement, Statement parent)
{
switch (statement) {
case IfElseStatement ies:
return parent is IfElseStatement && ies.Role == IfElseStatement.FalseRole;
case VariableDeclarationStatement vds:
case WhileStatement ws:
case DoWhileStatement dws:
case SwitchStatement ss:
case ForeachStatement fes:
case ForStatement fs:
case LockStatement ls:
case FixedStatement fxs:
return false;
case UsingStatement us:
return parent is UsingStatement;
default:
return !(parent?.Parent is IfElseStatement);
}
}
void IAstTransform.Run(AstNode rootNode, TransformContext context)
{
this.context = context;
rootNode.AcceptVisitor(this);
}
public override void VisitPropertyDeclaration(PropertyDeclaration propertyDeclaration)
{
if (context.Settings.UseExpressionBodyForCalculatedGetterOnlyProperties) {
SimplifyPropertyDeclaration(propertyDeclaration);
}
base.VisitPropertyDeclaration(propertyDeclaration);
}
public override void VisitIndexerDeclaration(IndexerDeclaration indexerDeclaration)
{
if (context.Settings.UseExpressionBodyForCalculatedGetterOnlyProperties) {
SimplifyIndexerDeclaration(indexerDeclaration);
}
base.VisitIndexerDeclaration(indexerDeclaration);
}
static readonly PropertyDeclaration CalculatedGetterOnlyPropertyPattern = new PropertyDeclaration() {
Attributes = { new Repeat(new AnyNode()) },
Modifiers = Modifiers.Any,
Name = Pattern.AnyString,
PrivateImplementationType = new AnyNodeOrNull(),
ReturnType = new AnyNode(),
Getter = new Accessor() {
Modifiers = Modifiers.Any,
Body = new BlockStatement() { new ReturnStatement(new AnyNode("expression")) }
}
};
static readonly IndexerDeclaration CalculatedGetterOnlyIndexerPattern = new IndexerDeclaration() {
Attributes = { new Repeat(new AnyNode()) },
Modifiers = Modifiers.Any,
PrivateImplementationType = new AnyNodeOrNull(),
Parameters = { new Repeat(new AnyNode()) },
ReturnType = new AnyNode(),
Getter = new Accessor() {
Modifiers = Modifiers.Any,
Body = new BlockStatement() { new ReturnStatement(new AnyNode("expression")) }
}
};
/// <summary>
/// Modifiers that are emitted on accessors, but can be moved to the property declaration.
/// </summary>
const Modifiers movableModifiers = Modifiers.Readonly;
void SimplifyPropertyDeclaration(PropertyDeclaration propertyDeclaration)
{
var m = CalculatedGetterOnlyPropertyPattern.Match(propertyDeclaration);
if (!m.Success)
return;
if ((propertyDeclaration.Getter.Modifiers & ~movableModifiers) != 0)
return;
propertyDeclaration.Modifiers |= propertyDeclaration.Getter.Modifiers;
propertyDeclaration.ExpressionBody = m.Get<Expression>("expression").Single().Detach();
propertyDeclaration.Getter.Remove();
}
void SimplifyIndexerDeclaration(IndexerDeclaration indexerDeclaration)
{
var m = CalculatedGetterOnlyIndexerPattern.Match(indexerDeclaration);
if (!m.Success)
return;
if ((indexerDeclaration.Getter.Modifiers & ~movableModifiers) != 0)
return;
indexerDeclaration.Modifiers |= indexerDeclaration.Getter.Modifiers;
indexerDeclaration.ExpressionBody = m.Get<Expression>("expression").Single().Detach();
indexerDeclaration.Getter.Remove();
}
}
}