Browse Source

Implemented decompilation of 'lock'.

pull/100/head
Daniel Grunwald 15 years ago
parent
commit
2892c9d50b
  1. 113
      ICSharpCode.Decompiler/Ast/Transforms/PatternStatementTransform.cs
  2. 2
      ICSharpCode.Decompiler/Ast/Transforms/ReplaceMethodCallsWithOperators.cs
  3. 15
      ICSharpCode.Decompiler/DecompilerSettings.cs
  4. 26
      ICSharpCode.Decompiler/ILAst/ILInlining.cs
  5. 56
      ICSharpCode.Decompiler/ILAst/PeepholeTransform.cs
  6. 6
      ICSharpCode.Decompiler/ILAst/YieldReturnDecompiler.cs
  7. 15
      ICSharpCode.Decompiler/Tests/YieldReturn.cs

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

@ -31,6 +31,8 @@ namespace ICSharpCode.Decompiler.Ast.Transforms @@ -31,6 +31,8 @@ namespace ICSharpCode.Decompiler.Ast.Transforms
TransformForeach(compilationUnit);
TransformFor(compilationUnit);
TransformDoWhile(compilationUnit);
if (context.Settings.LockStatement)
TransformLock(compilationUnit);
if (context.Settings.AutomaticProperties)
TransformAutomaticProperties(compilationUnit);
if (context.Settings.AutomaticEvents)
@ -300,6 +302,83 @@ namespace ICSharpCode.Decompiler.Ast.Transforms @@ -300,6 +302,83 @@ namespace ICSharpCode.Decompiler.Ast.Transforms
}
#endregion
#region lock
static readonly AstNode lockFlagInitPattern = new VariableDeclarationStatement {
Type = new PrimitiveType("bool"),
Variables = {
new NamedNode(
"variable",
new VariableInitializer {
Initializer = new PrimitiveExpression(false)
}
)
}};
static readonly AstNode lockTryCatchPattern = new TryCatchStatement {
TryBlock = new BlockStatement {
new TypePattern(typeof(System.Threading.Monitor)).ToType().Invoke(
"Enter", new AnyNode("enter"),
new DirectionExpression {
FieldDirection = FieldDirection.Ref,
Expression = new NamedNode("flag", new IdentifierExpression())
}),
new Repeat(new AnyNode()).ToStatement()
},
FinallyBlock = new BlockStatement {
new IfElseStatement {
Condition = new Backreference("flag"),
TrueStatement = new BlockStatement {
new TypePattern(typeof(System.Threading.Monitor)).ToType().Invoke("Exit", new NamedNode("exit", new IdentifierExpression()))
}
}
}};
public void TransformLock(AstNode compilationUnit)
{
foreach (AstNode node in compilationUnit.Descendants.ToArray()) {
Match m1 = lockFlagInitPattern.Match(node);
if (m1 == null) continue;
AstNode tryCatch = node.NextSibling;
while (simpleVariableDefinition.Match(tryCatch) != null)
tryCatch = tryCatch.NextSibling;
Match m2 = lockTryCatchPattern.Match(tryCatch);
if (m2 == null) continue;
if (m1.Get<VariableInitializer>("variable").Single().Name == m2.Get<IdentifierExpression>("flag").Single().Identifier) {
Expression enter = m2.Get<Expression>("enter").Single();
IdentifierExpression exit = m2.Get<IdentifierExpression>("exit").Single();
if (exit.Match(enter) == null) {
// If exit and enter are not the same, then enter must be "exit = ..."
AssignmentExpression assign = enter as AssignmentExpression;
if (assign == null)
continue;
if (exit.Match(assign.Left) == null)
continue;
enter = assign.Right;
// Remove 'exit' variable:
bool ok = false;
for (AstNode tmp = node.NextSibling; tmp != tryCatch; tmp = tmp.NextSibling) {
VariableDeclarationStatement v = (VariableDeclarationStatement)tmp;
if (v.Variables.Single().Name == exit.Identifier) {
ok = true;
v.Remove();
break;
}
}
if (!ok)
continue;
}
// transform the code into a lock statement:
LockStatement l = new LockStatement();
l.Expression = enter.Detach();
l.EmbeddedStatement = ((TryCatchStatement)tryCatch).TryBlock.Detach();
((BlockStatement)l.EmbeddedStatement).Statements.First().Remove(); // Remove 'Enter()' call
tryCatch.ReplaceWith(l);
node.Remove(); // remove flag variable
}
}
}
#endregion
#region Automatic Properties
static readonly PropertyDeclaration automaticPropertyPattern = new PropertyDeclaration {
Attributes = { new Repeat(new AnyNode()) },
@ -391,7 +470,7 @@ namespace ICSharpCode.Decompiler.Ast.Transforms @@ -391,7 +470,7 @@ namespace ICSharpCode.Decompiler.Ast.Transforms
}},
new AssignmentExpression {
Left = new IdentifierExpressionBackreference("var1"),
Right = new AnyNode("Interlocked").ToType().Invoke(
Right = new TypePattern(typeof(System.Threading.Interlocked)).ToType().Invoke(
"CompareExchange",
new AstType[] { new Backreference("type") }, // type argument
new Expression[] { // arguments
@ -419,10 +498,7 @@ namespace ICSharpCode.Decompiler.Ast.Transforms @@ -419,10 +498,7 @@ namespace ICSharpCode.Decompiler.Ast.Transforms
var combineMethod = m.Get("delegateCombine").Single().Parent.Annotation<MethodReference>();
if (combineMethod == null || combineMethod.Name != (isAddAccessor ? "Combine" : "Remove"))
return false;
if (combineMethod.DeclaringType.FullName != "System.Delegate")
return false;
var ice = m.Get("Interlocked").Single().Annotation<TypeReference>();
return ice != null && ice.FullName == "System.Threading.Interlocked";
return combineMethod.DeclaringType.FullName == "System.Delegate";
}
void TransformAutomaticEvents(AstNode compilationUnit)
@ -454,5 +530,32 @@ namespace ICSharpCode.Decompiler.Ast.Transforms @@ -454,5 +530,32 @@ namespace ICSharpCode.Decompiler.Ast.Transforms
}
}
#endregion
#region Pattern Matching Helpers
sealed class TypePattern : Pattern
{
readonly string ns;
readonly string name;
public TypePattern(Type type)
{
this.ns = type.Namespace;
this.name = type.Name;
}
protected override bool DoMatch(AstNode other, Match match)
{
if (other == null)
return false;
TypeReference tr = other.Annotation<TypeReference>();
return tr != null && tr.Namespace == ns && tr.Name == name;
}
public override S AcceptVisitor<T, S>(IAstVisitor<T, S> visitor, T data)
{
throw new NotImplementedException();
}
}
#endregion
}
}

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

@ -205,7 +205,7 @@ namespace ICSharpCode.Decompiler.Ast.Transforms @@ -205,7 +205,7 @@ namespace ICSharpCode.Decompiler.Ast.Transforms
return null;
}
bool IsWithoutSideEffects(Expression left)
static bool IsWithoutSideEffects(Expression left)
{
if (left is ThisReferenceExpression)
return true;

15
ICSharpCode.Decompiler/DecompilerSettings.cs

@ -101,6 +101,21 @@ namespace ICSharpCode.Decompiler @@ -101,6 +101,21 @@ namespace ICSharpCode.Decompiler
}
}
bool lockStatement = true;
/// <summary>
/// Decompile lock statements.
/// </summary>
public bool LockStatement {
get { return lockStatement; }
set {
if (lockStatement != value) {
lockStatement = value;
OnPropertyChanged("LockStatement");
}
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged(string propertyName)

26
ICSharpCode.Decompiler/ILAst/ILInlining.cs

@ -15,8 +15,8 @@ namespace ICSharpCode.Decompiler.ILAst @@ -15,8 +15,8 @@ namespace ICSharpCode.Decompiler.ILAst
public class ILInlining
{
readonly ILBlock method;
Dictionary<ILVariable, int> numStloc = new Dictionary<ILVariable, int>();
Dictionary<ILVariable, int> numLdloc = new Dictionary<ILVariable, int>();
internal Dictionary<ILVariable, int> numStloc = new Dictionary<ILVariable, int>();
internal Dictionary<ILVariable, int> numLdloc = new Dictionary<ILVariable, int>();
Dictionary<ILVariable, int> numLdloca = new Dictionary<ILVariable, int>();
Dictionary<ParameterDefinition, int> numStarg = new Dictionary<ParameterDefinition, int>();
@ -64,7 +64,7 @@ namespace ICSharpCode.Decompiler.ILAst @@ -64,7 +64,7 @@ namespace ICSharpCode.Decompiler.ILAst
ILExpression nextExpr = body[i + 1] as ILExpression;
ILVariable locVar;
ILExpression expr;
if (body[i].Match(ILCode.Stloc, out locVar, out expr) && InlineIfPossible(block, i, aggressive: false)) {
if (body[i].Match(ILCode.Stloc, out locVar, out expr) && InlineOneIfPossible(block, i, aggressive: false)) {
i = Math.Max(0, i - 1); // Go back one step
} else {
i++;
@ -85,7 +85,7 @@ namespace ICSharpCode.Decompiler.ILAst @@ -85,7 +85,7 @@ namespace ICSharpCode.Decompiler.ILAst
ILExpression expr = block.Body[pos] as ILExpression;
if (expr == null || expr.Code != ILCode.Stloc)
break;
if (InlineIfPossible(block, pos, aggressive))
if (InlineOneIfPossible(block, pos, aggressive))
count++;
else
break;
@ -93,10 +93,26 @@ namespace ICSharpCode.Decompiler.ILAst @@ -93,10 +93,26 @@ namespace ICSharpCode.Decompiler.ILAst
return count;
}
/// <summary>
/// Aggressively inlines the stloc instruction at block.Body[pos] into the next instruction, if possible.
/// If inlining was possible; we will continue to inline (non-aggressively) into the the combined instruction.
/// </summary>
/// <remarks>
/// After the operation, pos will point to the new combined instruction.
/// </remarks>
public bool InlineIfPossible(ILBlock block, ref int pos)
{
if (InlineOneIfPossible(block, pos, true)) {
pos -= InlineInto(block, pos, false);
return true;
}
return false;
}
/// <summary>
/// Inlines the stloc instruction at block.Body[pos] into the next instruction, if possible.
/// </summary>
public bool InlineIfPossible(ILBlock block, int pos, bool aggressive)
public bool InlineOneIfPossible(ILBlock block, int pos, bool aggressive)
{
ILVariable v;
ILExpression inlinedExpression;

56
ICSharpCode.Decompiler/ILAst/PeepholeTransform.cs

@ -27,7 +27,8 @@ namespace ICSharpCode.Decompiler.ILAst @@ -27,7 +27,8 @@ namespace ICSharpCode.Decompiler.ILAst
PeepholeTransform[] blockTransforms = {
ArrayInitializers.Transform(method),
transforms.CachedDelegateInitialization
transforms.CachedDelegateInitialization,
transforms.MakeAssignmentExpression
};
Func<ILExpression, ILExpression>[] exprTransforms = {
HandleDecimalConstants,
@ -49,8 +50,8 @@ namespace ICSharpCode.Decompiler.ILAst @@ -49,8 +50,8 @@ namespace ICSharpCode.Decompiler.ILAst
block.Body[i] = expr;
if (modified) {
ILInlining inlining = new ILInlining(method);
if (inlining.InlineIfPossible(block, i, aggressive: true)) {
i -= inlining.InlineInto(block, i, aggressive: false) - 1;
if (inlining.InlineIfPossible(block, ref i)) {
i++; // retry all transforms on the new combined instruction
continue;
}
}
@ -234,5 +235,54 @@ namespace ICSharpCode.Decompiler.ILAst @@ -234,5 +235,54 @@ namespace ICSharpCode.Decompiler.ILAst
}
}
#endregion
#region MakeAssignmentExpression
void MakeAssignmentExpression(ILBlock block, ref int i)
{
// expr_44 = ...
// stloc(v, expr_44)
// ->
// expr_44 = stloc(v, ...))
ILVariable exprVar;
ILExpression initializer;
if (!(block.Body[i].Match(ILCode.Stloc, out exprVar, out initializer) && exprVar.IsGenerated))
return;
ILExpression stloc1 = block.Body.ElementAtOrDefault(i + 1) as ILExpression;
if (!(stloc1 != null && stloc1.Code == ILCode.Stloc && stloc1.Arguments[0].Code == ILCode.Ldloc && stloc1.Arguments[0].Operand == exprVar))
return;
ILInlining inlining;
ILExpression stloc2 = block.Body.ElementAtOrDefault(i + 2) as ILExpression;
if (stloc2 != null && stloc2.Code == ILCode.Stloc && stloc2.Arguments[0].Code == ILCode.Ldloc && stloc2.Arguments[0].Operand == exprVar) {
// expr_44 = ...
// stloc(v1, expr_44)
// stloc(v2, expr_44)
// ->
// stloc(v1, stloc(v2, ...))
inlining = new ILInlining(method);
if (inlining.numLdloc.GetOrDefault(exprVar) == 2 && inlining.numStloc.GetOrDefault(exprVar) == 1) {
block.Body.RemoveAt(i + 2); // remove stloc2
block.Body.RemoveAt(i); // remove expr = ...
stloc1.Arguments[0] = stloc2;
stloc2.Arguments[0] = initializer;
if (inlining.InlineIfPossible(block, ref i)) {
i++; // retry transformations on the new combined instruction
}
return;
}
}
block.Body.RemoveAt(i + 1); // remove stloc
stloc1.Arguments[0] = initializer;
((ILExpression)block.Body[i]).Arguments[0] = stloc1;
inlining = new ILInlining(method);
if (inlining.InlineIfPossible(block, ref i)) {
i++; // retry transformations on the new combined instruction
}
}
#endregion
}
}

6
ICSharpCode.Decompiler/ILAst/YieldReturnDecompiler.cs

@ -60,6 +60,12 @@ namespace ICSharpCode.Decompiler.ILAst @@ -60,6 +60,12 @@ namespace ICSharpCode.Decompiler.ILAst
method.Body.Clear();
method.EntryGoto = null;
method.Body.AddRange(yrd.newBody);
// Repeat the inlining/copy propagation optimization because the conversion of field access
// to local variables can open up additional inlining possibilities.
ILInlining inlining = new ILInlining(method);
inlining.InlineAllVariables();
inlining.CopyPropagation();
}
void Run()

15
ICSharpCode.Decompiler/Tests/YieldReturn.cs

@ -31,6 +31,21 @@ public static class YieldReturn @@ -31,6 +31,21 @@ public static class YieldReturn
yield return 2;
}
public static IEnumerable<int> YieldReturnInLock1(object o)
{
lock (o) {
yield return 1;
}
}
public static IEnumerable<int> YieldReturnInLock2(object o)
{
lock (o) {
yield return 1;
o = null;
yield return 2;
}
}
public static IEnumerable<string> YieldReturnWithNestedTryFinally(bool breakInMiddle)
{

Loading…
Cancel
Save