|
|
@ -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(); |
|
|
|