Browse Source

Switched to "define-use" naming convention in ILAstBuilder. Refactored variable splitting.

pull/285/merge
David Srbecký 14 years ago
parent
commit
e0e53ae650
  1. 270
      ICSharpCode.Decompiler/ILAst/ILAstBuilder.cs

270
ICSharpCode.Decompiler/ILAst/ILAstBuilder.cs

@ -28,78 +28,60 @@ using Cecil = Mono.Cecil;
namespace ICSharpCode.Decompiler.ILAst namespace ICSharpCode.Decompiler.ILAst
{ {
/// <summary>
/// Converts stack-based bytecode to variable-based bytecode by calculating use-define chains
/// </summary>
public class ILAstBuilder public class ILAstBuilder
{ {
/// <summary> Immutable </summary> /// <summary> Immutable </summary>
sealed class StackSlot struct StackSlot
{ {
public readonly ByteCode[] PushedBy; // One of those public readonly ByteCode[] Definitions; // Reaching definitions of this stack slot
public readonly ILVariable LoadFrom; // Where can we get the value from in AST public readonly ILVariable LoadFrom; // Variable used for storage of the value
public StackSlot(ByteCode[] pushedBy, ILVariable loadFrom) public StackSlot(ByteCode[] definitions, ILVariable loadFrom)
{ {
this.PushedBy = pushedBy; this.Definitions = definitions;
this.LoadFrom = loadFrom; this.LoadFrom = loadFrom;
} }
public StackSlot(ByteCode pushedBy) public static StackSlot[] ModifyStack(StackSlot[] stack, int popCount, int pushCount, ByteCode pushDefinition)
{ {
this.PushedBy = new[] { pushedBy }; StackSlot[] newStack = new StackSlot[stack.Length - popCount + pushCount];
this.LoadFrom = null; Array.Copy(stack, newStack, stack.Length - popCount);
} for (int i = stack.Length - popCount; i < newStack.Length; i++) {
newStack[i] = new StackSlot(new [] { pushDefinition }, null);
public static List<StackSlot> CloneStack(List<StackSlot> stack, int? popCount)
{
if (popCount.HasValue) {
return stack.GetRange(0, stack.Count - popCount.Value);
} else {
return new List<StackSlot>(0);
} }
return newStack;
} }
} }
/// <summary> Immutable </summary> /// <summary> Immutable </summary>
sealed class VariableSlot struct VariableSlot
{ {
public readonly ByteCode[] StoredBy; // One of those public readonly ByteCode[] Definitions; // Reaching deinitions of this variable
public readonly bool StoredByAll; // Overestimate which is useful for exceptional control flow. public readonly bool UnknownDefinition; // Used for initial state and exceptional control flow
static readonly VariableSlot EmptyInstance = new VariableSlot(new ByteCode[0], false);
public static readonly VariableSlot StoredByAllInstance = new VariableSlot(EmptyInstance.StoredBy, true);
public VariableSlot(ByteCode[] storedBy, bool storedByAll) static readonly VariableSlot UnknownInstance = new VariableSlot(new ByteCode[0], true);
{
this.StoredBy = storedBy;
this.StoredByAll = storedByAll;
}
public VariableSlot(ByteCode storedBy) public VariableSlot(ByteCode[] definitions, bool unknownDefinition)
{ {
this.StoredBy = new[] { storedBy }; this.Definitions = definitions;
this.StoredByAll = false; this.UnknownDefinition = unknownDefinition;
} }
public static VariableSlot[] CloneVariableState(VariableSlot[] state) public static VariableSlot[] CloneVariableState(VariableSlot[] state)
{ {
VariableSlot[] clone = new VariableSlot[state.Length]; VariableSlot[] clone = new VariableSlot[state.Length];
Array.Copy(state, 0, clone, 0, state.Length); Array.Copy(state, clone, state.Length);
return clone; return clone;
} }
public static VariableSlot[] MakeEmptyState(int varCount) public static VariableSlot[] MakeUknownState(int varCount)
{
VariableSlot[] emptyVariableState = new VariableSlot[varCount];
for (int i = 0; i < emptyVariableState.Length; i++) {
emptyVariableState[i] = EmptyInstance;
}
return emptyVariableState;
}
public static VariableSlot[] MakeFullState(int varCount)
{ {
VariableSlot[] unknownVariableState = new VariableSlot[varCount]; VariableSlot[] unknownVariableState = new VariableSlot[varCount];
for (int i = 0; i < unknownVariableState.Length; i++) { for (int i = 0; i < unknownVariableState.Length; i++) {
unknownVariableState[i] = StoredByAllInstance; unknownVariableState[i] = UnknownInstance;
} }
return unknownVariableState; return unknownVariableState;
} }
@ -117,11 +99,15 @@ namespace ICSharpCode.Decompiler.ILAst
public string Name { get { return "IL_" + this.Offset.ToString("X2"); } } public string Name { get { return "IL_" + this.Offset.ToString("X2"); } }
public ByteCode Next; public ByteCode Next;
public Instruction[] Prefixes; // Non-null only if needed public Instruction[] Prefixes; // Non-null only if needed
public List<StackSlot> StackBefore; // Unique per bytecode; not shared public StackSlot[] StackBefore; // Unique per bytecode; not shared
public List<ILVariable> StoreTo; // Store result of instruction to those AST variables
public VariableSlot[] VariablesBefore; // Unique per bytecode; not shared public VariableSlot[] VariablesBefore; // Unique per bytecode; not shared
public List<ILVariable> StoreTo; // Store result of instruction to those AST variables
public VariableDefinition OperandAsVariable { get { return (VariableDefinition)this.Operand; } } public bool IsVariableDefinition {
get {
return (this.Code == ILCode.Stloc) || (this.Code == ILCode.Ldloca && this.Next != null && this.Next.Code == ILCode.Initobj);
}
}
public override string ToString() public override string ToString()
{ {
@ -170,9 +156,9 @@ namespace ICSharpCode.Decompiler.ILAst
foreach (StackSlot slot in this.StackBefore) { foreach (StackSlot slot in this.StackBefore) {
if (!first) sb.Append(","); if (!first) sb.Append(",");
bool first2 = true; bool first2 = true;
foreach(ByteCode pushedBy in slot.PushedBy) { foreach(ByteCode defs in slot.Definitions) {
if (!first2) sb.Append("|"); if (!first2) sb.Append("|");
sb.AppendFormat("IL_{0:X2}", pushedBy.Offset); sb.AppendFormat("IL_{0:X2}", defs.Offset);
first2 = false; first2 = false;
} }
first = false; first = false;
@ -196,13 +182,11 @@ namespace ICSharpCode.Decompiler.ILAst
bool first = true; bool first = true;
foreach (VariableSlot varSlot in this.VariablesBefore) { foreach (VariableSlot varSlot in this.VariablesBefore) {
if (!first) sb.Append(","); if (!first) sb.Append(",");
if (varSlot.StoredByAll) { if (varSlot.UnknownDefinition) {
sb.Append("*"); sb.Append("?");
} else if (varSlot.StoredBy.Length == 0) {
sb.Append("_");
} else { } else {
bool first2 = true; bool first2 = true;
foreach (ByteCode storedBy in varSlot.StoredBy) { foreach (ByteCode storedBy in varSlot.Definitions) {
if (!first2) sb.Append("|"); if (!first2) sb.Append("|");
sb.AppendFormat("IL_{0:X2}", storedBy.Offset); sb.AppendFormat("IL_{0:X2}", storedBy.Offset);
first2 = false; first2 = false;
@ -289,8 +273,8 @@ namespace ICSharpCode.Decompiler.ILAst
if(methodDef.Body.HasExceptionHandlers) { if(methodDef.Body.HasExceptionHandlers) {
foreach(ExceptionHandler ex in methodDef.Body.ExceptionHandlers) { foreach(ExceptionHandler ex in methodDef.Body.ExceptionHandlers) {
ByteCode handlerStart = instrToByteCode[ex.HandlerStart]; ByteCode handlerStart = instrToByteCode[ex.HandlerStart];
handlerStart.StackBefore = new List<StackSlot>(); handlerStart.StackBefore = new StackSlot[0];
handlerStart.VariablesBefore = VariableSlot.MakeFullState(varCount); handlerStart.VariablesBefore = VariableSlot.MakeUknownState(varCount);
if (ex.HandlerType == ExceptionHandlerType.Catch || ex.HandlerType == ExceptionHandlerType.Filter) { if (ex.HandlerType == ExceptionHandlerType.Catch || ex.HandlerType == ExceptionHandlerType.Filter) {
// Catch and Filter handlers start with the exeption on the stack // Catch and Filter handlers start with the exeption on the stack
ByteCode ldexception = new ByteCode() { ByteCode ldexception = new ByteCode() {
@ -300,15 +284,13 @@ namespace ICSharpCode.Decompiler.ILAst
PushCount = 1 PushCount = 1
}; };
ldexceptions[ex] = ldexception; ldexceptions[ex] = ldexception;
handlerStart.StackBefore.Add(new StackSlot(ldexception)); handlerStart.StackBefore = new StackSlot[] { new StackSlot(new [] { ldexception }, null) };
} }
agenda.Push(handlerStart); agenda.Push(handlerStart);
if (ex.HandlerType == ExceptionHandlerType.Filter) if (ex.HandlerType == ExceptionHandlerType.Filter)
{ {
ByteCode filterStart = instrToByteCode[ex.FilterStart]; ByteCode filterStart = instrToByteCode[ex.FilterStart];
filterStart.StackBefore = new List<StackSlot>();
filterStart.VariablesBefore = VariableSlot.MakeFullState(varCount);
ByteCode ldexception = new ByteCode() { ByteCode ldexception = new ByteCode() {
Code = ILCode.Ldexception, Code = ILCode.Ldexception,
Operand = ex.CatchType, Operand = ex.CatchType,
@ -316,14 +298,15 @@ namespace ICSharpCode.Decompiler.ILAst
PushCount = 1 PushCount = 1
}; };
// TODO: ldexceptions[ex] = ldexception; // TODO: ldexceptions[ex] = ldexception;
filterStart.StackBefore.Add(new StackSlot(ldexception)); filterStart.StackBefore = new StackSlot[] { new StackSlot(new [] { ldexception }, null) };
filterStart.VariablesBefore = VariableSlot.MakeUknownState(varCount);
agenda.Push(filterStart); agenda.Push(filterStart);
} }
} }
} }
body[0].StackBefore = new List<StackSlot>(); body[0].StackBefore = new StackSlot[0];
body[0].VariablesBefore = VariableSlot.MakeEmptyState(varCount); body[0].VariablesBefore = VariableSlot.MakeUknownState(varCount);
agenda.Push(body[0]); agenda.Push(body[0]);
// Process agenda // Process agenda
@ -331,23 +314,17 @@ namespace ICSharpCode.Decompiler.ILAst
ByteCode byteCode = agenda.Pop(); ByteCode byteCode = agenda.Pop();
// Calculate new stack // Calculate new stack
List<StackSlot> newStack = StackSlot.CloneStack(byteCode.StackBefore, byteCode.PopCount); StackSlot[] newStack = StackSlot.ModifyStack(byteCode.StackBefore, byteCode.PopCount ?? byteCode.StackBefore.Length, byteCode.PushCount, byteCode);
for (int i = 0; i < byteCode.PushCount; i++) {
newStack.Add(new StackSlot(byteCode));
}
// Calculate new variable state // Calculate new variable state
VariableSlot[] newVariableState = VariableSlot.CloneVariableState(byteCode.VariablesBefore); VariableSlot[] newVariableState = VariableSlot.CloneVariableState(byteCode.VariablesBefore);
if (byteCode.Code == ILCode.Stloc || byteCode.Code == ILCode.Ldloca) { if (byteCode.IsVariableDefinition) {
int varIndex = ((VariableReference)byteCode.Operand).Index; newVariableState[((VariableReference)byteCode.Operand).Index] = new VariableSlot(new [] { byteCode }, false);
newVariableState[varIndex] = byteCode.Code == ILCode.Stloc || byteCode.Next.Code == ILCode.Initobj ?
new VariableSlot(byteCode) : !newVariableState[varIndex].StoredByAll && IsDeterministicLdloca(byteCode) ?
new VariableSlot(newVariableState[varIndex].StoredBy.Union(byteCode), false) : VariableSlot.StoredByAllInstance;
} }
// After the leave, finally block might have touched the variables // After the leave, finally block might have touched the variables
if (byteCode.Code == ILCode.Leave) { if (byteCode.Code == ILCode.Leave) {
newVariableState = VariableSlot.MakeFullState(varCount); newVariableState = VariableSlot.MakeUknownState(varCount);
} }
// Find all successors // Find all successors
@ -386,12 +363,12 @@ namespace ICSharpCode.Decompiler.ILAst
branchTarget.VariablesBefore = newVariableState; branchTarget.VariablesBefore = newVariableState;
} else { } else {
// Do not share data for several bytecodes // Do not share data for several bytecodes
branchTarget.StackBefore = StackSlot.CloneStack(newStack, 0); branchTarget.StackBefore = StackSlot.ModifyStack(newStack, 0, 0, null);
branchTarget.VariablesBefore = VariableSlot.CloneVariableState(newVariableState); branchTarget.VariablesBefore = VariableSlot.CloneVariableState(newVariableState);
} }
agenda.Push(branchTarget); agenda.Push(branchTarget);
} else { } else {
if (branchTarget.StackBefore.Count != newStack.Count) { if (branchTarget.StackBefore.Length != newStack.Length) {
throw new Exception("Inconsistent stack size at " + byteCode.Name); throw new Exception("Inconsistent stack size at " + byteCode.Name);
} }
@ -401,11 +378,11 @@ namespace ICSharpCode.Decompiler.ILAst
bool modified = false; bool modified = false;
// Merge stacks - modify the target // Merge stacks - modify the target
for (int i = 0; i < newStack.Count; i++) { for (int i = 0; i < newStack.Length; i++) {
ByteCode[] oldPushedBy = branchTarget.StackBefore[i].PushedBy; ByteCode[] oldDefs = branchTarget.StackBefore[i].Definitions;
ByteCode[] newPushedBy = oldPushedBy.Union(newStack[i].PushedBy); ByteCode[] newDefs = oldDefs.Union(newStack[i].Definitions);
if (newPushedBy.Length > oldPushedBy.Length) { if (newDefs.Length > oldDefs.Length) {
branchTarget.StackBefore[i] = new StackSlot(newPushedBy, null); branchTarget.StackBefore[i] = new StackSlot(newDefs, null);
modified = true; modified = true;
} }
} }
@ -414,16 +391,15 @@ namespace ICSharpCode.Decompiler.ILAst
for (int i = 0; i < newVariableState.Length; i++) { for (int i = 0; i < newVariableState.Length; i++) {
VariableSlot oldSlot = branchTarget.VariablesBefore[i]; VariableSlot oldSlot = branchTarget.VariablesBefore[i];
VariableSlot newSlot = newVariableState[i]; VariableSlot newSlot = newVariableState[i];
// All can not be unioned further if (!oldSlot.UnknownDefinition) {
if (!oldSlot.StoredByAll) { if (newSlot.UnknownDefinition) {
if (newSlot.StoredByAll) {
branchTarget.VariablesBefore[i] = newSlot; branchTarget.VariablesBefore[i] = newSlot;
modified = true; modified = true;
} else { } else {
ByteCode[] oldStoredBy = oldSlot.StoredBy; ByteCode[] oldDefs = oldSlot.Definitions;
ByteCode[] newStoredBy = oldStoredBy.Union(newSlot.StoredBy); ByteCode[] newDefs = oldDefs.Union(newSlot.Definitions);
if (newStoredBy.Length > oldStoredBy.Length) { if (newDefs.Length > oldDefs.Length) {
branchTarget.VariablesBefore[i] = new VariableSlot(newStoredBy, false); branchTarget.VariablesBefore[i] = new VariableSlot(newDefs, false);
modified = true; modified = true;
} }
} }
@ -437,18 +413,18 @@ namespace ICSharpCode.Decompiler.ILAst
} }
} }
// Occasionally the compilers or obfuscators generate unreachable code (which migt be intentonally invalid) // Occasionally the compilers or obfuscators generate unreachable code (which might be intentonally invalid)
// I belive it is safe to just remove it // I belive it is safe to just remove it
body.RemoveAll(b => b.StackBefore == null); body.RemoveAll(b => b.StackBefore == null);
// Genertate temporary variables to replace stack // Genertate temporary variables to replace stack
foreach(ByteCode byteCode in body) { foreach(ByteCode byteCode in body) {
int argIdx = 0; int argIdx = 0;
int popCount = byteCode.PopCount ?? byteCode.StackBefore.Count; int popCount = byteCode.PopCount ?? byteCode.StackBefore.Length;
for (int i = byteCode.StackBefore.Count - popCount; i < byteCode.StackBefore.Count; i++) { for (int i = byteCode.StackBefore.Length - popCount; i < byteCode.StackBefore.Length; i++) {
ILVariable tmpVar = new ILVariable() { Name = string.Format("arg_{0:X2}_{1}", byteCode.Offset, argIdx), IsGenerated = true }; ILVariable tmpVar = new ILVariable() { Name = string.Format("arg_{0:X2}_{1}", byteCode.Offset, argIdx), IsGenerated = true };
byteCode.StackBefore[i] = new StackSlot(byteCode.StackBefore[i].PushedBy, tmpVar); byteCode.StackBefore[i] = new StackSlot(byteCode.StackBefore[i].Definitions, tmpVar);
foreach(ByteCode pushedBy in byteCode.StackBefore[i].PushedBy) { foreach(ByteCode pushedBy in byteCode.StackBefore[i].Definitions) {
if (pushedBy.StoreTo == null) { if (pushedBy.StoreTo == null) {
pushedBy.StoreTo = new List<ILVariable>(1); pushedBy.StoreTo = new List<ILVariable>(1);
} }
@ -467,16 +443,16 @@ namespace ICSharpCode.Decompiler.ILAst
var loadedBy = locVars.Select(locVar => body.SelectMany(bc => bc.StackBefore).Single(s => s.LoadFrom == locVar)).ToList(); var loadedBy = locVars.Select(locVar => body.SelectMany(bc => bc.StackBefore).Single(s => s.LoadFrom == locVar)).ToList();
// We now know that all the variables have a single load, // We now know that all the variables have a single load,
// Let's make sure that they have also a single store - us // Let's make sure that they have also a single store - us
if (loadedBy.All(slot => slot.PushedBy.Length == 1 && slot.PushedBy[0] == byteCode)) { if (loadedBy.All(slot => slot.Definitions.Length == 1 && slot.Definitions[0] == byteCode)) {
// Great - we can reduce everything into single variable // Great - we can reduce everything into single variable
ILVariable tmpVar = new ILVariable() { Name = string.Format("expr_{0:X2}", byteCode.Offset), IsGenerated = true }; ILVariable tmpVar = new ILVariable() { Name = string.Format("expr_{0:X2}", byteCode.Offset), IsGenerated = true };
byteCode.StoreTo = new List<ILVariable>() { tmpVar }; byteCode.StoreTo = new List<ILVariable>() { tmpVar };
foreach(ByteCode bc in body) { foreach(ByteCode bc in body) {
for (int i = 0; i < bc.StackBefore.Count; i++) { for (int i = 0; i < bc.StackBefore.Length; i++) {
// Is it one of the variable to be merged? // Is it one of the variable to be merged?
if (locVars.Contains(bc.StackBefore[i].LoadFrom)) { if (locVars.Contains(bc.StackBefore[i].LoadFrom)) {
// Replace with the new temp variable // Replace with the new temp variable
bc.StackBefore[i] = new StackSlot(bc.StackBefore[i].PushedBy, tmpVar); bc.StackBefore[i] = new StackSlot(bc.StackBefore[i].Definitions, tmpVar);
} }
} }
} }
@ -531,14 +507,16 @@ namespace ICSharpCode.Decompiler.ILAst
b = b.Next; b = b.Next;
if (b == null) return false; if (b == null) return false;
} }
if (b.Code == ILCode.Ldfld || b.Code == ILCode.Stfld)
return true;
return (b.Code == ILCode.Call || b.Code == ILCode.Callvirt) && ((MethodReference)b.Operand).HasThis; return (b.Code == ILCode.Call || b.Code == ILCode.Callvirt) && ((MethodReference)b.Operand).HasThis;
} }
sealed class VariableInfo sealed class VariableInfo
{ {
public ILVariable Variable; public ILVariable Variable;
public List<ByteCode> Stores; public List<ByteCode> Defs;
public List<ByteCode> Loads; public List<ByteCode> Uses;
} }
/// <summary> /// <summary>
@ -547,50 +525,37 @@ namespace ICSharpCode.Decompiler.ILAst
/// </summary> /// </summary>
void ConvertLocalVariables(List<ByteCode> body) void ConvertLocalVariables(List<ByteCode> body)
{ {
if (optimize) { foreach(VariableDefinition varDef in methodDef.Body.Variables) {
int varCount = methodDef.Body.Variables.Count;
for(int variableIndex = 0; variableIndex < varCount; variableIndex++) { // Find all definitions and uses of this variable
// Find all stores and loads for this variable var defs = body.Where(b => b.Operand == varDef && b.IsVariableDefinition).ToList();
var stores = body.Where(b => (b.Code == ILCode.Stloc || b.Code == ILCode.Ldloca) && b.Operand is VariableDefinition && b.OperandAsVariable.Index == variableIndex).ToList(); var uses = body.Where(b => b.Operand == varDef && !b.IsVariableDefinition).ToList();
// ldloca on an uninitialized variable or followed by initobj isn't considered a load
var loads = body.Where(b =>
(b.Code == ILCode.Ldloc || (b.Code == ILCode.Ldloca && b.Next.Code != ILCode.Initobj &&
(b.VariablesBefore[variableIndex].StoredBy.Length != 0 || b.VariablesBefore[variableIndex].StoredByAll || !IsDeterministicLdloca(b))))
&& b.Operand is VariableDefinition && b.OperandAsVariable.Index == variableIndex).ToList();
TypeReference varType = methodDef.Body.Variables[variableIndex].VariableType;
List<VariableInfo> newVars; List<VariableInfo> newVars;
bool isPinned = methodDef.Body.Variables[variableIndex].IsPinned;
// If the variable is pinned, use single variable. // If the variable is pinned, use single variable.
// If any of the loads is from "all", use single variable // If any of the uses is from unknown definition, use single variable
// If any of the loads is ldloca with a nondeterministic usage pattern, use single variable // If any of the uses is ldloca with a nondeterministic usage pattern, use single variable
if (isPinned || loads.Any(b => b.VariablesBefore[variableIndex].StoredByAll || (b.Code == ILCode.Ldloca && !IsDeterministicLdloca(b)))) { if (!optimize || varDef.IsPinned || uses.Any(b => b.VariablesBefore[varDef.Index].UnknownDefinition || (b.Code == ILCode.Ldloca && !IsDeterministicLdloca(b)))) {
newVars = new List<VariableInfo>(1) { new VariableInfo() { newVars = new List<VariableInfo>(1) { new VariableInfo() {
Variable = new ILVariable() { Variable = new ILVariable() {
Name = "var_" + variableIndex, Name = string.IsNullOrEmpty(varDef.Name) ? "var_" + varDef.Index : varDef.Name,
Type = isPinned ? ((PinnedType)varType).ElementType : varType, Type = varDef.IsPinned ? ((PinnedType)varDef.VariableType).ElementType : varDef.VariableType,
OriginalVariable = methodDef.Body.Variables[variableIndex] OriginalVariable = varDef
}, },
Stores = stores, Defs = defs,
Loads = loads Uses = uses
}}; }};
} else { } else {
// Create a new variable for each store // Create a new variable for each definition
newVars = stores.Where(st => newVars = defs.Select(def => new VariableInfo() {
{
if (st.Code == ILCode.Stloc || st.Next.Code == ILCode.Initobj) return true;
var storedBy = st.VariablesBefore[variableIndex].StoredBy;
return storedBy.Length == 0 || storedBy[0] == st;
}).Select(st => new VariableInfo() {
Variable = new ILVariable() { Variable = new ILVariable() {
Name = "var_" + variableIndex + "_" + st.Offset.ToString("X2"), Name = (string.IsNullOrEmpty(varDef.Name) ? "var_" + varDef.Index : varDef.Name) + "_" + def.Offset.ToString("X2"),
Type = varType, Type = varDef.VariableType,
OriginalVariable = methodDef.Body.Variables[variableIndex] OriginalVariable = varDef
}, },
Stores = new List<ByteCode>() {st}, Defs = new List<ByteCode>() { def },
Loads = new List<ByteCode>() Uses = new List<ByteCode>()
}).ToList(); }).ToList();
// VB.NET uses the 'init' to allow use of uninitialized variables. // VB.NET uses the 'init' to allow use of uninitialized variables.
@ -603,33 +568,19 @@ namespace ICSharpCode.Decompiler.ILAst
// Remember to handle cases where one path inits the variable, but other does not. // Remember to handle cases where one path inits the variable, but other does not.
// Add loads to the data structure; merge variables if necessary // Add loads to the data structure; merge variables if necessary
foreach(ByteCode load in loads) { foreach(ByteCode use in uses) {
ByteCode[] storedBy = load.VariablesBefore[variableIndex].StoredBy; ByteCode[] useDefs = use.VariablesBefore[varDef.Index].Definitions;
if (storedBy.Length == 0) { if (useDefs.Length == 1) {
// Load which always loads the default ('uninitialized') value VariableInfo newVar = newVars.Single(v => v.Defs.Contains(useDefs[0]));
// Create a dummy variable just for this load newVar.Uses.Add(use);
newVars.Add(new VariableInfo() {
Variable = new ILVariable() {
Name = "var_" + variableIndex + "_" + load.Offset.ToString("X2") + "_default",
Type = varType,
OriginalVariable = methodDef.Body.Variables[variableIndex]
},
Stores = new List<ByteCode>(),
Loads = new List<ByteCode>() { load }
});
} else if (storedBy.Length == 1) {
VariableInfo newVar = newVars.Single(v => v.Stores.Contains(storedBy[0]));
newVar.Loads.Add(load);
if (load.Code == ILCode.Ldloca) newVar.Stores.Add(load);
} else { } else {
List<VariableInfo> mergeVars = newVars.Where(v => v.Stores.Intersect(storedBy).Any()).ToList(); List<VariableInfo> mergeVars = newVars.Where(v => v.Defs.Intersect(useDefs).Any()).ToList();
VariableInfo mergedVar = new VariableInfo() { VariableInfo mergedVar = new VariableInfo() {
Variable = mergeVars[0].Variable, Variable = mergeVars[0].Variable,
Stores = mergeVars.SelectMany(v => v.Stores).ToList(), Defs = mergeVars.SelectMany(v => v.Defs).ToList(),
Loads = mergeVars.SelectMany(v => v.Loads).ToList() Uses = mergeVars.SelectMany(v => v.Uses).ToList()
}; };
mergedVar.Loads.Add(load); mergedVar.Uses.Add(use);
if (load.Code == ILCode.Ldloca) mergedVar.Stores.Add(load);
newVars = newVars.Except(mergeVars).ToList(); newVars = newVars.Except(mergeVars).ToList();
newVars.Add(mergedVar); newVars.Add(mergedVar);
} }
@ -638,20 +589,11 @@ namespace ICSharpCode.Decompiler.ILAst
// Set bytecode operands // Set bytecode operands
foreach(VariableInfo newVar in newVars) { foreach(VariableInfo newVar in newVars) {
foreach(ByteCode store in newVar.Stores) { foreach(ByteCode def in newVar.Defs) {
store.Operand = newVar.Variable; def.Operand = newVar.Variable;
}
foreach(ByteCode load in newVar.Loads) {
load.Operand = newVar.Variable;
}
} }
} foreach(ByteCode use in newVar.Uses) {
} else { use.Operand = newVar.Variable;
var variables = methodDef.Body.Variables.Select(v => new ILVariable() { Name = string.IsNullOrEmpty(v.Name) ? "var_" + v.Index : v.Name, Type = v.VariableType, OriginalVariable = v }).ToList();
foreach(ByteCode byteCode in body) {
if (byteCode.Code == ILCode.Ldloc || byteCode.Code == ILCode.Stloc || byteCode.Code == ILCode.Ldloca) {
int index = ((VariableDefinition)byteCode.Operand).Index;
byteCode.Operand = variables[index];
} }
} }
} }
@ -824,8 +766,8 @@ namespace ICSharpCode.Decompiler.ILAst
} }
// Reference arguments using temporary variables // Reference arguments using temporary variables
int popCount = byteCode.PopCount ?? byteCode.StackBefore.Count; int popCount = byteCode.PopCount ?? byteCode.StackBefore.Length;
for (int i = byteCode.StackBefore.Count - popCount; i < byteCode.StackBefore.Count; i++) { for (int i = byteCode.StackBefore.Length - popCount; i < byteCode.StackBefore.Length; i++) {
StackSlot slot = byteCode.StackBefore[i]; StackSlot slot = byteCode.StackBefore[i];
expr.Arguments.Add(new ILExpression(ILCode.Ldloc, slot.LoadFrom)); expr.Arguments.Add(new ILExpression(ILCode.Ldloc, slot.LoadFrom));
} }

Loading…
Cancel
Save