Browse Source

Completely rewritten ByteCodeExpressions.

It is now new representation of the code - that is, an extra stage in the decompilation process.
It is independent of the previous representation and thus it can be much more easily transformed.
pull/1/head^2
David Srbecký 18 years ago
parent
commit
4beea5c6a1
  1. 35
      bin/Debug/output.cs
  2. 50
      src/AstMetodBodyBuilder.cs
  3. 8
      src/ByteCode.cs
  4. 148
      src/ByteCodeExpression.cs
  5. 78
      src/ByteCodeExpressionCollection.cs
  6. 2
      src/ControlFlow/Nodes.cs

35
bin/Debug/output.cs

@ -162,48 +162,27 @@ namespace Reversi
} }
} }
if (squares[i, j] == Black) { if (squares[i, j] == Black) {
object expr122 = this; expr122.blackCount = blackCount + 1;
int expr123 = expr122.blackCount;
int expr129 = expr123 + 1;
expr122.blackCount = expr129;
if (flag2) { if (flag2) {
object expr134 = this; expr134.blackFrontierCount = blackFrontierCount + 1;
int expr135 = expr134.blackFrontierCount;
int expr13B = expr135 + 1;
expr134.blackFrontierCount = expr13B;
} }
if (safeDiscs[i, j]) { if (safeDiscs[i, j]) {
object expr151 = this; expr151.blackSafeCount = blackSafeCount + 1;
int expr152 = expr151.blackSafeCount;
int expr158 = expr152 + 1;
expr151.blackSafeCount = expr158;
} }
} else { } else {
if (squares[i, j] == White) { if (squares[i, j] == White) {
object expr175 = this; expr175.whiteCount = whiteCount + 1;
int expr176 = expr175.whiteCount;
int expr17C = expr176 + 1;
expr175.whiteCount = expr17C;
if (flag2) { if (flag2) {
object expr187 = this; expr187.whiteFrontierCount = whiteFrontierCount + 1;
int expr188 = expr187.whiteFrontierCount;
int expr18E = expr188 + 1;
expr187.whiteFrontierCount = expr18E;
} }
if (safeDiscs[i, j]) { if (safeDiscs[i, j]) {
object expr1A4 = this; expr1A4.whiteSafeCount = whiteSafeCount + 1;
int expr1A5 = expr1A4.whiteSafeCount;
int expr1AB = expr1A5 + 1;
expr1A4.whiteSafeCount = expr1AB;
goto BasicBlock_327; goto BasicBlock_327;
} else { } else {
goto BasicBlock_327; goto BasicBlock_327;
} }
} }
object expr1B4 = this; expr1B4.emptyCount = emptyCount + 1;
int expr1B5 = expr1B4.emptyCount;
int expr1BB = expr1B5 + 1;
expr1B4.emptyCount = expr1BB;
} }
BasicBlock_327: BasicBlock_327:
} }

50
src/AstMetodBodyBuilder.cs

@ -180,13 +180,9 @@ namespace Decompiler
List<Ast.Expression> TransformExpressionArguments(ByteCodeExpression expr) List<Ast.Expression> TransformExpressionArguments(ByteCodeExpression expr)
{ {
List<Ast.Expression> args = new List<Ast.Expression>(); List<Ast.Expression> args = new List<Ast.Expression>();
foreach(CilStackSlot stackSlot in expr.StackBefore.PeekCount(expr.PopCount)) {
string name = string.Format("expr{0:X2}", stackSlot.AllocadedBy.Offset);
args.Add(new Ast.IdentifierExpression(name));
}
// Args generated by nested expressions (which must be closed) // Args generated by nested expressions (which must be closed)
foreach(ByteCodeExpression nestedExpr in expr.LastArguments) { foreach(ByteCodeExpression arg in expr.Arguments) {
args.Add((Ast.Expression)TransformExpression(nestedExpr)); args.Add((Ast.Expression)TransformExpression(arg));
} }
return args; return args;
} }
@ -194,22 +190,14 @@ namespace Decompiler
object TransformExpression(ByteCodeExpression expr) object TransformExpression(ByteCodeExpression expr)
{ {
List<Ast.Expression> args = TransformExpressionArguments(expr); List<Ast.Expression> args = TransformExpressionArguments(expr);
return TransformByteCode(methodDef, expr.LastByteCode, args); return TransformByteCode(methodDef, expr, args);
} }
Ast.Statement TransformExpressionToStatement(ByteCodeExpression expr) Ast.Statement TransformExpressionToStatement(ByteCodeExpression expr)
{ {
object codeExpr = TransformExpression(expr); object codeExpr = TransformExpression(expr);
if (codeExpr is Ast.Expression) { if (codeExpr is Ast.Expression) {
if (expr.PushCount >= 1) { return new Ast.ExpressionStatement((Ast.Expression)codeExpr);
string type = expr.LastByteCode.Type.FullName;
string name = string.Format("expr{0:X2}", expr.LastByteCode.Offset);
Ast.LocalVariableDeclaration astLocal = new Ast.LocalVariableDeclaration(new Ast.TypeReference(type.ToString()));
astLocal.Variables.Add(new Ast.VariableDeclaration(name, (Ast.Expression)codeExpr));
return astLocal;
} else {
return new Ast.ExpressionStatement((Ast.Expression)codeExpr);
}
} else if (codeExpr is Ast.Statement) { } else if (codeExpr is Ast.Statement) {
return (Ast.Statement)codeExpr; return (Ast.Statement)codeExpr;
} else { } else {
@ -234,7 +222,7 @@ namespace Decompiler
List<Ast.Expression> args = TransformExpressionArguments(((SimpleBranch)branch).BasicBlock.Body[0]); List<Ast.Expression> args = TransformExpressionArguments(((SimpleBranch)branch).BasicBlock.Body[0]);
Ast.Expression arg1 = args.Count >= 1 ? args[0] : null; Ast.Expression arg1 = args.Count >= 1 ? args[0] : null;
Ast.Expression arg2 = args.Count >= 2 ? args[1] : null; Ast.Expression arg2 = args.Count >= 2 ? args[1] : null;
switch(((SimpleBranch)branch).BasicBlock.Body[0].LastByteCode.OpCode.Code) { switch(((SimpleBranch)branch).BasicBlock.Body[0].OpCode.Code) {
case Code.Brfalse: return new Ast.UnaryOperatorExpression(arg1, UnaryOperatorType.Not); case Code.Brfalse: return new Ast.UnaryOperatorExpression(arg1, UnaryOperatorType.Not);
case Code.Brtrue: return arg1; case Code.Brtrue: return arg1;
case Code.Beq: return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.Equality, arg2); case Code.Beq: return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.Equality, arg2);
@ -284,7 +272,7 @@ namespace Decompiler
} }
} }
static object TransformByteCode(MethodDefinition methodDef, ByteCode byteCode, List<Ast.Expression> args) static object TransformByteCode(MethodDefinition methodDef, ByteCodeExpression byteCode, List<Ast.Expression> args)
{ {
try { try {
Ast.INode ret = TransformByteCode_Internal(methodDef, byteCode, args); Ast.INode ret = TransformByteCode_Internal(methodDef, byteCode, args);
@ -296,14 +284,16 @@ namespace Decompiler
} catch (NotImplementedException) { } catch (NotImplementedException) {
// Output the operand of the unknown IL code as well // Output the operand of the unknown IL code as well
if (byteCode.Operand != null) { if (byteCode.Operand != null) {
args.Insert(0, new IdentifierExpression(byteCode.FormatedOperand)); args.Insert(0, new IdentifierExpression(ByteCode.FormatByteCodeOperand(byteCode.Operand)));
} }
return new Ast.InvocationExpression(new IdentifierExpression("IL__" + byteCode.OpCode.Name), args); return new Ast.InvocationExpression(new IdentifierExpression("IL__" + byteCode.OpCode.Name), args);
} }
} }
static Ast.INode TransformByteCode_Internal(MethodDefinition methodDef, ByteCode byteCode, List<Ast.Expression> args) static Ast.INode TransformByteCode_Internal(MethodDefinition methodDef, ByteCodeExpression byteCode, List<Ast.Expression> args)
{ {
// throw new NotImplementedException();
OpCode opCode = byteCode.OpCode; OpCode opCode = byteCode.OpCode;
object operand = byteCode.Operand; object operand = byteCode.Operand;
Ast.TypeReference operandAsTypeRef = operand is Cecil.TypeReference ? new Ast.TypeReference(((Cecil.TypeReference)operand).FullName) : null; Ast.TypeReference operandAsTypeRef = operand is Cecil.TypeReference ? new Ast.TypeReference(((Cecil.TypeReference)operand).FullName) : null;
@ -313,8 +303,8 @@ namespace Decompiler
Ast.Expression arg3 = args.Count >= 3 ? args[2] : null; Ast.Expression arg3 = args.Count >= 3 ? args[2] : null;
Ast.Statement branchCommand = null; Ast.Statement branchCommand = null;
if (operand is ByteCode) { if (byteCode.BranchTarget != null) {
branchCommand = new Ast.GotoStatement(((ByteCode)operand).Expression.BasicBlock.Label); branchCommand = new Ast.GotoStatement(byteCode.BranchTarget.BasicBlock.Label);
} }
switch(opCode.Code) { switch(opCode.Code) {
@ -603,13 +593,17 @@ namespace Decompiler
VariableDefinition locVar = (VariableDefinition)operand; VariableDefinition locVar = (VariableDefinition)operand;
string name = locVar.Name; string name = locVar.Name;
arg1 = Convert(arg1, locVar.VariableType); arg1 = Convert(arg1, locVar.VariableType);
if (localVarDefined[name]) { if (localVarDefined.ContainsKey(name)) {
return new Ast.AssignmentExpression(new Ast.IdentifierExpression(name), AssignmentOperatorType.Assign, arg1); if (localVarDefined[name]) {
return new Ast.AssignmentExpression(new Ast.IdentifierExpression(name), AssignmentOperatorType.Assign, arg1);
} else {
Ast.LocalVariableDeclaration astLocalVar = new Ast.LocalVariableDeclaration(new Ast.VariableDeclaration(name, arg1));
astLocalVar.TypeReference = new Ast.TypeReference(localVarTypes[name].FullName);
localVarDefined[name] = true;
return astLocalVar;
}
} else { } else {
Ast.LocalVariableDeclaration astLocalVar = new Ast.LocalVariableDeclaration(new Ast.VariableDeclaration(name, arg1)); return new Ast.AssignmentExpression(new Ast.IdentifierExpression(name), AssignmentOperatorType.Assign, arg1);
astLocalVar.TypeReference = new Ast.TypeReference(localVarTypes[name].FullName);
localVarDefined[name] = true;
return astLocalVar;
} }
} }
case Code.Stobj: throw new NotImplementedException(); case Code.Stobj: throw new NotImplementedException();

8
src/ByteCode.cs

@ -9,7 +9,6 @@ namespace Decompiler
{ {
public partial class ByteCode public partial class ByteCode
{ {
ByteCodeExpression expression;
ByteCode previous; ByteCode previous;
ByteCode next; ByteCode next;
@ -18,11 +17,6 @@ namespace Decompiler
OpCode opCode; OpCode opCode;
object operand; object operand;
public ByteCodeExpression Expression {
get { return expression; }
set { expression = value; }
}
public ByteCode Previous { public ByteCode Previous {
get { return previous; } get { return previous; }
set { previous = value; } set { previous = value; }
@ -94,7 +88,7 @@ namespace Decompiler
} }
} }
static string FormatByteCodeOperand(object operand) public static string FormatByteCodeOperand(object operand)
{ {
if (operand == null) { if (operand == null) {
return string.Empty; return string.Empty;

148
src/ByteCodeExpression.cs

@ -10,137 +10,103 @@ namespace Decompiler
public class ByteCodeExpression public class ByteCodeExpression
{ {
ControlFlow.BasicBlock basicBlock; ControlFlow.BasicBlock basicBlock;
ByteCodeExpressionCollection owner;
ByteCode lastByteCode; OpCode opCode;
List<ByteCodeExpression> lastArguments = new List<ByteCodeExpression>(); object operand;
List<ByteCodeExpression> arguments = new List<ByteCodeExpression>();
bool returnsValue;
Cecil.TypeReference type;
ByteCodeExpression branchTarget;
List<ByteCodeExpression> branchesHere = new List<ByteCodeExpression>();
bool isSSASR = false;
public Decompiler.ControlFlow.BasicBlock BasicBlock { public Decompiler.ControlFlow.BasicBlock BasicBlock {
get { return basicBlock; } get { return basicBlock; }
set { set {
basicBlock = value; basicBlock = value;
foreach (ByteCodeExpression lastArgument in lastArguments) { foreach (ByteCodeExpression argument in arguments) {
lastArgument.BasicBlock = value; argument.BasicBlock = value;
} }
} }
} }
public ByteCodeExpressionCollection Owner { public OpCode OpCode {
get { return owner; } get { return opCode; }
set { opCode = value; }
} }
public ByteCode LastByteCode { public object Operand {
get { return lastByteCode; } get { return operand; }
set { operand = value; }
} }
// A list of closed expression for last arguments public List<ByteCodeExpression> Arguments {
public List<ByteCodeExpression> LastArguments { get { return arguments; }
get { return lastArguments; }
} }
public CilStack StackBefore { public bool ReturnsValue {
get { get { return returnsValue; }
return this.FirstByteCode.StackBefore; set { returnsValue = value; }
}
} }
public CilStack StackAfter { public TypeReference Type {
get { get { return type; }
return this.LastByteCode.StackAfter; set { type = value; }
}
} }
/// <summary> /// <summary> Single static assignment; single read </summary>
/// Expression is closed if it has no inputs and has exactly one output public bool IsSSASR {
/// </summary> get { return isSSASR; }
public bool IsClosed { set { isSSASR = value; }
get {
return this.PopCount == 0 &&
this.PushCount == 1;
}
}
public List<ByteCodeExpression> BranchesHere {
get {
List<ByteCodeExpression> branchesHere = new List<ByteCodeExpression>();
foreach(ByteCode byteCode in this.FirstByteCode.BranchesHere) {
branchesHere.Add(byteCode.Expression);
}
return branchesHere;
}
} }
public ByteCodeExpression BranchTarget { public ByteCodeExpression BranchTarget {
get { get { return branchTarget; }
if (this.lastByteCode.BranchTarget == null) { set { branchTarget = value; }
return null;
} else {
return this.lastByteCode.BranchTarget.Expression;
}
}
} }
public bool IsBranchTarget { public List<ByteCodeExpression> BranchesHere {
get { get { return branchesHere; }
return this.FirstByteCode.BranchesHere.Count > 0;
}
} }
public int PopCount { public bool IsBranchTarget {
get { get { return BranchesHere.Count > 0; }
int popCount;
int pushCount;
SimulateStackSize(out popCount, out pushCount);
return popCount;
}
} }
public int PushCount { public static ByteCodeExpression Ldloc(string name)
get { {
int popCount; return new ByteCodeExpression(OpCodes.Ldloc, new VariableDefinition(name, 0, null, null), true);
int pushCount;
SimulateStackSize(out popCount, out pushCount);
return pushCount;
}
} }
void SimulateStackSize(out int popCount, out int pushCount) public static ByteCodeExpression Stloc(string name)
{ {
int stackSize = 0; return new ByteCodeExpression(OpCodes.Stloc, new VariableDefinition(name, 0, null, null), false);
int minStackSize = 0;
foreach(ByteCodeExpression expr in lastArguments) {
stackSize -= expr.PopCount;
minStackSize = Math.Min(minStackSize, stackSize);
stackSize += expr.PushCount;
}
{
stackSize -= lastByteCode.PopCount;
minStackSize = Math.Min(minStackSize, stackSize);
stackSize += lastByteCode.PushCount;
}
popCount = -minStackSize;
pushCount = stackSize - minStackSize;
} }
public ByteCode FirstByteCode { public ByteCodeExpression(OpCode opCode, object operand, bool returnsValue)
get { {
if (lastArguments.Count > 0) { this.opCode = opCode;
return lastArguments[0].FirstByteCode; this.operand = operand;
} else { this.returnsValue = returnsValue;
return this.LastByteCode;
}
}
} }
public ByteCodeExpression(ByteCodeExpressionCollection owner, ByteCode lastByteCode) public ByteCodeExpression(ByteCode byteCode)
{ {
this.owner = owner; this.OpCode = byteCode.OpCode;
this.lastByteCode = lastByteCode; this.Operand = byteCode.Operand;
this.lastByteCode.Expression = this; foreach(CilStackSlot arg in byteCode.StackBefore.PeekCount(byteCode.PopCount)) {
string name = string.Format("expr{0:X2}", arg.AllocadedBy.Offset);
this.Arguments.Add(Ldloc(name));
}
this.ReturnsValue = byteCode.PushCount > 0;
this.Type = byteCode.Type;
} }
public override string ToString() public override string ToString()
{ {
return this.LastByteCode.ToString(); return string.Format("[ByteCodeExpression OpCode={0}]", this.opCode);
} }
} }
} }

78
src/ByteCodeExpressionCollection.cs

@ -11,26 +11,78 @@ namespace Decompiler
{ {
public ByteCodeExpressionCollection(ByteCodeCollection byteCodeCol) public ByteCodeExpressionCollection(ByteCodeCollection byteCodeCol)
{ {
foreach(ByteCode bc in byteCodeCol) { Dictionary<ByteCode, ByteCodeExpression> exprForByteCode = new Dictionary<ByteCode, ByteCodeExpression>();
this.Add(new ByteCodeExpression(this, bc));
foreach(ByteCode byteCode in byteCodeCol) {
ByteCodeExpression newExpr = new ByteCodeExpression(byteCode);
// If the bytecode pushes anything encapsulate it with stloc
if (byteCode.PushCount > 0) {
string name = string.Format("expr{0:X2}", byteCode.Offset);
ByteCodeExpression stExpr = ByteCodeExpression.Stloc(name);
stExpr.Arguments.Add(newExpr);
stExpr.IsSSASR = true;
newExpr = stExpr;
}
exprForByteCode[byteCode] = newExpr;
this.Add(newExpr);
}
// Branching links
foreach(ByteCodeExpression expr in this) {
if (expr.Operand is ByteCode) {
expr.BranchTarget = exprForByteCode[(ByteCode)expr.Operand];
expr.BranchTarget.BranchesHere.Add(expr);
}
} }
} }
public void Optimize() public void Optimize()
{ {
for(int i = 1; i < this.Count; i++) { // Try to in-line stloc into following expression
if (i == 0) continue;
ByteCodeExpression prevExpr = this[i - 1]; for(int i = 0; i < this.Count - 1; i++) {
if (i < 0) continue;
ByteCodeExpression expr = this[i]; ByteCodeExpression expr = this[i];
ByteCodeExpression nextExpr = this[i + 1];
if (expr.PopCount > 0 && // This expr needs some more arguments if (expr.OpCode.Code == Code.Stloc &&
!expr.IsBranchTarget && expr.IsSSASR &&
prevExpr.IsClosed) !nextExpr.IsBranchTarget) {
{
Options.NotifyCollapsingExpression(); // If the next expression is stloc, look inside
this.RemoveAt(i - 1); i--; if (nextExpr.OpCode.Code == Code.Stloc &&
expr.LastArguments.Insert(0, prevExpr); nextExpr.Arguments[0].OpCode.Code != Code.Ldloc) {
i--; nextExpr = nextExpr.Arguments[0];
}
// Find the use of the 'expr'
for(int j = 0; j < nextExpr.Arguments.Count; j++) {
ByteCodeExpression arg = nextExpr.Arguments[j];
if (arg.OpCode.Code == Code.Ldloc &&
((VariableDefinition)arg.Operand).Name == ((VariableDefinition)expr.Operand).Name) {
// Found
Options.NotifyCollapsingExpression();
this.RemoveAt(i); i--; // Remove the stloc
nextExpr.Arguments[j] = expr.Arguments[0]; // Inline the stloc body
// Move branch links
foreach(ByteCodeExpression predExpr in expr.BranchesHere) {
predExpr.BranchTarget = this[i + 1];
predExpr.BranchTarget.BranchesHere.Add(predExpr);
}
i--; // Try the same index again
break;
}
if (arg.OpCode.Code != Code.Ldloc) {
// This argument might have side effects so we can not
// move the 'expr' after it. Terminate
break;
}
}
} }
} }
} }

2
src/ControlFlow/Nodes.cs

@ -149,7 +149,7 @@ namespace Decompiler.ControlFlow
if (node == target) continue; if (node == target) continue;
// Non-conditional branch does not fall-through // Non-conditional branch does not fall-through
if (exprs[i].LastByteCode.OpCode.Code == Code.Br) continue; if (exprs[i].OpCode.Code == Code.Br) continue;
node.FallThroughBasicBlock = target; node.FallThroughBasicBlock = target;
target.BasicBlockPredecessors.Add(node); target.BasicBlockPredecessors.Add(node);

Loading…
Cancel
Save