From 63d6f4bbca3719b9431c8213591973090d944189 Mon Sep 17 00:00:00 2001 From: Daniel Grunwald Date: Sat, 23 Sep 2017 01:26:06 +0200 Subject: [PATCH] [nullables] Add lifting for operator~. --- .../CSharp/ExpressionBuilder.cs | 39 +++++++++---------- ICSharpCode.Decompiler/IL/Instructions.cs | 15 +------ ICSharpCode.Decompiler/IL/Instructions.tt | 2 +- .../IL/Instructions/UnaryInstruction.cs | 24 +++++++++++- .../IL/Transforms/NullableLiftingTransform.cs | 8 ++++ 5 files changed, 51 insertions(+), 37 deletions(-) diff --git a/ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs b/ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs index 20c41f4a4..cda479e98 100644 --- a/ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs +++ b/ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs @@ -345,33 +345,30 @@ namespace ICSharpCode.Decompiler.CSharp protected internal override TranslatedExpression VisitBitNot(BitNot inst, TranslationContext context) { var argument = Translate(inst.Argument); - - if (argument.Type.GetStackType().GetSize() < inst.ResultType.GetSize() - || argument.Type.Kind == TypeKind.Enum && argument.Type.IsSmallIntegerType()) + var argUType = NullableType.GetUnderlyingType(argument.Type); + + if (argUType.GetStackType().GetSize() < inst.UnderlyingResultType.GetSize() + || argUType.Kind == TypeKind.Enum && argUType.IsSmallIntegerType() + || argUType.GetStackType() == StackType.I + || argUType.IsKnownType(KnownTypeCode.Boolean) + || argUType.IsKnownType(KnownTypeCode.Char)) { // Argument is undersized (even after implicit integral promotion to I4) // -> we need to perform sign/zero-extension before the BitNot. // Same if the argument is an enum based on a small integer type // (those don't undergo numeric promotion in C# the way non-enum small integer types do). - argument = argument.ConvertTo(compilation.FindType(inst.ResultType.ToKnownTypeCode(argument.Type.GetSign())), this); - } - - var type = argument.Type.GetDefinition(); - if (type != null) { - // Handle those types that don't support operator ~ - // Note that it's OK to use a type that's larger than necessary. - switch (type.KnownTypeCode) { - case KnownTypeCode.Boolean: - case KnownTypeCode.Char: - argument = argument.ConvertTo(compilation.FindType(KnownTypeCode.UInt32), this); - break; - case KnownTypeCode.IntPtr: - argument = argument.ConvertTo(compilation.FindType(KnownTypeCode.Int64), this); - break; - case KnownTypeCode.UIntPtr: - argument = argument.ConvertTo(compilation.FindType(KnownTypeCode.UInt64), this); - break; + // Same if the type is one that does not support ~ (IntPtr, bool and char). + StackType targetStackType = inst.UnderlyingResultType; + if (targetStackType == StackType.I) { + // IntPtr doesn't support operator ~. + // Note that it's OK to use a type that's larger than necessary. + targetStackType = StackType.I8; + } + IType targetType = compilation.FindType(targetStackType.ToKnownTypeCode(argUType.GetSign())); + if (inst.IsLifted) { + targetType = NullableType.Create(compilation, targetType); } + argument = argument.ConvertTo(targetType, this); } return new UnaryOperatorExpression(UnaryOperatorType.BitNot, argument) diff --git a/ICSharpCode.Decompiler/IL/Instructions.cs b/ICSharpCode.Decompiler/IL/Instructions.cs index 3f32e182a..1c0fcff0d 100644 --- a/ICSharpCode.Decompiler/IL/Instructions.cs +++ b/ICSharpCode.Decompiler/IL/Instructions.cs @@ -952,9 +952,6 @@ namespace ICSharpCode.Decompiler.IL /// Bitwise NOT public sealed partial class BitNot : UnaryInstruction { - public BitNot(ILInstruction argument) : base(OpCode.BitNot, argument) - { - } public override void AcceptVisitor(ILVisitor visitor) { @@ -971,7 +968,7 @@ namespace ICSharpCode.Decompiler.IL protected internal override bool PerformMatch(ILInstruction other, ref Patterns.Match match) { var o = other as BitNot; - return o != null && this.Argument.PerformMatch(o.Argument, ref match); + return o != null && this.Argument.PerformMatch(o.Argument, ref match) && IsLifted == o.IsLifted && UnderlyingResultType == o.UnderlyingResultType; } } } @@ -5092,16 +5089,6 @@ namespace ICSharpCode.Decompiler.IL body = default(ILInstruction); return false; } - public bool MatchBitNot(out ILInstruction argument) - { - var inst = this as BitNot; - if (inst != null) { - argument = inst.Argument; - return true; - } - argument = default(ILInstruction); - return false; - } public bool MatchArglist() { var inst = this as Arglist; diff --git a/ICSharpCode.Decompiler/IL/Instructions.tt b/ICSharpCode.Decompiler/IL/Instructions.tt index 3c00ce01e..7074ac75f 100644 --- a/ICSharpCode.Decompiler/IL/Instructions.tt +++ b/ICSharpCode.Decompiler/IL/Instructions.tt @@ -70,7 +70,7 @@ CustomClassName("CompoundAssignmentInstruction"), CustomConstructor, CustomComputeFlags, MayThrow, CustomArguments("target", "value"), HasTypeOperand, ResultType("type.GetStackType()"), CustomWriteTo, MatchCondition("CheckForOverflow == o.CheckForOverflow && Sign == o.Sign && Operator == o.Operator")), - new OpCode("bit.not", "Bitwise NOT", Unary), + new OpCode("bit.not", "Bitwise NOT", Unary, CustomConstructor, MatchCondition("IsLifted == o.IsLifted && UnderlyingResultType == o.UnderlyingResultType")), new OpCode("arglist", "Retrieves the RuntimeArgumentHandle.", NoArguments, ResultType("O")), new OpCode("br", "Unconditional branch. goto target;", CustomClassName("Branch"), NoArguments, CustomConstructor, UnconditionalBranch, MayBranch, CustomComputeFlags, diff --git a/ICSharpCode.Decompiler/IL/Instructions/UnaryInstruction.cs b/ICSharpCode.Decompiler/IL/Instructions/UnaryInstruction.cs index 914919c27..604d615bd 100644 --- a/ICSharpCode.Decompiler/IL/Instructions/UnaryInstruction.cs +++ b/ICSharpCode.Decompiler/IL/Instructions/UnaryInstruction.cs @@ -16,15 +16,37 @@ // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER // DEALINGS IN THE SOFTWARE. +using System.Diagnostics; namespace ICSharpCode.Decompiler.IL { - partial class BitNot + partial class BitNot : ILiftableInstruction { + public BitNot(ILInstruction arg) : base(OpCode.BitNot, arg) + { + this.UnderlyingResultType = arg.ResultType; + } + + public BitNot(ILInstruction arg, bool isLifted, StackType stackType) : base(OpCode.BitNot, arg) + { + this.IsLifted = isLifted; + this.UnderlyingResultType = stackType; + } + + public bool IsLifted { get; } + public StackType UnderlyingResultType { get; } + public override StackType ResultType { get { return Argument.ResultType; } } + + internal override void CheckInvariant(ILPhase phase) + { + base.CheckInvariant(phase); + Debug.Assert(IsLifted == (ResultType == StackType.O)); + Debug.Assert(IsLifted || ResultType == UnderlyingResultType); + } } } diff --git a/ICSharpCode.Decompiler/IL/Transforms/NullableLiftingTransform.cs b/ICSharpCode.Decompiler/IL/Transforms/NullableLiftingTransform.cs index c4a6efadd..a4dd14ed9 100644 --- a/ICSharpCode.Decompiler/IL/Transforms/NullableLiftingTransform.cs +++ b/ICSharpCode.Decompiler/IL/Transforms/NullableLiftingTransform.cs @@ -370,6 +370,14 @@ namespace ICSharpCode.Decompiler.IL.Transforms }; return (newInst, bits); } + } else if (inst is BitNot bitnot) { + var (arg, bits) = DoLift(bitnot.Argument); + if (arg != null) { + var newInst = new BitNot(arg, isLifted: true, stackType: bitnot.ResultType) { + ILRange = bitnot.ILRange + }; + return (newInst, bits); + } } else if (inst is BinaryNumericInstruction binary) { var (left, leftBits) = DoLift(binary.Left); var (right, rightBits) = DoLift(binary.Right);