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

8
ICSharpCode.Decompiler/ILAst/ILCodes.cs

@ -319,9 +319,15 @@ namespace ICSharpCode.Decompiler.ILAst @@ -319,9 +319,15 @@ namespace ICSharpCode.Decompiler.ILAst
AddressOf,
/// <summary>Simulates getting the value of the nullable argument in comparisons involving nullable values</summary>
/// <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>
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

1
ICSharpCode.Decompiler/ILAst/ILInlining.cs

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

62
ICSharpCode.Decompiler/ILAst/NullableOperators.cs

@ -143,7 +143,7 @@ namespace ICSharpCode.Decompiler.ILAst @@ -143,7 +143,7 @@ namespace ICSharpCode.Decompiler.ILAst
var m = e.Operand as MethodReference;
if (m == null || m.Name != this.method) return false;
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 @@ -328,7 +328,7 @@ namespace ICSharpCode.Decompiler.ILAst
{
var v = this.b ? pm.B : pm.A;
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;
}
}
@ -378,6 +378,11 @@ namespace ICSharpCode.Decompiler.ILAst @@ -378,6 +378,11 @@ namespace ICSharpCode.Decompiler.ILAst
return new OperatorPattern(type, true);
}
static Pattern NewObj(Pattern p)
{
return new MethodPattern(ILCode.Newobj, ".ctor", p);
}
static readonly Pattern[] Comparisons = new Pattern[] {
/* both operands nullable */
// == (primitive, decimal)
@ -417,18 +422,18 @@ namespace ICSharpCode.Decompiler.ILAst @@ -417,18 +422,18 @@ namespace ICSharpCode.Decompiler.ILAst
new ILPattern(ILCode.TernaryOp, VariableAGetValueOrDefault | (!VariableBGetValueOrDefault & !VariableAHasValue), VariableA, VariableB),
new ILPattern(ILCode.Or, VariableA, VariableB),
// 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),
// all other
new ILPattern(ILCode.TernaryOp, AndHasValue, new MethodPattern(ILCode.Newobj, ".ctor", OperatorNN(OperatorType.Other)), new ILPattern(ILCode.DefaultValue)),
null,
new ILPattern(ILCode.TernaryOp, AndHasValue, NewObj(OperatorNN(OperatorType.Other)), new ILPattern(ILCode.DefaultValue)),
OperatorVariableAB,
/* only one operand nullable */
// & (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),
// | (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),
// == true
VariableAGetValueOrDefault & VariableAHasValue,
@ -452,8 +457,8 @@ namespace ICSharpCode.Decompiler.ILAst @@ -452,8 +457,8 @@ namespace ICSharpCode.Decompiler.ILAst
new ILPattern(ILCode.TernaryOp, VariableAHasValue, VariableAGetValueOrDefault, Any),
new ILPattern(ILCode.NullCoalescing, VariableA, Any),
// all other
new ILPattern(ILCode.TernaryOp, VariableAHasValue, new MethodPattern(ILCode.Newobj, ".ctor", OperatorNV(OperatorType.Other)), new ILPattern(ILCode.DefaultValue)),
null,
new ILPattern(ILCode.TernaryOp, VariableAHasValue, NewObj(OperatorNV(OperatorType.Other)), new ILPattern(ILCode.DefaultValue)),
OperatorVariableAB,
};
ILVariable A, B;
@ -478,15 +483,7 @@ namespace ICSharpCode.Decompiler.ILAst @@ -478,15 +483,7 @@ namespace ICSharpCode.Decompiler.ILAst
for (int i = 0; i < ps.Length; i++) {
this.Reset();
if (!ps[i].Match(this, expr)) continue;
var n = 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;
SetResult(expr, OperatorVariableAB.BuildNew(this));
return true;
}
}
@ -494,19 +491,32 @@ namespace ICSharpCode.Decompiler.ILAst @@ -494,19 +491,32 @@ namespace ICSharpCode.Decompiler.ILAst
for (int i = 0; i < ps.Length; i += 2) {
this.Reset();
if (!ps[i].Match(this, expr)) continue;
var n = (ps[i + 1] ?? 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
var n = ps[i + 1].BuildNew(this);
SetResult(expr, n);
if (n.Code == ILCode.NullCoalescing) {
// if both operands are nullable then the result is also nullable
if (n.Arguments[1].Code == ILCode.ValueOf) {
n.Arguments[0] = n.Arguments[0].Arguments[0];
n.Arguments[1] = n.Arguments[1].Arguments[0];
}
} else if (n.Code != ILCode.Ceq && n.Code != ILCode.Cne) expr.Code = ILCode.NullableOf;
return true;
}
}
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.Operand = null;
expr.Arguments.Clear();
expr.Arguments.Add(n);
expr.ILRanges.Clear();
expr.InferredType = n.InferredType;
return true;
}
}
return false;
}
}
}

46
ICSharpCode.Decompiler/ILAst/TypeAnalysis.cs

@ -473,7 +473,7 @@ namespace ICSharpCode.Decompiler.ILAst @@ -473,7 +473,7 @@ namespace ICSharpCode.Decompiler.ILAst
if (forceInferChildren) {
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:
if (forceInferChildren) {
InferTypeForExpression(expr.Arguments[0], typeSystem.TypedReference);
@ -484,15 +484,10 @@ namespace ICSharpCode.Decompiler.ILAst @@ -484,15 +484,10 @@ namespace ICSharpCode.Decompiler.ILAst
TypeReference t = InferTypeForExpression(expr.Arguments[0], UnpackPointer(expectedType));
return t != null ? new ByReferenceType(t) : null;
}
case ILCode.ValueOf: {
GenericInstanceType t = null;
if (expectedType != null) {
t = new GenericInstanceType(new TypeReference("System", "Nullable`1", module, module.TypeSystem.Corlib));
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];
}
case ILCode.ValueOf:
return GetNullableTypeArgument(InferTypeForExpression(expr.Arguments[0], CreateNullableType(expectedType)));
case ILCode.NullableOf:
return CreateNullableType(InferTypeForExpression(expr.Arguments[0], GetNullableTypeArgument(expectedType)));
#endregion
#region Arithmetic instructions
case ILCode.Not: // bitwise complement
@ -560,16 +555,16 @@ namespace ICSharpCode.Decompiler.ILAst @@ -560,16 +555,16 @@ namespace ICSharpCode.Decompiler.ILAst
case ILCode.Ldc_R8:
return typeSystem.Double;
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:
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)
return new TypeReference("System", "RuntimeFieldHandle", module, module, true);
return new TypeReference("System", "RuntimeFieldHandle", module, module.TypeSystem.Corlib, true);
else
return new TypeReference("System", "RuntimeMethodHandle", module, module, true);
return new TypeReference("System", "RuntimeMethodHandle", module, module.TypeSystem.Corlib, true);
case ILCode.Arglist:
return new TypeReference("System", "RuntimeArgumentHandle", module, module, true);
return new TypeReference("System", "RuntimeArgumentHandle", module, module.TypeSystem.Corlib, true);
#endregion
#region Array instructions
case ILCode.Newarr:
@ -873,6 +868,20 @@ namespace ICSharpCode.Decompiler.ILAst @@ -873,6 +868,20 @@ namespace ICSharpCode.Decompiler.ILAst
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)
{
ILExpression left = expr.Arguments[0];
@ -884,6 +893,8 @@ namespace ICSharpCode.Decompiler.ILAst @@ -884,6 +893,8 @@ namespace ICSharpCode.Decompiler.ILAst
} else if (IsSameType(rightPreferred, DoInferTypeForExpression(left, rightPreferred))) {
return left.InferredType = right.InferredType = left.ExpectedType = right.ExpectedType = rightPreferred;
} 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;
} else {
left.ExpectedType = right.ExpectedType = TypeWithMoreInformation(leftPreferred, rightPreferred);
@ -1069,6 +1080,11 @@ namespace ICSharpCode.Decompiler.ILAst @@ -1069,6 +1080,11 @@ namespace ICSharpCode.Decompiler.ILAst
return false;
}
internal static bool IsNullableType(TypeReference type)
{
return type != null && type.Name == "Nullable`1" && type.Namespace == "System";
}
public static TypeCode GetTypeCode(TypeReference type)
{
if (type == null)

Loading…
Cancel
Save