Browse Source

Added some comments and simplified the pattern matching

pull/205/head
Pent Ploompuu 15 years ago
parent
commit
f7811e69ab
  1. 92
      ICSharpCode.Decompiler/ILAst/NullableOperators.cs
  2. 4
      ICSharpCode.Decompiler/ILAst/PeepholeTransform.cs
  3. 5
      ICSharpCode.Decompiler/Tests/NullableOperators.cs

92
ICSharpCode.Decompiler/ILAst/NullableOperators.cs

@ -101,9 +101,9 @@ namespace ICSharpCode.Decompiler.ILAst
sealed class MethodPattern : Pattern sealed class MethodPattern : Pattern
{ {
readonly ILCode code; readonly ILCode code;
readonly Tuple<string, string, string> method; readonly string method;
public MethodPattern(ILCode code, Tuple<string, string, string> method, params Pattern[] arguments) public MethodPattern(ILCode code, string method, params Pattern[] arguments)
: base(arguments) : base(arguments)
{ {
this.code = code; this.code = code;
@ -114,33 +114,37 @@ namespace ICSharpCode.Decompiler.ILAst
{ {
if (e.Code != this.code) return false; if (e.Code != this.code) return false;
var m = e.Operand as MethodReference; var m = e.Operand as MethodReference;
if (m == null || m.Name != this.method.Item1) return false; if (m == null || m.Name != this.method) return false;
var t = m.DeclaringType; var t = m.DeclaringType;
return t.Name == this.method.Item2 && t.Namespace == this.method.Item3; return t.Name == "Nullable`1" && t.Namespace == "System";
} }
} }
enum OperatorType
{
Equality, InEquality, Other
}
sealed class OperatorPattern : Pattern sealed class OperatorPattern : Pattern
{ {
bool? equals, custom; OperatorType type;
public OperatorPattern(params Pattern[] arguments) : base(arguments) { } public OperatorPattern(params Pattern[] arguments) : base(arguments) { }
public OperatorPattern(bool? equals, bool? custom, Pattern[] arguments) public OperatorPattern(OperatorType type, Pattern[] arguments)
: base(arguments) : base(arguments)
{ {
this.equals = equals; this.type = type;
this.custom = custom;
} }
public override bool Match(ref PatternMatcher pm, ILExpression e) public override bool Match(ref PatternMatcher pm, ILExpression e)
{ {
switch (e.Code) { switch (e.Code) {
case ILCode.Ceq: case ILCode.Ceq:
if (!equals.GetValueOrDefault() || custom.GetValueOrDefault()) return false; if (type != OperatorType.Equality) return false;
break; break;
case ILCode.Cne: case ILCode.Cne:
if (equals.GetValueOrDefault(true) || custom.GetValueOrDefault()) return false; if (type != OperatorType.InEquality) return false;
break; break;
case ILCode.Cgt: case ILCode.Cgt:
case ILCode.Cgt_Un: case ILCode.Cgt_Un:
@ -150,10 +154,9 @@ namespace ICSharpCode.Decompiler.ILAst
case ILCode.Clt_Un: case ILCode.Clt_Un:
case ILCode.Cle: case ILCode.Cle:
case ILCode.Cle_Un: case ILCode.Cle_Un:
if (equals != null || custom.GetValueOrDefault()) return false; if (type != OperatorType.Other) return false;
break; break;
case ILCode.Call: case ILCode.Call:
if (custom != null && !custom.GetValueOrDefault()) return false;
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 || m.Parameters.Count != base.Arguments.Length || !IsCustomOperator(m)) return false;
break; break;
@ -168,14 +171,14 @@ namespace ICSharpCode.Decompiler.ILAst
{ {
switch (m.Name) { switch (m.Name) {
case "op_Equality": case "op_Equality":
return equals.GetValueOrDefault(); return type == OperatorType.Equality;
case "op_Inequality": case "op_Inequality":
return equals != null && !equals.GetValueOrDefault(); return type == OperatorType.InEquality;
case "op_GreaterThan": case "op_GreaterThan":
case "op_GreaterThanOrEqual": case "op_GreaterThanOrEqual":
case "op_LessThan": case "op_LessThan":
case "op_LessThanOrEqual": case "op_LessThanOrEqual":
return equals == null; return type == OperatorType.Other;
default: return false; default: return false;
} }
} }
@ -224,78 +227,76 @@ namespace ICSharpCode.Decompiler.ILAst
} }
} }
static readonly Tuple<string, string, string> GetValueOrDefault = new Tuple<string, string, string>("GetValueOrDefault", "Nullable`1", "System");
static readonly Tuple<string, string, string> get_HasValue = new Tuple<string, string, string>("get_HasValue", "Nullable`1", "System");
static readonly Pattern VariableRefA = new VariablePattern(ILCode.Ldloca, false), VariableRefB = new VariablePattern(ILCode.Ldloca, true); static readonly Pattern VariableRefA = new VariablePattern(ILCode.Ldloca, false), VariableRefB = new VariablePattern(ILCode.Ldloca, true);
static readonly Pattern VariableA = new VariablePattern(ILCode.Ldloc, false), VariableB = new VariablePattern(ILCode.Ldloc, true); static readonly Pattern VariableA = new VariablePattern(ILCode.Ldloc, false), VariableB = new VariablePattern(ILCode.Ldloc, true);
static readonly Pattern VariableAHasValue = new MethodPattern(ILCode.CallGetter, get_HasValue, VariableRefA); static readonly Pattern VariableAHasValue = new MethodPattern(ILCode.CallGetter, "get_HasValue", VariableRefA);
static readonly Pattern VariableAGetValueOrDefault = new MethodPattern(ILCode.Call, GetValueOrDefault, VariableRefA); static readonly Pattern VariableAGetValueOrDefault = new MethodPattern(ILCode.Call, "GetValueOrDefault", VariableRefA);
static readonly Pattern VariableBHasValue = new MethodPattern(ILCode.CallGetter, get_HasValue, VariableRefB); static readonly Pattern VariableBHasValue = new MethodPattern(ILCode.CallGetter, "get_HasValue", VariableRefB);
static readonly Pattern VariableBGetValueOrDefault = new MethodPattern(ILCode.Call, GetValueOrDefault, VariableRefB); static readonly Pattern VariableBGetValueOrDefault = new MethodPattern(ILCode.Call, "GetValueOrDefault", VariableRefB);
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[] LoadValuesNN = new[] { VariableAGetValueOrDefault, VariableBGetValueOrDefault }; static readonly Pattern[] LoadValuesNN = new[] { VariableAGetValueOrDefault, VariableBGetValueOrDefault };
static OperatorPattern OperatorNN(bool? equals = null, bool? custom = null) static OperatorPattern OperatorNN(OperatorType type)
{ {
return new OperatorPattern(equals, custom, LoadValuesNN); return new OperatorPattern(type, LoadValuesNN);
} }
static readonly Pattern[] LoadValuesNV = new[] { VariableAGetValueOrDefault, VariableB }; static readonly Pattern[] LoadValuesNV = new[] { VariableAGetValueOrDefault, VariableB };
static OperatorPattern OperatorNV(bool? equals = null, bool? custom = null) static OperatorPattern OperatorNV(OperatorType type)
{ {
return new OperatorPattern(equals, custom, LoadValuesNV); return new OperatorPattern(type, LoadValuesNV);
} }
static readonly Pattern[] LoadValuesVN = new[] { VariableA, VariableBGetValueOrDefault }; static readonly Pattern[] LoadValuesVN = new[] { VariableA, VariableBGetValueOrDefault };
static OperatorPattern OperatorVN(bool? equals = null, bool? custom = null) static OperatorPattern OperatorVN(OperatorType type)
{ {
return new OperatorPattern(equals, custom, LoadValuesVN); return new OperatorPattern(type, LoadValuesVN);
} }
static readonly Pattern[] Comparisons = new Pattern[] { static readonly Pattern[] Comparisons = new Pattern[] {
/* both operands nullable */ /* both operands nullable */
// == (Primitive, Decimal) // == (Primitive, Decimal)
OperatorNN(equals: true) & CeqHasValue, OperatorNN(OperatorType.Equality) & CeqHasValue,
// == (Struct) // == (Struct)
CeqHasValue & (!VariableAHasValue | OperatorNN(equals: true, custom: true)), CeqHasValue & (!VariableAHasValue | OperatorNN(OperatorType.Equality)),
// != (Primitive, Decimal) // != (Primitive, Decimal)
OperatorNN(equals: false) | CneHasValue, OperatorNN(OperatorType.InEquality) | CneHasValue,
// != (Struct) // != (Struct)
CneHasValue | (VariableAHasValue & OperatorNN(equals: false, custom: true)), CneHasValue | (VariableAHasValue & OperatorNN(OperatorType.InEquality)),
// > , < , >= , <= (Primitive, Decimal) // > , < , >= , <= (Primitive, Decimal)
OperatorNN() & AndHasValue, OperatorNN(OperatorType.Other) & AndHasValue,
// > , < , >= , <= (Struct) // > , < , >= , <= (Struct)
AndHasValue & OperatorNN(custom: true), AndHasValue & OperatorNN(OperatorType.Other),
/* only first operand nullable */ /* only first operand nullable */
// == (Primitive, Decimal) // == (Primitive, Decimal)
OperatorNV(equals: true) & VariableAHasValue, OperatorNV(OperatorType.Equality) & VariableAHasValue,
// == (Struct) // == (Struct)
VariableAHasValue & OperatorNV(equals: true, custom: true), VariableAHasValue & OperatorNV(OperatorType.Equality),
// != (Primitive, Decimal) // != (Primitive, Decimal)
OperatorNV(equals: false) | !VariableAHasValue, OperatorNV(OperatorType.InEquality) | !VariableAHasValue,
// != (Struct) // != (Struct)
!VariableAHasValue | OperatorNV(equals: false, custom: true), !VariableAHasValue | OperatorNV(OperatorType.InEquality),
// > , <, >= , <= (Primitive, Decimal) // > , <, >= , <= (Primitive, Decimal)
OperatorNV() & VariableAHasValue, OperatorNV(OperatorType.Other) & VariableAHasValue,
// > , < , >= , <= (Struct) // > , < , >= , <= (Struct)
VariableAHasValue & OperatorNV(custom: true), VariableAHasValue & OperatorNV(OperatorType.Other),
/* only second operand nullable */ /* only second operand nullable */
// == (Primitive, Decimal) // == (Primitive, Decimal)
OperatorVN(equals: true) & VariableBHasValue, OperatorVN(OperatorType.Equality) & VariableBHasValue,
// == (Struct) // == (Struct)
VariableBHasValue & OperatorVN(equals: true, custom: true), VariableBHasValue & OperatorVN(OperatorType.Equality),
// != (Primitive, Decimal) // != (Primitive, Decimal)
OperatorVN(equals: false) | !VariableBHasValue, OperatorVN(OperatorType.InEquality) | !VariableBHasValue,
// != (Struct) // != (Struct)
!VariableBHasValue | OperatorVN(equals: false, custom: true), !VariableBHasValue | OperatorVN(OperatorType.InEquality),
// > , <, >= , <= (Primitive, Decimal) // > , <, >= , <= (Primitive, Decimal)
OperatorVN() & VariableBHasValue, OperatorVN(OperatorType.Other) & VariableBHasValue,
// > , < , >= , <= (Struct) // > , < , >= , <= (Struct)
VariableBHasValue & OperatorVN(custom: true), VariableBHasValue & OperatorVN(OperatorType.Other),
}; };
ILVariable A, B; ILVariable A, B;
@ -328,6 +329,7 @@ namespace ICSharpCode.Decompiler.ILAst
var pm = new PatternMatcher(); var pm = new PatternMatcher();
if (!pm.Match(ps[i], expr)) continue; if (!pm.Match(ps[i], expr)) continue;
var n = pm.BuildNew(OperatorVariableAB, expr); var n = pm.BuildNew(OperatorVariableAB, expr);
// 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;
expr.Operand = null; expr.Operand = null;
expr.Arguments.Clear(); expr.Arguments.Clear();

4
ICSharpCode.Decompiler/ILAst/PeepholeTransform.cs

@ -892,6 +892,7 @@ namespace ICSharpCode.Decompiler.ILAst
ILExpression res = null; ILExpression res = null;
while (expr.Code == ILCode.LogicNot) { while (expr.Code == ILCode.LogicNot) {
a = expr.Arguments[0]; a = expr.Arguments[0];
// remove double negation
if (a.Code == ILCode.LogicNot) { if (a.Code == ILCode.LogicNot) {
res = a.Arguments[0]; res = a.Arguments[0];
res.ILRanges.AddRange(expr.ILRanges); res.ILRanges.AddRange(expr.ILRanges);
@ -914,6 +915,9 @@ namespace ICSharpCode.Decompiler.ILAst
return res; return res;
} }
/// <summary>
/// If the argument is a binary comparison operation then the negation is pushed through it
/// </summary>
static bool SimplifyLogicNotArgument(ILExpression expr) static bool SimplifyLogicNotArgument(ILExpression expr)
{ {
var a = expr.Arguments[0]; var a = expr.Arguments[0];

5
ICSharpCode.Decompiler/Tests/NullableOperators.cs

@ -20,6 +20,11 @@ using System;
public static class NullableOperators public static class NullableOperators
{ {
// C# uses 4 different patterns of IL for operators involving nullable values: bool, other primitive types, decimal, other structs.
// Different patterns are used depending on whether both of the operands are nullable or only the left/right operand is nullable.
// Negation must not be pushed through such comparisons because it would change the semantics.
// A comparison used in a condition differs somewhat from a comparison used as a simple value.
public static void BoolBasic(bool? a, bool? b) public static void BoolBasic(bool? a, bool? b)
{ {
if (a == b) if (a == b)

Loading…
Cancel
Save