Browse Source

Restrict variable splitting in case of nondeterministic ldloca opcodes.

pull/286/merge
Pent Ploompuu 14 years ago committed by David Srbecký
parent
commit
8490b4cfd5
  1. 59
      ICSharpCode.Decompiler/ILAst/ILAstBuilder.cs
  2. 2
      ICSharpCode.Decompiler/ILAst/LiftedOperators.cs
  3. 2
      ICSharpCode.Decompiler/ILAst/TypeAnalysis.cs

59
ICSharpCode.Decompiler/ILAst/ILAstBuilder.cs

@ -30,10 +30,8 @@ namespace ICSharpCode.Decompiler.ILAst @@ -30,10 +30,8 @@ namespace ICSharpCode.Decompiler.ILAst
{
public class ILAstBuilder
{
static ByteCode[] EmptyByteCodeArray = new ByteCode[] {};
/// <summary> Immutable </summary>
class StackSlot
sealed class StackSlot
{
public readonly ByteCode[] PushedBy; // One of those
public readonly ILVariable LoadFrom; // Where can we get the value from in AST
@ -61,11 +59,14 @@ namespace ICSharpCode.Decompiler.ILAst @@ -61,11 +59,14 @@ namespace ICSharpCode.Decompiler.ILAst
}
/// <summary> Immutable </summary>
class VariableSlot
sealed class VariableSlot
{
public readonly ByteCode[] StoredBy; // One of those
public readonly bool StoredByAll; // Overestimate which is useful for 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)
{
this.StoredBy = storedBy;
@ -81,9 +82,7 @@ namespace ICSharpCode.Decompiler.ILAst @@ -81,9 +82,7 @@ namespace ICSharpCode.Decompiler.ILAst
public static VariableSlot[] CloneVariableState(VariableSlot[] state)
{
VariableSlot[] clone = new VariableSlot[state.Length];
for (int i = 0; i < clone.Length; i++) {
clone[i] = state[i];
}
Array.Copy(state, 0, clone, 0, state.Length);
return clone;
}
@ -91,7 +90,7 @@ namespace ICSharpCode.Decompiler.ILAst @@ -91,7 +90,7 @@ namespace ICSharpCode.Decompiler.ILAst
{
VariableSlot[] emptyVariableState = new VariableSlot[varCount];
for (int i = 0; i < emptyVariableState.Length; i++) {
emptyVariableState[i] = new VariableSlot(EmptyByteCodeArray, false);
emptyVariableState[i] = EmptyInstance;
}
return emptyVariableState;
}
@ -100,13 +99,13 @@ namespace ICSharpCode.Decompiler.ILAst @@ -100,13 +99,13 @@ namespace ICSharpCode.Decompiler.ILAst
{
VariableSlot[] unknownVariableState = new VariableSlot[varCount];
for (int i = 0; i < unknownVariableState.Length; i++) {
unknownVariableState[i] = new VariableSlot(EmptyByteCodeArray, true);
unknownVariableState[i] = StoredByAllInstance;
}
return unknownVariableState;
}
}
class ByteCode
sealed class ByteCode
{
public ILLabel Label; // Non-null only if needed
public int Offset;
@ -342,7 +341,8 @@ namespace ICSharpCode.Decompiler.ILAst @@ -342,7 +341,8 @@ namespace ICSharpCode.Decompiler.ILAst
if (byteCode.Code == ILCode.Stloc || byteCode.Code == ILCode.Ldloca) {
int varIndex = ((VariableReference)byteCode.Operand).Index;
newVariableState[varIndex] = byteCode.Code == ILCode.Stloc || byteCode.Next.Code == ILCode.Initobj ?
new VariableSlot(byteCode) : new VariableSlot(newVariableState[varIndex].StoredBy.Union(byteCode), false);
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
@ -505,8 +505,36 @@ namespace ICSharpCode.Decompiler.ILAst @@ -505,8 +505,36 @@ namespace ICSharpCode.Decompiler.ILAst
return body;
}
static bool IsDeterministicLdloca(ByteCode b)
{
var v = b.Operand;
b = b.Next;
if (b.Code == ILCode.Initobj) return true;
// instance method calls on value types use the variable ref deterministically
int stack = 1;
while (true) {
if (b.PopCount == null) return false;
stack -= b.PopCount.GetValueOrDefault();
if (stack == 0) break;
if (stack < 0) return false;
if (b.Code.IsConditionalControlFlow() || b.Code.IsUnconditionalControlFlow()) return false;
switch (b.Code) {
case ILCode.Ldloc:
case ILCode.Ldloca:
case ILCode.Stloc:
if (b.Operand == v) return false;
break;
}
stack += b.PushCount;
b = b.Next;
if (b == null) return false;
}
return (b.Code == ILCode.Call || b.Code == ILCode.Callvirt) && ((MethodReference)b.Operand).HasThis;
}
class VariableInfo
sealed class VariableInfo
{
public ILVariable Variable;
public List<ByteCode> Stores;
@ -528,7 +556,7 @@ namespace ICSharpCode.Decompiler.ILAst @@ -528,7 +556,7 @@ namespace ICSharpCode.Decompiler.ILAst
// 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)))
(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;
@ -537,7 +565,8 @@ namespace ICSharpCode.Decompiler.ILAst @@ -537,7 +565,8 @@ namespace ICSharpCode.Decompiler.ILAst
bool isPinned = methodDef.Body.Variables[variableIndex].IsPinned;
// If the variable is pinned, use single variable.
// If any of the loads is from "all", use single variable
if (isPinned || loads.Any(b => b.VariablesBefore[variableIndex].StoredByAll)) {
// If any of the loads 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)))) {
newVars = new List<VariableInfo>(1) { new VariableInfo() {
Variable = new ILVariable() {
Name = "var_" + variableIndex,
@ -838,7 +867,7 @@ namespace ICSharpCode.Decompiler.ILAst @@ -838,7 +867,7 @@ namespace ICSharpCode.Decompiler.ILAst
if (Array.IndexOf(a, b) >= 0)
return a;
var res = new T[a.Length + 1];
Array.Copy(a, res, a.Length);
Array.Copy(a, 0, res, 0, a.Length);
res[res.Length - 1] = b;
return res;
}

2
ICSharpCode.Decompiler/ILAst/LiftedOperators.cs

@ -515,7 +515,7 @@ namespace ICSharpCode.Decompiler.ILAst @@ -515,7 +515,7 @@ namespace ICSharpCode.Decompiler.ILAst
{
// IL ranges from removed nodes are assigned to the new operator expression
var removednodes = expr.GetSelfAndChildrenRecursive<ILExpression>().Except(n.GetSelfAndChildrenRecursive<ILExpression>());
n.ILRanges = ILRange.OrderAndJoint(n.ILRanges.Concat(removednodes.SelectMany(el => el.ILRanges)));
n.ILRanges.AddRange(removednodes.SelectMany(el => el.ILRanges));
// the new expression is wrapped in a container so that negations aren't pushed through lifted comparison operations
expr.Code = ILCode.Wrap;
expr.Arguments.Clear();

2
ICSharpCode.Decompiler/ILAst/TypeAnalysis.cs

@ -799,7 +799,7 @@ namespace ICSharpCode.Decompiler.ILAst @@ -799,7 +799,7 @@ namespace ICSharpCode.Decompiler.ILAst
case ILCode.Dup:
{
var arg = expr.Arguments.Single();
return arg.ExpectedType = InferTypeForExpression(expr.Arguments.Single(), expectedType);
return arg.ExpectedType = InferTypeForExpression(arg, expectedType);
}
default:
Debug.WriteLine("Type Inference: Can't handle " + expr.Code.GetName());

Loading…
Cancel
Save