From 587c36bea24a68626c63ab4bf024a0e687822628 Mon Sep 17 00:00:00 2001 From: Pent Ploompuu Date: Wed, 29 Jun 2011 04:59:42 +0300 Subject: [PATCH] Type analysis support for the remaining lifted operators --- .../Ast/AstMethodBodyBuilder.cs | 1 + ICSharpCode.Decompiler/ILAst/ILCodes.cs | 8 ++- ICSharpCode.Decompiler/ILAst/ILInlining.cs | 1 + .../ILAst/NullableOperators.cs | 64 +++++++++++-------- ICSharpCode.Decompiler/ILAst/TypeAnalysis.cs | 46 ++++++++----- 5 files changed, 77 insertions(+), 43 deletions(-) diff --git a/ICSharpCode.Decompiler/Ast/AstMethodBodyBuilder.cs b/ICSharpCode.Decompiler/Ast/AstMethodBodyBuilder.cs index 8aee8cdf5..029e260ec 100644 --- a/ICSharpCode.Decompiler/Ast/AstMethodBodyBuilder.cs +++ b/ICSharpCode.Decompiler/Ast/AstMethodBodyBuilder.cs @@ -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); diff --git a/ICSharpCode.Decompiler/ILAst/ILCodes.cs b/ICSharpCode.Decompiler/ILAst/ILCodes.cs index 8216a1b60..e6e5db0cf 100644 --- a/ICSharpCode.Decompiler/ILAst/ILCodes.cs +++ b/ICSharpCode.Decompiler/ILAst/ILCodes.cs @@ -319,9 +319,15 @@ namespace ICSharpCode.Decompiler.ILAst AddressOf, /// Simulates getting the value of the nullable argument in comparisons involving nullable values /// - /// 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(...), ...))" /// ValueOf, + /// Simulates creating a new nullable value from a valuetype argument + /// + /// 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(...), ...))" + /// + NullableOf, } public static class ILCodeUtil diff --git a/ICSharpCode.Decompiler/ILAst/ILInlining.cs b/ICSharpCode.Decompiler/ILAst/ILInlining.cs index 7257bf9ec..880592b10 100644 --- a/ICSharpCode.Decompiler/ILAst/ILInlining.cs +++ b/ICSharpCode.Decompiler/ILAst/ILInlining.cs @@ -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)) diff --git a/ICSharpCode.Decompiler/ILAst/NullableOperators.cs b/ICSharpCode.Decompiler/ILAst/NullableOperators.cs index 4b7b9b00a..342b1b1b9 100644 --- a/ICSharpCode.Decompiler/ILAst/NullableOperators.cs +++ b/ICSharpCode.Decompiler/ILAst/NullableOperators.cs @@ -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 { 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 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 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 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 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().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,20 +491,33 @@ 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().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; + 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().Except(n.GetSelfAndChildrenRecursive()); + 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; + } } } } diff --git a/ICSharpCode.Decompiler/ILAst/TypeAnalysis.cs b/ICSharpCode.Decompiler/ILAst/TypeAnalysis.cs index 238959cb3..add8d71d9 100644 --- a/ICSharpCode.Decompiler/ILAst/TypeAnalysis.cs +++ b/ICSharpCode.Decompiler/ILAst/TypeAnalysis.cs @@ -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 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 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: @@ -872,6 +867,20 @@ namespace ICSharpCode.Decompiler.ILAst type = ((TypeSpecification)type).ElementType; 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) { @@ -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); @@ -1068,6 +1079,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) {