// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
// This code is distributed under MIT X11 license (for details please see \doc\license.txt)
using System;
using System.Linq;
using ICSharpCode.NRefactory.CSharp;
using ICSharpCode.NRefactory.CSharp.PatternMatching;
namespace ICSharpCode.Decompiler.Ast
{
///
/// Helper class for declaring variables.
///
public static class DeclareVariableInSmallestScope
{
static readonly ExpressionStatement assignmentPattern = new ExpressionStatement(
new AssignmentExpression(
new NamedNode("ident", new IdentifierExpression()),
new AnyNode("init")
));
///
/// Declares a variable in the smallest required scope.
///
/// The root of the subtree being searched for the best insertion position
/// The type of the new variable
/// The name of the new variable
/// Whether the variable is allowed to be placed inside a loop
public static VariableDeclarationStatement DeclareVariable(AstNode node, AstType type, string name, bool allowPassIntoLoops = true)
{
VariableDeclarationStatement result = null;
AstNode pos = FindInsertPos(node, name, allowPassIntoLoops);
if (pos != null) {
Match m = assignmentPattern.Match(pos);
if (m != null && m.Get("ident").Single().Identifier == name) {
result = new VariableDeclarationStatement(type, name, m.Get("init").Single().Detach());
result.Variables.Single().CopyAnnotationsFrom(((ExpressionStatement)pos).Expression);
result.CopyAnnotationsFrom(pos);
pos.ReplaceWith(result);
} else {
result = new VariableDeclarationStatement(type, name);
pos.Parent.InsertChildBefore(pos, result, BlockStatement.StatementRole);
}
}
return result;
}
static AstNode FindInsertPos(AstNode node, string name, bool allowPassIntoLoops)
{
AstNode pos = null;
AstNode withinPos = null;
while (node != null) {
IdentifierExpression ident = node as IdentifierExpression;
if (ident != null && ident.Identifier == name && ident.TypeArguments.Count == 0)
return node;
FixedStatement fixedStatement = node as FixedStatement;
if (fixedStatement != null) {
foreach (VariableInitializer v in fixedStatement.Variables) {
if (v.Name == name)
return null; // no need to introduce the variable here
}
}
AstNode withinCurrent = FindInsertPos(node.FirstChild, name, allowPassIntoLoops);
if (withinCurrent != null) {
if (pos == null) {
pos = node;
withinPos = withinCurrent;
} else {
return pos;
}
}
node = node.NextSibling;
}
if (withinPos != null && withinPos.Role == BlockStatement.StatementRole && AllowPassInto(pos, allowPassIntoLoops))
return withinPos;
else
return pos;
}
static bool AllowPassInto(AstNode node, bool allowPassIntoLoops)
{
if (node is AnonymousMethodExpression || node is LambdaExpression)
return false;
if (node is ForStatement || node is ForeachStatement || node is DoWhileStatement || node is WhileStatement)
return allowPassIntoLoops;
return true;
}
}
}