Browse Source

Improved handling of nested display classes.

pull/124/head
Daniel Grunwald 14 years ago
parent
commit
542bb9db2f
  1. 72
      ICSharpCode.Decompiler/Ast/Transforms/DelegateConstruction.cs
  2. 2
      ICSharpCode.Decompiler/Ast/Transforms/PatternStatementTransform.cs
  3. 63
      ICSharpCode.Decompiler/Tests/DelegateConstruction.cs

72
ICSharpCode.Decompiler/Ast/Transforms/DelegateConstruction.cs

@ -296,50 +296,54 @@ namespace ICSharpCode.Decompiler.Ast.Transforms @@ -296,50 +296,54 @@ namespace ICSharpCode.Decompiler.Ast.Transforms
// Delete the variable declaration statement:
AstNode cur = stmt.NextSibling;
stmt.Remove();
if (blockStatement.Parent.NodeType == NodeType.Member || blockStatement.Parent is Accessor) {
// Delete any following statements as long as they assign parameters to the display class
// Do parameter handling only for closures created in the top scope (direct child of method/accessor)
List<ILVariable> parameterOccurrances = blockStatement.Descendants.OfType<IdentifierExpression>()
.Select(n => n.Annotation<ILVariable>()).Where(p => p != null && p.IsParameter).ToList();
AstNode next;
for (; cur != null; cur = next) {
next = cur.NextSibling;
// Test for the pattern:
// "variableName.MemberName = right;"
ExpressionStatement closureFieldAssignmentPattern = new ExpressionStatement(
new AssignmentExpression(
new NamedNode("left", new MemberReferenceExpression { Target = new IdentifierExpression(variable.Name) }),
new AnyNode("right")
)
);
Match m = closureFieldAssignmentPattern.Match(cur);
if (m != null) {
AstNode right = m.Get("right").Single();
bool isParameter = false;
if (right is ThisReferenceExpression) {
isParameter = true;
} else if (right is IdentifierExpression) {
// handle parameters only if the whole method contains no other occurrance except for 'right'
ILVariable param = right.Annotation<ILVariable>();
isParameter = param.IsParameter && parameterOccurrances.Count(c => c == param) == 1;
}
if (isParameter) {
dict[m.Get<MemberReferenceExpression>("left").Single().Annotation<FieldReference>().ResolveWithinSameModule()] = right;
cur.Remove();
} else {
break;
// Delete any following statements as long as they assign parameters to the display class
BlockStatement rootBlock = blockStatement.Ancestors.OfType<BlockStatement>().LastOrDefault() ?? blockStatement;
List<ILVariable> parameterOccurrances = rootBlock.Descendants.OfType<IdentifierExpression>()
.Select(n => n.Annotation<ILVariable>()).Where(p => p != null && p.IsParameter).ToList();
AstNode next;
for (; cur != null; cur = next) {
next = cur.NextSibling;
// Test for the pattern:
// "variableName.MemberName = right;"
ExpressionStatement closureFieldAssignmentPattern = new ExpressionStatement(
new AssignmentExpression(
new NamedNode("left", new MemberReferenceExpression { Target = new IdentifierExpression(variable.Name) }),
new AnyNode("right")
)
);
Match m = closureFieldAssignmentPattern.Match(cur);
if (m != null) {
FieldDefinition fieldDef = m.Get<MemberReferenceExpression>("left").Single().Annotation<FieldReference>().ResolveWithinSameModule();
AstNode right = m.Get("right").Single();
bool isParameter = false;
bool isDisplayClassParentPointerAssignment = false;
if (right is ThisReferenceExpression) {
isParameter = true;
} else if (right is IdentifierExpression) {
// handle parameters only if the whole method contains no other occurrance except for 'right'
ILVariable v = right.Annotation<ILVariable>();
isParameter = v.IsParameter && parameterOccurrances.Count(c => c == v) == 1;
if (!isParameter && TypeAnalysis.IsSameType(v.Type, fieldDef.FieldType) && IsPotentialClosure(context, v.Type.ResolveWithinSameModule())) {
isDisplayClassParentPointerAssignment = true;
}
}
if (isParameter || isDisplayClassParentPointerAssignment) {
dict[fieldDef] = right;
cur.Remove();
} else {
break;
}
} else {
break;
}
}
// Now create variables for all fields of the display class (except for those that we already handled as parameters)
List<Tuple<AstType, string>> variablesToDeclare = new List<Tuple<AstType, string>>();
foreach (FieldDefinition field in type.Fields) {
if (dict.ContainsKey(field))
if (dict.ContainsKey(field)) // skip field if it already was handled as parameter
continue;
EnsureVariableNameIsAvailable(blockStatement, field.Name);
currentlyUsedVariableNames.Add(field.Name);

2
ICSharpCode.Decompiler/Ast/Transforms/PatternStatementTransform.cs

@ -299,6 +299,8 @@ namespace ICSharpCode.Decompiler.Ast.Transforms @@ -299,6 +299,8 @@ namespace ICSharpCode.Decompiler.Ast.Transforms
InExpression = m.Get<Expression>("collection").Single().Detach(),
EmbeddedStatement = newBody
};
if (foreachStatement.InExpression is BaseReferenceExpression)
foreachStatement.InExpression = new ThisReferenceExpression();
node.ReplaceWith(foreachStatement);
foreach (Statement stmt in m.Get<Statement>("variablesOutsideLoop")) {
((BlockStatement)foreachStatement.Parent).Statements.InsertAfter(null, stmt.Detach());

63
ICSharpCode.Decompiler/Tests/DelegateConstruction.cs

@ -3,43 +3,82 @@ @@ -3,43 +3,82 @@
using System;
using System.Collections.Generic;
using System.Linq;
public static class DelegateConstruction
{
class InstanceTests
{
public Action CaptureOfThis()
{
return delegate {
CaptureOfThis();
};
}
public Action CaptureOfThisAndParameter(int a)
{
return delegate {
CaptureOfThisAndParameter(a);
};
}
public Action CaptureOfThisAndParameterInForEach(int a)
{
foreach (var item in Enumerable.Empty<int>()) {
return delegate {
CaptureOfThisAndParameter(item + a);
};
}
return null;
}
public Action CaptureOfThisAndParameterInForEachWithItemCopy(int a)
{
foreach (var item in Enumerable.Empty<int>()) {
int copyOfItem = item;
return delegate {
CaptureOfThisAndParameter(item + a + copyOfItem);
};
}
return null;
}
}
public static void Test(this string a)
{
}
public static Action<string> ExtensionMethodUnbound()
{
return new Action<string>(DelegateConstruction.Test);
}
public static Action ExtensionMethodBound()
{
return new Action("abc".Test);
}
public static Action ExtensionMethodBoundOnNull()
{
return new Action(((string)null).Test);
}
public static object StaticMethod()
{
return new Func<Action>(DelegateConstruction.ExtensionMethodBound);
}
public static object InstanceMethod()
{
return new Func<string>("hello".ToUpper);
}
public static object InstanceMethodOnNull()
{
return new Func<string>(((string)null).ToUpper);
}
public static List<Action<int>> AnonymousMethodStoreWithinLoop()
{
List<Action<int>> list = new List<Action<int>>();
@ -54,7 +93,7 @@ public static class DelegateConstruction @@ -54,7 +93,7 @@ public static class DelegateConstruction
}
return list;
}
public static List<Action<int>> AnonymousMethodStoreOutsideLoop()
{
List<Action<int>> list = new List<Action<int>>();
@ -69,7 +108,7 @@ public static class DelegateConstruction @@ -69,7 +108,7 @@ public static class DelegateConstruction
}
return list;
}
public static Action StaticAnonymousMethodNoClosure()
{
return delegate
@ -77,7 +116,7 @@ public static class DelegateConstruction @@ -77,7 +116,7 @@ public static class DelegateConstruction
Console.WriteLine();
};
}
public static void NameConflict()
{
// i is captured variable,
@ -98,7 +137,7 @@ public static class DelegateConstruction @@ -98,7 +137,7 @@ public static class DelegateConstruction
}
}
}
public static void NameConflict2(int j)
{
List<Action<int>> list = new List<Action<int>>();
@ -109,7 +148,7 @@ public static class DelegateConstruction @@ -109,7 +148,7 @@ public static class DelegateConstruction
});
}
}
public static Action<int> NameConflict3(int i)
{
return delegate(int j) {

Loading…
Cancel
Save