mirror of https://github.com/icsharpcode/ILSpy.git
19 changed files with 891 additions and 337 deletions
@ -1,91 +0,0 @@
@@ -1,91 +0,0 @@
|
||||
// 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 |
||||
{ |
||||
/// <summary>
|
||||
/// Helper class for declaring variables.
|
||||
/// </summary>
|
||||
public static class DeclareVariableInSmallestScope |
||||
{ |
||||
static readonly ExpressionStatement assignmentPattern = new ExpressionStatement( |
||||
new AssignmentExpression( |
||||
new NamedNode("ident", new IdentifierExpression()), |
||||
new AnyNode("init") |
||||
)); |
||||
|
||||
/// <summary>
|
||||
/// Declares a variable in the smallest required scope.
|
||||
/// </summary>
|
||||
/// <param name="node">The root of the subtree being searched for the best insertion position</param>
|
||||
/// <param name="type">The type of the new variable</param>
|
||||
/// <param name="name">The name of the new variable</param>
|
||||
/// <param name="allowPassIntoLoops">Whether the variable is allowed to be placed inside a loop</param>
|
||||
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<IdentifierExpression>("ident").Single().Identifier == name) { |
||||
result = new VariableDeclarationStatement(type, name, m.Get<Expression>("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; |
||||
} |
||||
} |
||||
} |
||||
@ -0,0 +1,312 @@
@@ -0,0 +1,312 @@
|
||||
// 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.Collections.Generic; |
||||
using System.Diagnostics; |
||||
using System.Linq; |
||||
using System.Threading; |
||||
using ICSharpCode.NRefactory.CSharp; |
||||
using ICSharpCode.NRefactory.CSharp.Analysis; |
||||
|
||||
namespace ICSharpCode.Decompiler.Ast.Transforms |
||||
{ |
||||
/// <summary>
|
||||
/// Moves variable declarations to improved positions.
|
||||
/// </summary>
|
||||
public class DeclareVariables : IAstTransform |
||||
{ |
||||
sealed class VariableToDeclare |
||||
{ |
||||
public AstType Type; |
||||
public string Name; |
||||
|
||||
public AssignmentExpression ReplacedAssignment; |
||||
public Statement InsertionPoint; |
||||
} |
||||
|
||||
readonly CancellationToken cancellationToken; |
||||
List<VariableToDeclare> variablesToDeclare = new List<VariableToDeclare>(); |
||||
|
||||
public DeclareVariables(DecompilerContext context) |
||||
{ |
||||
this.cancellationToken = context.CancellationToken; |
||||
} |
||||
|
||||
public void Run(AstNode node) |
||||
{ |
||||
Run(node, null); |
||||
// Declare all the variables at the end, after all the logic has run.
|
||||
// This is done so that definite assignment analysis can work on a single representation and doesn't have to be updated
|
||||
// when we change the AST.
|
||||
foreach (var v in variablesToDeclare) { |
||||
if (v.ReplacedAssignment == null) { |
||||
BlockStatement block = (BlockStatement)v.InsertionPoint.Parent; |
||||
block.Statements.InsertBefore( |
||||
v.InsertionPoint, |
||||
new VariableDeclarationStatement((AstType)v.Type.Clone(), v.Name)); |
||||
} |
||||
} |
||||
// First do all the insertions, then do all the replacements. This is necessary because a replacement might remove our reference point from the AST.
|
||||
foreach (var v in variablesToDeclare) { |
||||
if (v.ReplacedAssignment != null) { |
||||
// We clone the right expression so that it doesn't get removed from the old ExpressionStatement,
|
||||
// which might be still in use by the definite assignment graph.
|
||||
VariableDeclarationStatement varDecl = new VariableDeclarationStatement { |
||||
Type = (AstType)v.Type.Clone(), |
||||
Variables = { new VariableInitializer(v.Name, v.ReplacedAssignment.Right.Detach()).CopyAnnotationsFrom(v.ReplacedAssignment) } |
||||
}; |
||||
ExpressionStatement es = v.ReplacedAssignment.Parent as ExpressionStatement; |
||||
if (es != null) |
||||
es.ReplaceWith(varDecl.CopyAnnotationsFrom(es)); |
||||
else |
||||
v.ReplacedAssignment.ReplaceWith(varDecl); |
||||
} |
||||
} |
||||
variablesToDeclare = null; |
||||
} |
||||
|
||||
void Run(AstNode node, DefiniteAssignmentAnalysis daa) |
||||
{ |
||||
BlockStatement block = node as BlockStatement; |
||||
if (block != null) { |
||||
var variables = block.Statements.TakeWhile(stmt => stmt is VariableDeclarationStatement) |
||||
.Cast<VariableDeclarationStatement>().ToList(); |
||||
if (variables.Count > 0) { |
||||
// remove old variable declarations:
|
||||
foreach (VariableDeclarationStatement varDecl in variables) { |
||||
Debug.Assert(varDecl.Variables.Single().Initializer.IsNull); |
||||
varDecl.Remove(); |
||||
} |
||||
if (daa == null) { |
||||
// If possible, reuse the DefiniteAssignmentAnalysis that was created for the parent block
|
||||
daa = new DefiniteAssignmentAnalysis(block, cancellationToken); |
||||
} |
||||
foreach (VariableDeclarationStatement varDecl in variables) { |
||||
string variableName = varDecl.Variables.Single().Name; |
||||
bool allowPassIntoLoops = varDecl.Variables.Single().Annotation<DelegateConstruction.CapturedVariableAnnotation>() == null; |
||||
DeclareVariableInBlock(daa, block, varDecl.Type, variableName, allowPassIntoLoops); |
||||
} |
||||
} |
||||
} |
||||
for (AstNode child = node.FirstChild; child != null; child = child.NextSibling) { |
||||
Run(child, daa); |
||||
} |
||||
} |
||||
|
||||
void DeclareVariableInBlock(DefiniteAssignmentAnalysis daa, BlockStatement block, AstType type, string variableName, bool allowPassIntoLoops) |
||||
{ |
||||
// declarationPoint: The point where the variable would be declared, if we decide to declare it in this block
|
||||
Statement declarationPoint = null; |
||||
// Check whether we can move down the variable into the sub-blocks
|
||||
bool canMoveVariableIntoSubBlocks = FindDeclarationPoint(daa, variableName, allowPassIntoLoops, block, out declarationPoint); |
||||
if (declarationPoint == null) { |
||||
// The variable isn't used at all
|
||||
return; |
||||
} |
||||
if (canMoveVariableIntoSubBlocks) { |
||||
// Declare the variable within the sub-blocks
|
||||
foreach (Statement stmt in block.Statements) { |
||||
ForStatement forStmt = stmt as ForStatement; |
||||
if (forStmt != null && forStmt.Initializers.Count == 1) { |
||||
// handle the special case of moving a variable into the for initializer
|
||||
if (TryConvertAssignmentExpressionIntoVariableDeclaration(forStmt.Initializers.Single(), type, variableName)) |
||||
continue; |
||||
} |
||||
UsingStatement usingStmt = stmt as UsingStatement; |
||||
if (usingStmt != null && usingStmt.ResourceAcquisition is AssignmentExpression) { |
||||
// handle the special case of moving a variable into a using statement
|
||||
if (TryConvertAssignmentExpressionIntoVariableDeclaration((Expression)usingStmt.ResourceAcquisition, type, variableName)) |
||||
continue; |
||||
} |
||||
foreach (AstNode child in stmt.Children) { |
||||
BlockStatement subBlock = child as BlockStatement; |
||||
if (subBlock != null) { |
||||
DeclareVariableInBlock(daa, subBlock, type, variableName, allowPassIntoLoops); |
||||
} else if (HasNestedBlocks(child)) { |
||||
foreach (BlockStatement nestedSubBlock in child.Children.OfType<BlockStatement>()) { |
||||
DeclareVariableInBlock(daa, nestedSubBlock, type, variableName, allowPassIntoLoops); |
||||
} |
||||
} |
||||
} |
||||
} |
||||
} else { |
||||
// Try converting an assignment expression into a VariableDeclarationStatement
|
||||
if (!TryConvertAssignmentExpressionIntoVariableDeclaration(declarationPoint, type, variableName)) { |
||||
// Declare the variable in front of declarationPoint
|
||||
variablesToDeclare.Add(new VariableToDeclare { Type = type, Name = variableName, InsertionPoint = declarationPoint }); |
||||
} |
||||
} |
||||
} |
||||
|
||||
bool TryConvertAssignmentExpressionIntoVariableDeclaration(Statement declarationPoint, AstType type, string variableName) |
||||
{ |
||||
// convert the declarationPoint into a VariableDeclarationStatement
|
||||
ExpressionStatement es = declarationPoint as ExpressionStatement; |
||||
if (es != null) { |
||||
return TryConvertAssignmentExpressionIntoVariableDeclaration(es.Expression, type, variableName); |
||||
} |
||||
return false; |
||||
} |
||||
|
||||
bool TryConvertAssignmentExpressionIntoVariableDeclaration(Expression expression, AstType type, string variableName) |
||||
{ |
||||
AssignmentExpression ae = expression as AssignmentExpression; |
||||
if (ae != null && ae.Operator == AssignmentOperatorType.Assign) { |
||||
IdentifierExpression ident = ae.Left as IdentifierExpression; |
||||
if (ident != null && ident.Identifier == variableName) { |
||||
variablesToDeclare.Add(new VariableToDeclare { Type = type, Name = variableName, ReplacedAssignment = ae }); |
||||
return true; |
||||
} |
||||
} |
||||
return false; |
||||
} |
||||
|
||||
/// <summary>
|
||||
/// Finds the declaration point for the variable within the specified block.
|
||||
/// </summary>
|
||||
/// <param name="daa">
|
||||
/// Definite assignment analysis, must be prepared for 'block' or one of its parents.
|
||||
/// </param>
|
||||
/// <param name="varDecl">The variable to declare</param>
|
||||
/// <param name="block">The block in which the variable should be declared</param>
|
||||
/// <param name="declarationPoint">
|
||||
/// Output parameter: the first statement within 'block' where the variable needs to be declared.
|
||||
/// </param>
|
||||
/// <returns>
|
||||
/// Returns whether it is possible to move the variable declaration into sub-blocks.
|
||||
/// </returns>
|
||||
public static bool FindDeclarationPoint(DefiniteAssignmentAnalysis daa, VariableDeclarationStatement varDecl, BlockStatement block, out Statement declarationPoint) |
||||
{ |
||||
string variableName = varDecl.Variables.Single().Name; |
||||
bool allowPassIntoLoops = varDecl.Variables.Single().Annotation<DelegateConstruction.CapturedVariableAnnotation>() == null; |
||||
return FindDeclarationPoint(daa, variableName, allowPassIntoLoops, block, out declarationPoint); |
||||
} |
||||
|
||||
static bool FindDeclarationPoint(DefiniteAssignmentAnalysis daa, string variableName, bool allowPassIntoLoops, BlockStatement block, out Statement declarationPoint) |
||||
{ |
||||
// declarationPoint: The point where the variable would be declared, if we decide to declare it in this block
|
||||
declarationPoint = null; |
||||
foreach (Statement stmt in block.Statements) { |
||||
if (UsesVariable(stmt, variableName)) { |
||||
if (declarationPoint == null) |
||||
declarationPoint = stmt; |
||||
if (!CanMoveVariableUseIntoSubBlock(stmt, variableName, allowPassIntoLoops)) { |
||||
// If it's not possible to move the variable use into a nested block,
|
||||
// we need to declare the variable in this block
|
||||
return false; |
||||
} |
||||
// If we can move the variable into the sub-block, we need to ensure that the remaining code
|
||||
// does not use the value that was assigned by the first sub-block
|
||||
Statement nextStatement = stmt.NextStatement; |
||||
if (nextStatement != null) { |
||||
// Analyze the range from the next statement to the end of the block
|
||||
daa.SetAnalyzedRange(nextStatement, block); |
||||
daa.Analyze(variableName); |
||||
if (daa.UnassignedVariableUses.Count > 0) { |
||||
return false; |
||||
} |
||||
} |
||||
} |
||||
} |
||||
return true; |
||||
} |
||||
|
||||
static bool CanMoveVariableUseIntoSubBlock(Statement stmt, string variableName, bool allowPassIntoLoops) |
||||
{ |
||||
if (!allowPassIntoLoops && (stmt is ForStatement || stmt is ForeachStatement || stmt is DoWhileStatement || stmt is WhileStatement)) |
||||
return false; |
||||
|
||||
ForStatement forStatement = stmt as ForStatement; |
||||
if (forStatement != null && forStatement.Initializers.Count == 1) { |
||||
// for-statement is special case: we can move variable declarations into the initializer
|
||||
ExpressionStatement es = forStatement.Initializers.Single() as ExpressionStatement; |
||||
if (es != null) { |
||||
AssignmentExpression ae = es.Expression as AssignmentExpression; |
||||
if (ae != null && ae.Operator == AssignmentOperatorType.Assign) { |
||||
IdentifierExpression ident = ae.Left as IdentifierExpression; |
||||
if (ident != null && ident.Identifier == variableName) { |
||||
return !UsesVariable(ae.Right, variableName); |
||||
} |
||||
} |
||||
} |
||||
} |
||||
|
||||
UsingStatement usingStatement = stmt as UsingStatement; |
||||
if (usingStatement != null) { |
||||
// using-statement is special case: we can move variable declarations into the initializer
|
||||
AssignmentExpression ae = usingStatement.ResourceAcquisition as AssignmentExpression; |
||||
if (ae != null && ae.Operator == AssignmentOperatorType.Assign) { |
||||
IdentifierExpression ident = ae.Left as IdentifierExpression; |
||||
if (ident != null && ident.Identifier == variableName) { |
||||
return !UsesVariable(ae.Right, variableName); |
||||
} |
||||
} |
||||
} |
||||
|
||||
// We can move the variable into a sub-block only if the variable is used in only that sub-block (and not in expressions such as the loop condition)
|
||||
for (AstNode child = stmt.FirstChild; child != null; child = child.NextSibling) { |
||||
if (!(child is BlockStatement) && UsesVariable(child, variableName)) { |
||||
if (HasNestedBlocks(child)) { |
||||
// catch clauses/switch sections can contain nested blocks
|
||||
for (AstNode grandchild = child.FirstChild; grandchild != null; grandchild = grandchild.NextSibling) { |
||||
if (!(grandchild is BlockStatement) && UsesVariable(grandchild, variableName)) |
||||
return false; |
||||
} |
||||
} else { |
||||
return false; |
||||
} |
||||
} |
||||
} |
||||
return true; |
||||
} |
||||
|
||||
static bool HasNestedBlocks(AstNode node) |
||||
{ |
||||
return node is CatchClause || node is SwitchSection; |
||||
} |
||||
|
||||
static bool UsesVariable(AstNode node, string variableName) |
||||
{ |
||||
IdentifierExpression ie = node as IdentifierExpression; |
||||
if (ie != null && ie.Identifier == variableName) |
||||
return true; |
||||
|
||||
FixedStatement fixedStatement = node as FixedStatement; |
||||
if (fixedStatement != null) { |
||||
foreach (VariableInitializer v in fixedStatement.Variables) { |
||||
if (v.Name == variableName) |
||||
return false; // no need to introduce the variable here
|
||||
} |
||||
} |
||||
|
||||
ForeachStatement foreachStatement = node as ForeachStatement; |
||||
if (foreachStatement != null) { |
||||
if (foreachStatement.VariableName == variableName) |
||||
return false; // no need to introduce the variable here
|
||||
} |
||||
|
||||
UsingStatement usingStatement = node as UsingStatement; |
||||
if (usingStatement != null) { |
||||
VariableDeclarationStatement varDecl = usingStatement.ResourceAcquisition as VariableDeclarationStatement; |
||||
if (varDecl != null) { |
||||
foreach (VariableInitializer v in varDecl.Variables) { |
||||
if (v.Name == variableName) |
||||
return false; // no need to introduce the variable here
|
||||
} |
||||
} |
||||
} |
||||
|
||||
CatchClause catchClause = node as CatchClause; |
||||
if (catchClause != null && catchClause.VariableName == variableName) { |
||||
return false; // no need to introduce the variable here
|
||||
} |
||||
|
||||
for (AstNode child = node.FirstChild; child != null; child = child.NextSibling) { |
||||
if (UsesVariable(child, variableName)) |
||||
return true; |
||||
} |
||||
return false; |
||||
} |
||||
} |
||||
} |
||||
@ -0,0 +1,116 @@
@@ -0,0 +1,116 @@
|
||||
// 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.Collections.Generic; |
||||
using System.Collections.ObjectModel; |
||||
using System.Linq; |
||||
|
||||
using ICSharpCode.NRefactory.TypeSystem; |
||||
using ICSharpCode.NRefactory.TypeSystem.Implementation; |
||||
|
||||
namespace ICSharpCode.NRefactory.CSharp.Analysis |
||||
{ |
||||
/// <summary>
|
||||
/// Resolve context represents the minimal mscorlib required for evaluating constants.
|
||||
/// </summary>
|
||||
sealed class MinimalResolveContext : IProjectContent, ISynchronizedTypeResolveContext |
||||
{ |
||||
static readonly Lazy<MinimalResolveContext> instance = new Lazy<MinimalResolveContext>(() => new MinimalResolveContext()); |
||||
|
||||
public static MinimalResolveContext Instance { |
||||
get { return instance.Value; } |
||||
} |
||||
|
||||
readonly ReadOnlyCollection<string> namespaces = Array.AsReadOnly(new string[] { "System" }); |
||||
readonly IAttribute[] assemblyAttributes = new IAttribute[0]; |
||||
readonly ITypeDefinition systemObject, systemValueType; |
||||
readonly ReadOnlyCollection<ITypeDefinition> types; |
||||
|
||||
private MinimalResolveContext() |
||||
{ |
||||
List<ITypeDefinition> types = new List<ITypeDefinition>(); |
||||
types.Add(systemObject = new DefaultTypeDefinition(this, "System", "Object")); |
||||
types.Add(systemValueType = new DefaultTypeDefinition(this, "System", "ValueType") { BaseTypes = { systemObject } }); |
||||
types.Add(CreateStruct("System", "Boolean")); |
||||
types.Add(CreateStruct("System", "SByte")); |
||||
types.Add(CreateStruct("System", "Byte")); |
||||
types.Add(CreateStruct("System", "Int16")); |
||||
types.Add(CreateStruct("System", "UInt16")); |
||||
types.Add(CreateStruct("System", "Int32")); |
||||
types.Add(CreateStruct("System", "UInt32")); |
||||
types.Add(CreateStruct("System", "Int64")); |
||||
types.Add(CreateStruct("System", "UInt64")); |
||||
types.Add(CreateStruct("System", "Single")); |
||||
types.Add(CreateStruct("System", "Double")); |
||||
types.Add(CreateStruct("System", "Decimal")); |
||||
types.Add(new DefaultTypeDefinition(this, "System", "String") { BaseTypes = { systemObject } }); |
||||
foreach (ITypeDefinition type in types) |
||||
type.Freeze(); |
||||
this.types = types.AsReadOnly(); |
||||
} |
||||
|
||||
ITypeDefinition CreateStruct(string nameSpace, string name) |
||||
{ |
||||
return new DefaultTypeDefinition(this, nameSpace, name) { |
||||
ClassType = ClassType.Struct, |
||||
BaseTypes = { systemValueType } |
||||
}; |
||||
} |
||||
|
||||
public ITypeDefinition GetClass(string nameSpace, string name, int typeParameterCount, StringComparer nameComparer) |
||||
{ |
||||
foreach (ITypeDefinition type in types) { |
||||
if (nameComparer.Equals(type.Name, name) && nameComparer.Equals(type.Namespace, nameSpace) && type.TypeParameterCount == typeParameterCount) |
||||
return type; |
||||
} |
||||
return null; |
||||
} |
||||
|
||||
public IEnumerable<ITypeDefinition> GetClasses() |
||||
{ |
||||
return types; |
||||
} |
||||
|
||||
public IEnumerable<ITypeDefinition> GetClasses(string nameSpace, StringComparer nameComparer) |
||||
{ |
||||
return types.Where(t => nameComparer.Equals(t.Namespace, nameSpace)); |
||||
} |
||||
|
||||
public IEnumerable<string> GetNamespaces() |
||||
{ |
||||
return namespaces; |
||||
} |
||||
|
||||
public string GetNamespace(string nameSpace, StringComparer nameComparer) |
||||
{ |
||||
foreach (string ns in namespaces) { |
||||
if (nameComparer.Equals(ns, nameSpace)) |
||||
return ns; |
||||
} |
||||
return null; |
||||
} |
||||
|
||||
public IList<IAttribute> AssemblyAttributes { |
||||
get { return assemblyAttributes; } |
||||
} |
||||
|
||||
ICSharpCode.NRefactory.Utils.CacheManager ITypeResolveContext.CacheManager { |
||||
get { |
||||
// We don't support caching
|
||||
return null; |
||||
} |
||||
} |
||||
|
||||
ISynchronizedTypeResolveContext ITypeResolveContext.Synchronize() |
||||
{ |
||||
// This class is immutable
|
||||
return this; |
||||
} |
||||
|
||||
void IDisposable.Dispose() |
||||
{ |
||||
// exit from Synchronize() block
|
||||
} |
||||
} |
||||
} |
||||
Loading…
Reference in new issue