Browse Source

Decompilation of comparisons between nullable operands and constant values

pull/205/head
Pent Ploompuu 15 years ago
parent
commit
cd37d3dfd2
  1. 150
      ICSharpCode.Decompiler/ILAst/NullableOperators.cs

150
ICSharpCode.Decompiler/ILAst/NullableOperators.cs

@ -52,16 +52,20 @@ namespace ICSharpCode.Decompiler.ILAst
{ {
public readonly Pattern[] Arguments; public readonly Pattern[] Arguments;
protected static readonly Pattern[] EmptyArguments = new Pattern[0];
protected Pattern(Pattern[] arguments) protected Pattern(Pattern[] arguments)
{ {
this.Arguments = arguments; this.Arguments = arguments;
} }
public abstract bool Match(ref PatternMatcher pm, ILExpression e); public virtual bool Match(ref PatternMatcher pm, ILExpression e)
{
if (e.Arguments.Count != this.Arguments.Length || e.Prefixes != null) return false;
for (int i = 0; i < this.Arguments.Length; i++)
if (!this.Arguments[i].Match(ref pm, e.Arguments[i])) return false;
return true;
}
public virtual ILExpression BuildNew(ref PatternMatcher pm, ILExpression[] args) public virtual ILExpression BuildNew(ref PatternMatcher pm)
{ {
throw new NotSupportedException(); throw new NotSupportedException();
} }
@ -94,11 +98,13 @@ namespace ICSharpCode.Decompiler.ILAst
public override bool Match(ref PatternMatcher pm, ILExpression e) public override bool Match(ref PatternMatcher pm, ILExpression e)
{ {
return e.Code == this.code; return e.Code == this.code && base.Match(ref pm, e);
} }
public override ILExpression BuildNew(ref PatternMatcher pm, ILExpression[] args) public override ILExpression BuildNew(ref PatternMatcher pm)
{ {
var args = new ILExpression[this.Arguments.Length];
for (int i = 0; i < args.Length; i++) args[i] = this.Arguments[i].BuildNew(ref pm);
return new ILExpression(code, null, args); return new ILExpression(code, null, args);
} }
} }
@ -121,25 +127,27 @@ namespace ICSharpCode.Decompiler.ILAst
var m = e.Operand as MethodReference; var m = e.Operand as MethodReference;
if (m == null || m.Name != this.method) return false; if (m == null || m.Name != this.method) return false;
var t = m.DeclaringType; var t = m.DeclaringType;
return t.Name == "Nullable`1" && t.Namespace == "System"; return t.Name == "Nullable`1" && t.Namespace == "System" && base.Match(ref pm, e);
} }
} }
enum OperatorType enum OperatorType
{ {
Equality, InEquality, Comparison, Unary, Other Equality, InEquality, Comparison, Other
} }
sealed class OperatorPattern : Pattern sealed class OperatorPattern : Pattern
{ {
OperatorType type; OperatorType type;
bool simple;
public OperatorPattern(params Pattern[] arguments) : base(arguments) { } public OperatorPattern() : base(null) { }
public OperatorPattern(OperatorType type, params Pattern[] arguments) public OperatorPattern(OperatorType type, bool simple)
: base(arguments) : this()
{ {
this.type = type; this.type = type;
this.simple = simple;
} }
public override bool Match(ref PatternMatcher pm, ILExpression e) public override bool Match(ref PatternMatcher pm, ILExpression e)
@ -180,27 +188,40 @@ namespace ICSharpCode.Decompiler.ILAst
case ILCode.Shl: case ILCode.Shl:
case ILCode.Shr: case ILCode.Shr:
case ILCode.Shr_Un: case ILCode.Shr_Un:
if (type != OperatorType.Other) return false;
break;
case ILCode.Not: case ILCode.Not:
case ILCode.Neg: case ILCode.Neg:
case ILCode.LogicNot: case ILCode.LogicNot:
if (type != OperatorType.Unary) return false; if (type != OperatorType.Other) return false;
break; break;
case ILCode.Call: case ILCode.Call:
var m = e.Operand as MethodReference; var m = e.Operand as MethodReference;
if (m == null || m.HasThis || !m.HasParameters || m.Parameters.Count != base.Arguments.Length || !IsCustomOperator(m)) return false; if (m == null || m.HasThis || !m.HasParameters || e.Arguments.Count > 2 || !IsCustomOperator(m.Name)) return false;
break; break;
default: return false; default: return false;
} }
if (pm.Operator != null) throw new InvalidOperationException(); if (pm.Operator != null) throw new InvalidOperationException();
pm.Operator = e; pm.Operator = e;
return true;
var a0 = e.Arguments[0];
if (!simple) return VariableAGetValueOrDefault.Match(ref pm, a0) && VariableBGetValueOrDefault.Match(ref pm, e.Arguments[1]);
if (e.Arguments.Count == 1) return VariableAGetValueOrDefault.Match(ref pm, a0);
if (VariableAGetValueOrDefault.Match(ref pm, a0)) {
pm.SimpleOperand = e.Arguments[1];
pm.SimpleLeftOperand = false;
return true;
}
if (VariableAGetValueOrDefault.Match(ref pm, e.Arguments[1])) {
pm.SimpleOperand = a0;
pm.SimpleLeftOperand = true;
return true;
}
return false;
} }
bool IsCustomOperator(MethodReference m) bool IsCustomOperator(string s)
{ {
switch (m.Name) { if (s.Length < 11 || !s.StartsWith("op_", StringComparison.Ordinal)) return false;
switch (s) {
case "op_Equality": case "op_Equality":
return type == OperatorType.Equality; return type == OperatorType.Equality;
case "op_Inequality": case "op_Inequality":
@ -210,15 +231,32 @@ namespace ICSharpCode.Decompiler.ILAst
case "op_LessThan": case "op_LessThan":
case "op_LessThanOrEqual": case "op_LessThanOrEqual":
return type == OperatorType.Comparison; return type == OperatorType.Comparison;
case "op_Addition":
case "op_Subtraction":
case "op_Multiply":
case "op_Division":
case "op_Modulus":
case "op_BitwiseAnd":
case "op_BitwiseOr":
case "op_ExclusiveOr":
case "op_LeftShift":
case "op_RightShift":
case "op_Negation":
case "op_UnaryNegation":
case "op_UnaryPlus":
return type == OperatorType.Other;
default: return false; default: return false;
} }
} }
public override ILExpression BuildNew(ref PatternMatcher pm, ILExpression[] args) public override ILExpression BuildNew(ref PatternMatcher pm)
{ {
var res = pm.Operator; var res = pm.Operator;
res.Arguments.Clear(); res.Arguments.Clear();
res.Arguments.AddRange(args); if (pm.SimpleLeftOperand) res.Arguments.Add(pm.SimpleOperand);
res.Arguments.Add(VariableA.BuildNew(ref pm));
if (pm.B != null) res.Arguments.Add(VariableB.BuildNew(ref pm));
else if (pm.SimpleOperand != null && !pm.SimpleLeftOperand) res.Arguments.Add(pm.SimpleOperand);
return res; return res;
} }
} }
@ -229,7 +267,7 @@ namespace ICSharpCode.Decompiler.ILAst
readonly bool b; readonly bool b;
public VariablePattern(ILCode code, bool b) public VariablePattern(ILCode code, bool b)
: base(EmptyArguments) : base(null)
{ {
this.code = code; this.code = code;
this.b = b; this.b = b;
@ -249,10 +287,11 @@ namespace ICSharpCode.Decompiler.ILAst
return true; return true;
} }
public override ILExpression BuildNew(ref PatternMatcher pm, ILExpression[] args) static readonly ILExpression[] EmptyArguments = new ILExpression[0];
public override ILExpression BuildNew(ref PatternMatcher pm)
{ {
var v = this.b ? pm.B : pm.A; var v = this.b ? pm.B : pm.A;
var e = new ILExpression(ILCode.Ldloc, v, args); var e = new ILExpression(ILCode.Ldloc, v, EmptyArguments);
if (v.Type.Name == "Nullable`1" && v.Type.Namespace == "System") e = new ILExpression(ILCode.ValueOf, null, e); if (v.Type.Name == "Nullable`1" && v.Type.Namespace == "System") e = new ILExpression(ILCode.ValueOf, null, e);
return e; return e;
} }
@ -267,24 +306,16 @@ namespace ICSharpCode.Decompiler.ILAst
static readonly Pattern CeqHasValue = new ILPattern(ILCode.Ceq, VariableAHasValue, VariableBHasValue); static readonly Pattern CeqHasValue = new ILPattern(ILCode.Ceq, VariableAHasValue, VariableBHasValue);
static readonly Pattern CneHasValue = new ILPattern(ILCode.Cne, VariableAHasValue, VariableBHasValue); static readonly Pattern CneHasValue = new ILPattern(ILCode.Cne, VariableAHasValue, VariableBHasValue);
static readonly Pattern AndHasValue = new ILPattern(ILCode.And, VariableAHasValue, VariableBHasValue); static readonly Pattern AndHasValue = new ILPattern(ILCode.And, VariableAHasValue, VariableBHasValue);
static readonly Pattern OperatorVariableAB = new OperatorPattern(VariableA, VariableB); static readonly Pattern OperatorVariableAB = new OperatorPattern();
static readonly Pattern[] LoadValuesNN = new[] { VariableAGetValueOrDefault, VariableBGetValueOrDefault };
static OperatorPattern OperatorNN(OperatorType type) static OperatorPattern OperatorNN(OperatorType type)
{ {
return new OperatorPattern(type, LoadValuesNN); return new OperatorPattern(type, false);
} }
static readonly Pattern[] LoadValuesNV = new[] { VariableAGetValueOrDefault, VariableB };
static OperatorPattern OperatorNV(OperatorType type) static OperatorPattern OperatorNV(OperatorType type)
{ {
return new OperatorPattern(type, LoadValuesNV); return new OperatorPattern(type, true);
}
static readonly Pattern[] LoadValuesVN = new[] { VariableA, VariableBGetValueOrDefault };
static OperatorPattern OperatorVN(OperatorType type)
{
return new OperatorPattern(type, LoadValuesVN);
} }
static readonly Pattern[] Comparisons = new Pattern[] { static readonly Pattern[] Comparisons = new Pattern[] {
@ -302,7 +333,7 @@ namespace ICSharpCode.Decompiler.ILAst
// > , < , >= , <= (struct) // > , < , >= , <= (struct)
AndHasValue & OperatorNN(OperatorType.Comparison), AndHasValue & OperatorNN(OperatorType.Comparison),
/* only first operand nullable */ /* only one operand nullable */
// == (primitive, decimal) // == (primitive, decimal)
OperatorNV(OperatorType.Equality) & VariableAHasValue, OperatorNV(OperatorType.Equality) & VariableAHasValue,
// == (struct) // == (struct)
@ -315,27 +346,9 @@ namespace ICSharpCode.Decompiler.ILAst
OperatorNV(OperatorType.Comparison) & VariableAHasValue, OperatorNV(OperatorType.Comparison) & VariableAHasValue,
// > , < , >= , <= (struct) // > , < , >= , <= (struct)
VariableAHasValue & OperatorNV(OperatorType.Comparison), VariableAHasValue & OperatorNV(OperatorType.Comparison),
/* only second operand nullable */
// == (primitive, decimal)
OperatorVN(OperatorType.Equality) & VariableBHasValue,
// == (struct)
VariableBHasValue & OperatorVN(OperatorType.Equality),
// != (primitive, decimal)
OperatorVN(OperatorType.InEquality) | !VariableBHasValue,
// != (struct)
!VariableBHasValue | OperatorVN(OperatorType.InEquality),
// > , <, >= , <= (primitive, decimal)
OperatorVN(OperatorType.Comparison) & VariableBHasValue,
// > , < , >= , <= (struct)
VariableBHasValue & OperatorVN(OperatorType.Comparison),
}; };
static readonly Pattern[] Other = new Pattern[] { static readonly Pattern[] Other = new Pattern[] {
/* single nullable operand */
new ILPattern(ILCode.TernaryOp, VariableAHasValue, new MethodPattern(ILCode.Newobj, ".ctor", new OperatorPattern(OperatorType.Unary, VariableAGetValueOrDefault)), new ILPattern(ILCode.DefaultValue)),
new OperatorPattern(VariableA),
/* both operands nullable */ /* both operands nullable */
// & (bool) // & (bool)
new ILPattern(ILCode.TernaryOp, VariableAGetValueOrDefault | (!VariableBGetValueOrDefault & !VariableAHasValue), VariableB, VariableA), new ILPattern(ILCode.TernaryOp, VariableAGetValueOrDefault | (!VariableBGetValueOrDefault & !VariableAHasValue), VariableB, VariableA),
@ -345,25 +358,16 @@ namespace ICSharpCode.Decompiler.ILAst
new ILPattern(ILCode.Or, VariableA, VariableB), new ILPattern(ILCode.Or, VariableA, VariableB),
// all other // all other
new ILPattern(ILCode.TernaryOp, AndHasValue, new MethodPattern(ILCode.Newobj, ".ctor", OperatorNN(OperatorType.Other)), new ILPattern(ILCode.DefaultValue)), new ILPattern(ILCode.TernaryOp, AndHasValue, new MethodPattern(ILCode.Newobj, ".ctor", OperatorNN(OperatorType.Other)), new ILPattern(ILCode.DefaultValue)),
OperatorVariableAB, null,
/* only one operand nullable */
new ILPattern(ILCode.TernaryOp, VariableAHasValue, new MethodPattern(ILCode.Newobj, ".ctor", OperatorNV(OperatorType.Other)), new ILPattern(ILCode.DefaultValue)),
null,
}; };
ILVariable A, B; ILVariable A, B;
ILExpression Operator; ILExpression Operator, SimpleOperand;
bool Match(Pattern p, ILExpression e) bool SimpleLeftOperand;
{
if (!p.Match(ref this, e) || e.Arguments.Count != p.Arguments.Length || e.Prefixes != null) return false;
for (int i = 0; i < p.Arguments.Length; i++)
if (!Match(p.Arguments[i], e.Arguments[i])) return false;
return true;
}
ILExpression BuildNew(Pattern p, ILExpression old)
{
var args = new ILExpression[p.Arguments.Length];
for (int i = 0; i < args.Length; i++) args[i] = BuildNew(p.Arguments[i], old);
return p.BuildNew(ref this, args);
}
public static bool Simplify(ILExpression expr) public static bool Simplify(ILExpression expr)
{ {
@ -371,8 +375,8 @@ namespace ICSharpCode.Decompiler.ILAst
var ps = Comparisons; var ps = Comparisons;
for (int i = 0; i < ps.Length; i++) { for (int i = 0; i < ps.Length; i++) {
var pm = new PatternMatcher(); var pm = new PatternMatcher();
if (!pm.Match(ps[i], expr)) continue; if (!ps[i].Match(ref pm, expr)) continue;
var n = pm.BuildNew(OperatorVariableAB, expr); var n = OperatorVariableAB.BuildNew(ref pm);
n.ILRanges = ILRange.OrderAndJoint(expr.GetSelfAndChildrenRecursive<ILExpression>().SelectMany(el => el.ILRanges)); n.ILRanges = ILRange.OrderAndJoint(expr.GetSelfAndChildrenRecursive<ILExpression>().SelectMany(el => el.ILRanges));
// the new expression is wrapped in a container so that negations aren't pushed through the comparison operation // the new expression is wrapped in a container so that negations aren't pushed through the comparison operation
expr.Code = ILCode.Wrap; expr.Code = ILCode.Wrap;
@ -387,8 +391,8 @@ namespace ICSharpCode.Decompiler.ILAst
var ps = Other; var ps = Other;
for (int i = 0; i < ps.Length; i += 2) { for (int i = 0; i < ps.Length; i += 2) {
var pm = new PatternMatcher(); var pm = new PatternMatcher();
if (!pm.Match(ps[i], expr)) continue; if (!ps[i].Match(ref pm, expr)) continue;
var n = pm.BuildNew(ps[i + 1], expr); var n = (ps[i + 1] ?? OperatorVariableAB).BuildNew(ref pm);
n.ILRanges = ILRange.OrderAndJoint(expr.GetSelfAndChildrenRecursive<ILExpression>().SelectMany(el => el.ILRanges)); n.ILRanges = ILRange.OrderAndJoint(expr.GetSelfAndChildrenRecursive<ILExpression>().SelectMany(el => el.ILRanges));
// the new expression is wrapped in a container so that negations aren't pushed through the comparison operation // the new expression is wrapped in a container so that negations aren't pushed through the comparison operation
expr.Code = ILCode.Wrap; expr.Code = ILCode.Wrap;

Loading…
Cancel
Save