Browse Source

Improved handling of captured variables.

pull/728/merge
Siegfried Pammer 9 years ago
parent
commit
41bcf920d3
  1. 1
      ICSharpCode.Decompiler/CSharp/StatementBuilder.cs
  2. 52
      ICSharpCode.Decompiler/CSharp/Transforms/DeclareVariables.cs
  3. 4
      ICSharpCode.Decompiler/CSharp/Transforms/PatternStatementTransform.cs
  4. 4
      ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj
  5. 3
      ICSharpCode.Decompiler/IL/ILVariable.cs
  6. 32
      ICSharpCode.Decompiler/IL/Transforms/DelegateConstruction.cs
  7. 47
      ICSharpCode.Decompiler/Tests/TestCases/Correctness/Capturing.cs
  8. 1
      ICSharpCode.Decompiler/packages.config

1
ICSharpCode.Decompiler/CSharp/StatementBuilder.cs

@ -259,6 +259,7 @@ namespace ICSharpCode.Decompiler.CSharp @@ -259,6 +259,7 @@ namespace ICSharpCode.Decompiler.CSharp
var oldContinueCount = continueCount;
var oldBreakTarget = breakTarget;
var loop = ConvertLoop(container);
loop.AddAnnotation(container);
continueTarget = oldContinueTarget;
continueCount = oldContinueCount;
breakTarget = oldBreakTarget;

52
ICSharpCode.Decompiler/CSharp/Transforms/DeclareVariables.cs

@ -112,8 +112,10 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms @@ -112,8 +112,10 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms
this.context = null;
}
}
#region FindInsertionPoints
List<(InsertionPoint InsertionPoint, BlockContainer Loop)> loopTracking = new List<(InsertionPoint, BlockContainer)>();
/// <summary>
/// Finds insertion points for all variables used within `node`
/// and adds them to the variableDict.
@ -126,24 +128,42 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms @@ -126,24 +128,42 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms
/// </remarks>
void FindInsertionPoints(AstNode node, int nodeLevel)
{
for (AstNode child = node.FirstChild; child != null; child = child.NextSibling) {
FindInsertionPoints(child, nodeLevel + 1);
BlockContainer loop = node.Annotation<BlockContainer>();
if (loop != null) {
loopTracking.Add((new InsertionPoint { level = nodeLevel, nextNode = node }, loop));
}
var identExpr = node as IdentifierExpression;
if (identExpr != null) {
var rr = identExpr.GetResolveResult() as ILVariableResolveResult;
if (rr != null && VariableNeedsDeclaration(rr.Variable.Kind)) {
var newPoint = new InsertionPoint { level = nodeLevel, nextNode = identExpr };
VariableToDeclare v;
if (variableDict.TryGetValue(rr.Variable, out v)) {
v.InsertionPoint = FindCommonParent(v.InsertionPoint, newPoint);
} else {
v = new VariableToDeclare(
rr.Variable.Type, rr.Variable.Name, rr.Variable.HasInitialValue,
newPoint, sourceOrder: variableDict.Count);
variableDict.Add(rr.Variable, v);
try {
for (AstNode child = node.FirstChild; child != null; child = child.NextSibling) {
FindInsertionPoints(child, nodeLevel + 1);
}
var identExpr = node as IdentifierExpression;
if (identExpr != null) {
var rr = identExpr.GetResolveResult() as ILVariableResolveResult;
if (rr != null && VariableNeedsDeclaration(rr.Variable.Kind)) {
var variable = rr.Variable;
InsertionPoint newPoint;
int startIndex = loopTracking.Count - 1;
if (variable.CaptureScope != null && startIndex > -1 && variable.CaptureScope != loopTracking[startIndex].Loop) {
while (startIndex > -1 && loopTracking[startIndex].Loop != variable.CaptureScope)
startIndex--;
newPoint = loopTracking[startIndex + 1].InsertionPoint;
} else {
newPoint = new InsertionPoint { level = nodeLevel, nextNode = identExpr };
}
VariableToDeclare v;
if (variableDict.TryGetValue(rr.Variable, out v)) {
v.InsertionPoint = FindCommonParent(v.InsertionPoint, newPoint);
} else {
v = new VariableToDeclare(
rr.Variable.Type, rr.Variable.Name, rr.Variable.HasInitialValue,
newPoint, sourceOrder: variableDict.Count);
variableDict.Add(rr.Variable, v);
}
}
}
} finally {
if (loop != null)
loopTracking.RemoveAt(loopTracking.Count - 1);
}
}

4
ICSharpCode.Decompiler/CSharp/Transforms/PatternStatementTransform.cs

@ -451,6 +451,7 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms @@ -451,6 +451,7 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms
InExpression = m.Get<Expression>("collection").Single().Detach(),
EmbeddedStatement = newBody
}.WithAnnotation(itemVarDecl.Variables.Single().Annotation<IL.ILVariable>());
foreachStatement.CopyAnnotationsFrom(loop);
if (foreachStatement.InExpression is BaseReferenceExpression) {
foreachStatement.InExpression = new ThisReferenceExpression().CopyAnnotationsFrom(foreachStatement.InExpression);
}
@ -547,6 +548,7 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms @@ -547,6 +548,7 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms
}.WithAnnotation(itemVarDecl.Variables.Single().Annotation<IL.ILVariable>());
BlockStatement body = new BlockStatement();
foreachStatement.EmbeddedStatement = body;
foreachStatement.CopyAnnotationsFrom(loop);
((BlockStatement)node.Parent).Statements.InsertBefore(node, foreachStatement);
body.Add(node.Detach());
@ -639,6 +641,7 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms @@ -639,6 +641,7 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms
foreach (Statement stmt in m3.Get<Statement>("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<Statement>("increment").Single().Detach());
@ -671,6 +674,7 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms @@ -671,6 +674,7 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms
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:

4
ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj

@ -62,6 +62,10 @@ @@ -62,6 +62,10 @@
<Reference Include="System.Core">
<RequiredTargetFramework>3.5</RequiredTargetFramework>
</Reference>
<Reference Include="System.ValueTuple, Version=4.0.1.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL">
<HintPath>..\packages\System.ValueTuple.4.3.0\lib\netstandard1.0\System.ValueTuple.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="System.Xml" />
<Reference Include="System.Xml.Linq">
<RequiredTargetFramework>3.5</RequiredTargetFramework>

3
ICSharpCode.Decompiler/IL/ILVariable.cs

@ -220,6 +220,9 @@ namespace ICSharpCode.Decompiler.IL @@ -220,6 +220,9 @@ namespace ICSharpCode.Decompiler.IL
if (hasInitialValue && Kind != VariableKind.Parameter) {
output.Write(" init");
}
if (CaptureScope != null) {
output.Write(" captured in " + CaptureScope.EntryPoint.Label);
}
}
internal void WriteTo(ITextOutput output)

32
ICSharpCode.Decompiler/IL/Transforms/DelegateConstruction.cs

@ -57,13 +57,14 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -57,13 +57,14 @@ namespace ICSharpCode.Decompiler.IL.Transforms
// TODO : it is probably not a good idea to remove *all* display-classes
// is there a way to minimize the false-positives?
if (newObj != null && IsSimpleDisplayClass(newObj.Method)) {
targetVariable.CaptureScope = FindBlockContainer(block);
targetsToReplace.Add((IInstructionWithVariableOperand)block.Instructions[i]);
}
}
}
}
foreach (var target in targetsToReplace.OrderByDescending(t => ((ILInstruction)t).ILRange.Start)) {
function.AcceptVisitor(new TransformDisplayClassUsages(target, orphanedVariableInits));
function.AcceptVisitor(new TransformDisplayClassUsages(target, target.Variable.CaptureScope, orphanedVariableInits));
}
foreach (var store in orphanedVariableInits) {
ILInstruction containingBlock = store.Parent as Block;
@ -72,6 +73,17 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -72,6 +73,17 @@ namespace ICSharpCode.Decompiler.IL.Transforms
}
}
private BlockContainer FindBlockContainer(Block block)
{
ILInstruction current = block;
while (current != null) {
current = current.Parent;
if (current is BlockContainer)
return (BlockContainer)current;
}
throw new NotSupportedException();
}
bool IsSimpleDisplayClass(IMethod method)
{
if (!method.IsCompilerGeneratedOrIsInCompilerGeneratedClass())
@ -181,6 +193,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -181,6 +193,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms
class TransformDisplayClassUsages : ILVisitor
{
ILFunction currentFunction;
BlockContainer captureScope;
readonly IInstructionWithVariableOperand targetLoad;
readonly List<ILVariable> targetAndCopies = new List<ILVariable>();
readonly List<ILInstruction> orphanedVariableInits;
@ -192,9 +205,10 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -192,9 +205,10 @@ namespace ICSharpCode.Decompiler.IL.Transforms
public ILInstruction value;
}
public TransformDisplayClassUsages(IInstructionWithVariableOperand targetLoad, List<ILInstruction> orphanedVariableInits)
public TransformDisplayClassUsages(IInstructionWithVariableOperand targetLoad, BlockContainer captureScope, List<ILInstruction> orphanedVariableInits)
{
this.targetLoad = targetLoad;
this.captureScope = captureScope;
this.orphanedVariableInits = orphanedVariableInits;
this.targetAndCopies.Add(targetLoad.Variable);
}
@ -242,16 +256,17 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -242,16 +256,17 @@ namespace ICSharpCode.Decompiler.IL.Transforms
return;
field = (IField)field.MemberDefinition;
DisplayClassVariable info;
if (initValues.TryGetValue(field, out info) && info.variable != null) {
ILInstruction value;
if (initValues.TryGetValue(field, out info)) {
inst.ReplaceWith(new StLoc(info.variable, inst.Value));
} else {
ILInstruction value;
ILVariable v;
if (inst.Value.MatchLdLoc(out v) && v.Kind != VariableKind.PinnedLocal) {
if (inst.Value.MatchLdLoc(out var v) && v.Kind == VariableKind.Parameter) {
// special case for parameters: remove copies of parameter values.
orphanedVariableInits.Add(inst);
value = inst.Value;
} else {
v = currentFunction.RegisterVariable(VariableKind.Local, field.Type, field.Name);
v.CaptureScope = captureScope;
inst.ReplaceWith(new StLoc(v, inst.Value));
value = new LdLoc(v);
}
@ -283,11 +298,12 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -283,11 +298,12 @@ namespace ICSharpCode.Decompiler.IL.Transforms
DisplayClassVariable info;
if (!initValues.TryGetValue(field, out info)) {
var v = currentFunction.RegisterVariable(VariableKind.Local, field.Type, field.Name);
v.CaptureScope = captureScope;
inst.ReplaceWith(new LdLoca(v));
var value = new LdLoc(v);
initValues.Add(field, new DisplayClassVariable { value = value, variable = v });
} else if (info.value is LdLoc) {
inst.ReplaceWith(new LdLoca(((LdLoc)info.value).Variable));
} else if (info.value is LdLoc l) {
inst.ReplaceWith(new LdLoca(l.Variable));
} else {
throw new NotImplementedException();
}

47
ICSharpCode.Decompiler/Tests/TestCases/Correctness/Capturing.cs

@ -11,6 +11,9 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Correctness @@ -11,6 +11,9 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Correctness
static void Main(string[] args)
{
TestCase1();
TestCase2();
TestCase3();
TestCase4("TestCase4");
}
static void TestCase1()
@ -32,6 +35,50 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Correctness @@ -32,6 +35,50 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Correctness
action();
}
static void TestCase2()
{
Console.WriteLine("TestCase2");
List<Action> actions = new List<Action>();
int max = 5;
string line;
while (ReadLine(out line, ref max)) {
string capture = line;
actions.Add(() => Console.WriteLine(capture));
}
// line still declared
line = null;
Console.WriteLine("----");
foreach (var action in actions)
action();
}
static void TestCase3()
{
Console.WriteLine("TestCase3");
List<Action> actions = new List<Action>();
int max = 5;
string line, capture;
while (ReadLine(out line, ref max)) {
capture = line;
actions.Add(() => Console.WriteLine(capture));
}
// line still declared
line = null;
Console.WriteLine("----");
foreach (var action in actions)
action();
}
static void TestCase4(string capture)
{
Console.WriteLine("TestCase4");
List<Action> actions = new List<Action>();
actions.Add(() => Console.WriteLine(capture));
Console.WriteLine("----");
foreach (var action in actions)
action();
}
private static bool ReadLine(out string line, ref int v)
{
line = v + " line";

1
ICSharpCode.Decompiler/packages.config

@ -1,4 +1,5 @@ @@ -1,4 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="System.Collections.Immutable" version="1.3.0" targetFramework="net45" />
<package id="System.ValueTuple" version="4.3.0" targetFramework="net45" />
</packages>
Loading…
Cancel
Save