Browse Source

Type analysis support for the remaining lifted operators

pull/205/head
Pent Ploompuu 15 years ago
parent
commit
587c36bea2
  1. 1
      ICSharpCode.Decompiler/Ast/AstMethodBodyBuilder.cs
  2. 8
      ICSharpCode.Decompiler/ILAst/ILCodes.cs
  3. 1
      ICSharpCode.Decompiler/ILAst/ILInlining.cs
  4. 64
      ICSharpCode.Decompiler/ILAst/NullableOperators.cs
  5. 46
      ICSharpCode.Decompiler/ILAst/TypeAnalysis.cs

1
ICSharpCode.Decompiler/Ast/AstMethodBodyBuilder.cs

@ -800,6 +800,7 @@ namespace ICSharpCode.Decompiler.Ast
return new ParenthesizedExpression(arg1); return new ParenthesizedExpression(arg1);
case ILCode.AddressOf: case ILCode.AddressOf:
return MakeRef(arg1); return MakeRef(arg1);
case ILCode.NullableOf:
case ILCode.ValueOf: return arg1; case ILCode.ValueOf: return arg1;
default: default:
throw new Exception("Unknown OpCode: " + byteCode.Code); throw new Exception("Unknown OpCode: " + byteCode.Code);

8
ICSharpCode.Decompiler/ILAst/ILCodes.cs

@ -319,9 +319,15 @@ namespace ICSharpCode.Decompiler.ILAst
AddressOf, AddressOf,
/// <summary>Simulates getting the value of the nullable argument in comparisons involving nullable values</summary> /// <summary>Simulates getting the value of the nullable argument in comparisons involving nullable values</summary>
/// <remarks> /// <remarks>
/// For example "stloc(v1, ...); stloc(v2, ...); logicand(ceq(call(Nullable`1::GetValueOrDefault, ldloca(v1)), ldloc(v2)), callgetter(Nullable`1::get_HasValue, ldloca(v1)))" becomes "ceq(ValueOf(...), ...)" /// For example "stloc(v1, ...); stloc(v2, ...); logicand(ceq(call(Nullable`1::GetValueOrDefault, ldloca(v1)), ldloc(v2)), callgetter(Nullable`1::get_HasValue, ldloca(v1)))" becomes "wrap(ceq(ValueOf(...), ...))"
/// </remarks> /// </remarks>
ValueOf, ValueOf,
/// <summary>Simulates creating a new nullable value from a valuetype argument</summary>
/// <remarks>
/// For example "stloc(v1, ...); stloc(v2, ...); ternaryop(callgetter(Nullable`1::get_HasValue, ldloca(v1)), newobj(Nullable`1::.ctor, add(call(Nullable`1::GetValueOrDefault, ldloca(v1)), ldloc(v2))), defaultvalue(Nullable`1))"
/// becomes "NullableOf(add(valueof(...), ...))"
/// </remarks>
NullableOf,
} }
public static class ILCodeUtil public static class ILCodeUtil

1
ICSharpCode.Decompiler/ILAst/ILInlining.cs

@ -429,6 +429,7 @@ namespace ICSharpCode.Decompiler.ILAst
case ILCode.Ldelema: case ILCode.Ldelema:
case ILCode.AddressOf: case ILCode.AddressOf:
case ILCode.ValueOf: case ILCode.ValueOf:
case ILCode.NullableOf:
// address-loading instructions are safe if their arguments are safe // address-loading instructions are safe if their arguments are safe
foreach (ILExpression arg in expr.Arguments) { foreach (ILExpression arg in expr.Arguments) {
if (!IsSafeForInlineOver(arg, expressionBeingMoved)) if (!IsSafeForInlineOver(arg, expressionBeingMoved))

64
ICSharpCode.Decompiler/ILAst/NullableOperators.cs

@ -143,7 +143,7 @@ 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" && base.Match(pm, e); return TypeAnalysis.IsNullableType(t) && base.Match(pm, e);
} }
} }
@ -328,7 +328,7 @@ namespace ICSharpCode.Decompiler.ILAst
{ {
var v = this.b ? pm.B : pm.A; var v = this.b ? pm.B : pm.A;
var e = new ILExpression(ILCode.Ldloc, v, EmptyArguments); 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 (TypeAnalysis.IsNullableType(v.Type)) e = new ILExpression(ILCode.ValueOf, null, e);
return e; return e;
} }
} }
@ -378,6 +378,11 @@ namespace ICSharpCode.Decompiler.ILAst
return new OperatorPattern(type, true); return new OperatorPattern(type, true);
} }
static Pattern NewObj(Pattern p)
{
return new MethodPattern(ILCode.Newobj, ".ctor", p);
}
static readonly Pattern[] Comparisons = new Pattern[] { static readonly Pattern[] Comparisons = new Pattern[] {
/* both operands nullable */ /* both operands nullable */
// == (primitive, decimal) // == (primitive, decimal)
@ -417,18 +422,18 @@ namespace ICSharpCode.Decompiler.ILAst
new ILPattern(ILCode.TernaryOp, VariableAGetValueOrDefault | (!VariableBGetValueOrDefault & !VariableAHasValue), VariableA, VariableB), new ILPattern(ILCode.TernaryOp, VariableAGetValueOrDefault | (!VariableBGetValueOrDefault & !VariableAHasValue), VariableA, VariableB),
new ILPattern(ILCode.Or, VariableA, VariableB), new ILPattern(ILCode.Or, VariableA, VariableB),
// null coalescing // null coalescing
new ILPattern(ILCode.TernaryOp, VariableAHasValue, new MethodPattern(ILCode.Newobj, ".ctor", VariableAGetValueOrDefault), VariableB), new ILPattern(ILCode.TernaryOp, VariableAHasValue, NewObj(VariableAGetValueOrDefault), VariableB),
new ILPattern(ILCode.NullCoalescing, VariableA, VariableB), new ILPattern(ILCode.NullCoalescing, 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, NewObj(OperatorNN(OperatorType.Other)), new ILPattern(ILCode.DefaultValue)),
null, OperatorVariableAB,
/* only one operand nullable */ /* only one operand nullable */
// & (bool) // & (bool)
new ILPattern(ILCode.TernaryOp, Any, VariableA, new MethodPattern(ILCode.Newobj, ".ctor", BooleanPattern.False)), new ILPattern(ILCode.TernaryOp, Any, VariableA, NewObj(BooleanPattern.False)),
new ILPattern(ILCode.And, VariableA, Any), new ILPattern(ILCode.And, VariableA, Any),
// | (bool) // | (bool)
new ILPattern(ILCode.TernaryOp, Any, new MethodPattern(ILCode.Newobj, ".ctor", BooleanPattern.True), VariableA), new ILPattern(ILCode.TernaryOp, Any, NewObj(BooleanPattern.True), VariableA),
new ILPattern(ILCode.Or, VariableA, Any), new ILPattern(ILCode.Or, VariableA, Any),
// == true // == true
VariableAGetValueOrDefault & VariableAHasValue, VariableAGetValueOrDefault & VariableAHasValue,
@ -452,8 +457,8 @@ namespace ICSharpCode.Decompiler.ILAst
new ILPattern(ILCode.TernaryOp, VariableAHasValue, VariableAGetValueOrDefault, Any), new ILPattern(ILCode.TernaryOp, VariableAHasValue, VariableAGetValueOrDefault, Any),
new ILPattern(ILCode.NullCoalescing, VariableA, Any), new ILPattern(ILCode.NullCoalescing, VariableA, Any),
// all other // all other
new ILPattern(ILCode.TernaryOp, VariableAHasValue, new MethodPattern(ILCode.Newobj, ".ctor", OperatorNV(OperatorType.Other)), new ILPattern(ILCode.DefaultValue)), new ILPattern(ILCode.TernaryOp, VariableAHasValue, NewObj(OperatorNV(OperatorType.Other)), new ILPattern(ILCode.DefaultValue)),
null, OperatorVariableAB,
}; };
ILVariable A, B; ILVariable A, B;
@ -478,15 +483,7 @@ namespace ICSharpCode.Decompiler.ILAst
for (int i = 0; i < ps.Length; i++) { for (int i = 0; i < ps.Length; i++) {
this.Reset(); this.Reset();
if (!ps[i].Match(this, expr)) continue; if (!ps[i].Match(this, expr)) continue;
var n = OperatorVariableAB.BuildNew(this); SetResult(expr, OperatorVariableAB.BuildNew(this));
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
expr.Code = ILCode.Wrap;
expr.Operand = null;
expr.Arguments.Clear();
expr.Arguments.Add(n);
expr.ILRanges.Clear();
expr.InferredType = n.InferredType;
return true; return true;
} }
} }
@ -494,20 +491,33 @@ namespace ICSharpCode.Decompiler.ILAst
for (int i = 0; i < ps.Length; i += 2) { for (int i = 0; i < ps.Length; i += 2) {
this.Reset(); this.Reset();
if (!ps[i].Match(this, expr)) continue; if (!ps[i].Match(this, expr)) continue;
var n = (ps[i + 1] ?? OperatorVariableAB).BuildNew(this); var n = ps[i + 1].BuildNew(this);
n.ILRanges = ILRange.OrderAndJoint(expr.GetSelfAndChildrenRecursive<ILExpression>().SelectMany(el => el.ILRanges)); SetResult(expr, n);
// the new expression is wrapped in a container so that negations aren't pushed through the comparison operation if (n.Code == ILCode.NullCoalescing) {
expr.Code = ILCode.Wrap; // if both operands are nullable then the result is also nullable
expr.Operand = null; if (n.Arguments[1].Code == ILCode.ValueOf) {
expr.Arguments.Clear(); n.Arguments[0] = n.Arguments[0].Arguments[0];
expr.Arguments.Add(n); n.Arguments[1] = n.Arguments[1].Arguments[0];
expr.ILRanges.Clear(); }
expr.InferredType = n.InferredType; } else if (n.Code != ILCode.Ceq && n.Code != ILCode.Cne) expr.Code = ILCode.NullableOf;
return true; return true;
} }
} }
return false; return false;
} }
static void SetResult(ILExpression expr, ILExpression n)
{
// 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)));
// 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();
expr.Arguments.Add(n);
expr.ILRanges.Clear();
expr.InferredType = n.InferredType;
}
} }
} }
} }

46
ICSharpCode.Decompiler/ILAst/TypeAnalysis.cs

@ -473,7 +473,7 @@ namespace ICSharpCode.Decompiler.ILAst
if (forceInferChildren) { if (forceInferChildren) {
InferTypeForExpression(expr.Arguments[0], typeSystem.TypedReference); InferTypeForExpression(expr.Arguments[0], typeSystem.TypedReference);
} }
return new TypeReference("System", "RuntimeTypeHandle", module, module, true); return new TypeReference("System", "RuntimeTypeHandle", module, module.TypeSystem.Corlib, true);
case ILCode.Refanyval: case ILCode.Refanyval:
if (forceInferChildren) { if (forceInferChildren) {
InferTypeForExpression(expr.Arguments[0], typeSystem.TypedReference); InferTypeForExpression(expr.Arguments[0], typeSystem.TypedReference);
@ -484,15 +484,10 @@ namespace ICSharpCode.Decompiler.ILAst
TypeReference t = InferTypeForExpression(expr.Arguments[0], UnpackPointer(expectedType)); TypeReference t = InferTypeForExpression(expr.Arguments[0], UnpackPointer(expectedType));
return t != null ? new ByReferenceType(t) : null; return t != null ? new ByReferenceType(t) : null;
} }
case ILCode.ValueOf: { case ILCode.ValueOf:
GenericInstanceType t = null; return GetNullableTypeArgument(InferTypeForExpression(expr.Arguments[0], CreateNullableType(expectedType)));
if (expectedType != null) { case ILCode.NullableOf:
t = new GenericInstanceType(new TypeReference("System", "Nullable`1", module, module.TypeSystem.Corlib)); return CreateNullableType(InferTypeForExpression(expr.Arguments[0], GetNullableTypeArgument(expectedType)));
t.GenericArguments.Add(expectedType);
}
t = InferTypeForExpression(expr.Arguments[0], t) as GenericInstanceType;
return t == null || t.Name != "Nullable`1" || t.Namespace != "System" ? null : t.GenericArguments[0];
}
#endregion #endregion
#region Arithmetic instructions #region Arithmetic instructions
case ILCode.Not: // bitwise complement case ILCode.Not: // bitwise complement
@ -560,16 +555,16 @@ namespace ICSharpCode.Decompiler.ILAst
case ILCode.Ldc_R8: case ILCode.Ldc_R8:
return typeSystem.Double; return typeSystem.Double;
case ILCode.Ldc_Decimal: case ILCode.Ldc_Decimal:
return new TypeReference("System", "Decimal", module, module, true); return new TypeReference("System", "Decimal", module, module.TypeSystem.Corlib, true);
case ILCode.Ldtoken: case ILCode.Ldtoken:
if (expr.Operand is TypeReference) if (expr.Operand is TypeReference)
return new TypeReference("System", "RuntimeTypeHandle", module, module, true); return new TypeReference("System", "RuntimeTypeHandle", module, module.TypeSystem.Corlib, true);
else if (expr.Operand is FieldReference) else if (expr.Operand is FieldReference)
return new TypeReference("System", "RuntimeFieldHandle", module, module, true); return new TypeReference("System", "RuntimeFieldHandle", module, module.TypeSystem.Corlib, true);
else else
return new TypeReference("System", "RuntimeMethodHandle", module, module, true); return new TypeReference("System", "RuntimeMethodHandle", module, module.TypeSystem.Corlib, true);
case ILCode.Arglist: case ILCode.Arglist:
return new TypeReference("System", "RuntimeArgumentHandle", module, module, true); return new TypeReference("System", "RuntimeArgumentHandle", module, module.TypeSystem.Corlib, true);
#endregion #endregion
#region Array instructions #region Array instructions
case ILCode.Newarr: case ILCode.Newarr:
@ -872,6 +867,20 @@ namespace ICSharpCode.Decompiler.ILAst
type = ((TypeSpecification)type).ElementType; type = ((TypeSpecification)type).ElementType;
return type; return type;
} }
static TypeReference GetNullableTypeArgument(TypeReference type)
{
var t = type as GenericInstanceType;
return IsNullableType(t) ? t.GenericArguments[0] : type;
}
GenericInstanceType CreateNullableType(TypeReference type)
{
if (type == null) return null;
var t = new GenericInstanceType(new TypeReference("System", "Nullable`1", module, module.TypeSystem.Corlib, true));
t.GenericArguments.Add(type);
return t;
}
TypeReference InferArgumentsInBinaryOperator(ILExpression expr, bool? isSigned, TypeReference expectedType) TypeReference InferArgumentsInBinaryOperator(ILExpression expr, bool? isSigned, TypeReference expectedType)
{ {
@ -884,6 +893,8 @@ namespace ICSharpCode.Decompiler.ILAst
} else if (IsSameType(rightPreferred, DoInferTypeForExpression(left, rightPreferred))) { } else if (IsSameType(rightPreferred, DoInferTypeForExpression(left, rightPreferred))) {
return left.InferredType = right.InferredType = left.ExpectedType = right.ExpectedType = rightPreferred; return left.InferredType = right.InferredType = left.ExpectedType = right.ExpectedType = rightPreferred;
} else if (IsSameType(leftPreferred, DoInferTypeForExpression(right, leftPreferred))) { } else if (IsSameType(leftPreferred, DoInferTypeForExpression(right, leftPreferred))) {
// re-infer the left expression with the preferred type to reset any conflicts caused by the rightPreferred type
DoInferTypeForExpression(left, leftPreferred);
return left.InferredType = right.InferredType = left.ExpectedType = right.ExpectedType = leftPreferred; return left.InferredType = right.InferredType = left.ExpectedType = right.ExpectedType = leftPreferred;
} else { } else {
left.ExpectedType = right.ExpectedType = TypeWithMoreInformation(leftPreferred, rightPreferred); left.ExpectedType = right.ExpectedType = TypeWithMoreInformation(leftPreferred, rightPreferred);
@ -1068,6 +1079,11 @@ namespace ICSharpCode.Decompiler.ILAst
} }
return false; return false;
} }
internal static bool IsNullableType(TypeReference type)
{
return type != null && type.Name == "Nullable`1" && type.Namespace == "System";
}
public static TypeCode GetTypeCode(TypeReference type) public static TypeCode GetTypeCode(TypeReference type)
{ {

Loading…
Cancel
Save