diff --git a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/ValueTypes.cs b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/ValueTypes.cs index 346c3b13e..322b34c95 100644 --- a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/ValueTypes.cs +++ b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/ValueTypes.cs @@ -273,5 +273,10 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty { Get().Field.ToString(); } + + public static string CallOnIntegerConstant() + { + return ulong.MaxValue.ToString(); + } } } \ No newline at end of file diff --git a/ICSharpCode.Decompiler/CSharp/CallBuilder.cs b/ICSharpCode.Decompiler/CSharp/CallBuilder.cs index c765b12d4..ad581e53e 100644 --- a/ICSharpCode.Decompiler/CSharp/CallBuilder.cs +++ b/ICSharpCode.Decompiler/CSharp/CallBuilder.cs @@ -1294,7 +1294,7 @@ namespace ICSharpCode.Decompiler.CSharp if (thisArgBox.Argument is LdObj ldobj) { thisArg = ldobj.Target; } else { - thisArg = new AddressOf(thisArgBox.Argument); + thisArg = new AddressOf(thisArgBox.Argument, thisArgBox.Type); } } target = expressionBuilder.TranslateTarget(thisArg, diff --git a/ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs b/ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs index 239d84976..a6a7862e2 100644 --- a/ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs +++ b/ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs @@ -2769,14 +2769,9 @@ namespace ICSharpCode.Decompiler.CSharp protected internal override TranslatedExpression VisitAddressOf(AddressOf inst, TranslationContext context) { - IType targetTypeHint = null; - if (context.TypeHint is ByReferenceType brt) { - targetTypeHint = brt.ElementType; - } else if (context.TypeHint is PointerType pt) { - targetTypeHint = pt.ElementType; - } // HACK: this is only correct if the argument is an R-value; otherwise we're missing the copy to the temporary - var value = Translate(inst.Value, targetTypeHint); + var value = Translate(inst.Value, inst.Type); + value = value.ConvertTo(inst.Type, this); return new DirectionExpression(FieldDirection.Ref, value) .WithILInstruction(inst) .WithRR(new ByReferenceResolveResult(value.ResolveResult, ReferenceKind.Ref)); diff --git a/ICSharpCode.Decompiler/IL/ILReader.cs b/ICSharpCode.Decompiler/IL/ILReader.cs index 730198abf..f840dce25 100644 --- a/ICSharpCode.Decompiler/IL/ILReader.cs +++ b/ICSharpCode.Decompiler/IL/ILReader.cs @@ -1199,7 +1199,7 @@ namespace ICSharpCode.Decompiler.IL case false: // field of value type: ldfld can handle temporaries if (PeekStackType() == StackType.O || PeekStackType() == StackType.Unknown) - return new AddressOf(Pop()); + return new AddressOf(Pop(), field.DeclaringType); else return PopPointer(); default: diff --git a/ICSharpCode.Decompiler/IL/Instructions.cs b/ICSharpCode.Decompiler/IL/Instructions.cs index 67d736150..0d01840b5 100644 --- a/ICSharpCode.Decompiler/IL/Instructions.cs +++ b/ICSharpCode.Decompiler/IL/Instructions.cs @@ -2529,9 +2529,10 @@ namespace ICSharpCode.Decompiler.IL /// Stores the value into an anonymous temporary variable, and returns the address of that variable. public sealed partial class AddressOf : ILInstruction { - public AddressOf(ILInstruction value) : base(OpCode.AddressOf) + public AddressOf(ILInstruction value, IType type) : base(OpCode.AddressOf) { this.Value = value; + this.type = type; } public static readonly SlotInfo ValueSlot = new SlotInfo("Value", canInlineInto: true); ILInstruction value; @@ -2581,6 +2582,12 @@ namespace ICSharpCode.Decompiler.IL return clone; } public override StackType ResultType { get { return StackType.Ref; } } + IType type; + /// Returns the type operand. + public IType Type { + get { return type; } + set { type = value; InvalidateFlags(); } + } protected override InstructionFlags ComputeFlags() { return value.Flags; @@ -2594,6 +2601,8 @@ namespace ICSharpCode.Decompiler.IL { WriteILRange(output, options); output.Write(OpCode); + output.Write(' '); + type.WriteTo(output); output.Write('('); this.value.WriteTo(output, options); output.Write(')'); @@ -2613,7 +2622,7 @@ namespace ICSharpCode.Decompiler.IL protected internal override bool PerformMatch(ILInstruction other, ref Patterns.Match match) { var o = other as AddressOf; - return o != null && this.value.PerformMatch(o.value, ref match); + return o != null && this.value.PerformMatch(o.value, ref match) && type.Equals(o.type); } } } @@ -7805,14 +7814,16 @@ namespace ICSharpCode.Decompiler.IL value = default(ILInstruction); return false; } - public bool MatchAddressOf(out ILInstruction value) + public bool MatchAddressOf(out ILInstruction value, out IType type) { var inst = this as AddressOf; if (inst != null) { value = inst.Value; + type = inst.Type; return true; } value = default(ILInstruction); + type = default(IType); return false; } public bool MatchThreeValuedBoolAnd(out ILInstruction left, out ILInstruction right) diff --git a/ICSharpCode.Decompiler/IL/Instructions.tt b/ICSharpCode.Decompiler/IL/Instructions.tt index 97add0105..db99589f3 100644 --- a/ICSharpCode.Decompiler/IL/Instructions.tt +++ b/ICSharpCode.Decompiler/IL/Instructions.tt @@ -179,7 +179,7 @@ CustomClassName("StLoc"), HasVariableOperand("Store", generateCheckInvariant: false), CustomArguments(("value", null)), ResultType("variable.StackType")), new OpCode("addressof", "Stores the value into an anonymous temporary variable, and returns the address of that variable.", - CustomClassName("AddressOf"), CustomArguments(("value", null)), ResultType("Ref")), + CustomClassName("AddressOf"), CustomArguments(("value", null)), ResultType("Ref"), HasTypeOperand), new OpCode("3vl.bool.and", "Three valued logic and. Inputs are of type bool? or I4, output is of type bool?. Unlike logic.and(), does not have short-circuiting behavior.", CustomClassName("ThreeValuedBoolAnd"), Binary, ResultType("O")), new OpCode("3vl.bool.or", "Three valued logic or. Inputs are of type bool? or I4, output is of type bool?. Unlike logic.or(), does not have short-circuiting behavior.", diff --git a/ICSharpCode.Decompiler/IL/Instructions/PatternMatching.cs b/ICSharpCode.Decompiler/IL/Instructions/PatternMatching.cs index dbd25242f..ecc8dab60 100644 --- a/ICSharpCode.Decompiler/IL/Instructions/PatternMatching.cs +++ b/ICSharpCode.Decompiler/IL/Instructions/PatternMatching.cs @@ -418,7 +418,7 @@ namespace ICSharpCode.Decompiler.IL { if (this is LdObj ldobj && ldobj.Target is LdFlda ldflda && ldobj.UnalignedPrefix == 0 && !ldobj.IsVolatile) { field = ldflda.Field; - if (field.DeclaringType.IsReferenceType == true || !ldflda.Target.MatchAddressOf(out target)) { + if (field.DeclaringType.IsReferenceType == true || !ldflda.Target.MatchAddressOf(out target, out _)) { target = ldflda.Target; } return true; diff --git a/ICSharpCode.Decompiler/IL/Transforms/EarlyExpressionTransforms.cs b/ICSharpCode.Decompiler/IL/Transforms/EarlyExpressionTransforms.cs index 5ee5b2fbe..8b5b3c6e7 100644 --- a/ICSharpCode.Decompiler/IL/Transforms/EarlyExpressionTransforms.cs +++ b/ICSharpCode.Decompiler/IL/Transforms/EarlyExpressionTransforms.cs @@ -95,7 +95,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms temp = ldfldaTarget; range = range.Concat(temp.ILRanges); } - if (temp.MatchAddressOf(out var addressOfTarget) && addressOfTarget.MatchLdLoc(out var v)) { + if (temp.MatchAddressOf(out var addressOfTarget, out _) && addressOfTarget.MatchLdLoc(out var v)) { context.Step($"ldobj(...(addressof(ldloca {v.Name}))) => ldobj(...(ldloca {v.Name}))", inst); var replacement = new LdLoca(v).WithILRange(addressOfTarget); foreach (var r in range) { diff --git a/ICSharpCode.Decompiler/IL/Transforms/ILInlining.cs b/ICSharpCode.Decompiler/IL/Transforms/ILInlining.cs index d1d8a0e69..3f3a658d7 100644 --- a/ICSharpCode.Decompiler/IL/Transforms/ILInlining.cs +++ b/ICSharpCode.Decompiler/IL/Transforms/ILInlining.cs @@ -226,7 +226,8 @@ namespace ICSharpCode.Decompiler.IL.Transforms if (loadInst.OpCode == OpCode.LdLoca) { // it was an ldloca instruction, so we need to use the pseudo-opcode 'addressof' // to preserve the semantics of the compiler-generated temporary - loadInst.ReplaceWith(new AddressOf(inlinedExpression)); + Debug.Assert(((LdLoca)loadInst).Variable == v); + loadInst.ReplaceWith(new AddressOf(inlinedExpression, v.Type)); } else { loadInst.ReplaceWith(inlinedExpression); } diff --git a/ICSharpCode.Decompiler/IL/Transforms/NullPropagationTransform.cs b/ICSharpCode.Decompiler/IL/Transforms/NullPropagationTransform.cs index 9288c447b..90d41d06d 100644 --- a/ICSharpCode.Decompiler/IL/Transforms/NullPropagationTransform.cs +++ b/ICSharpCode.Decompiler/IL/Transforms/NullPropagationTransform.cs @@ -203,7 +203,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms return false; // setter/adder/remover cannot be called with ?. syntax } inst = call.Arguments[0]; - if ((call.ConstrainedTo ?? call.Method.DeclaringType).IsReferenceType == false && inst.MatchAddressOf(out var arg)) { + if ((call.ConstrainedTo ?? call.Method.DeclaringType).IsReferenceType == false && inst.MatchAddressOf(out var arg, out _)) { inst = arg; } // ensure the access chain does not contain any 'nullable.unwrap' that aren't directly part of the chain diff --git a/ICSharpCode.Decompiler/IL/Transforms/TransformExpressionTrees.cs b/ICSharpCode.Decompiler/IL/Transforms/TransformExpressionTrees.cs index 07ba5adff..537c1fb03 100644 --- a/ICSharpCode.Decompiler/IL/Transforms/TransformExpressionTrees.cs +++ b/ICSharpCode.Decompiler/IL/Transforms/TransformExpressionTrees.cs @@ -541,7 +541,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms if (target.ResultType == StackType.Ref) return target; else - return new AddressOf(target); + return new AddressOf(target, expectedType); case StackType.O: if (targetType.IsReferenceType == false) { return new Box(target, targetType); @@ -721,7 +721,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms if (member.DeclaringType.IsReferenceType == true) { inst = new LdFlda(target, (IField)member); } else { - inst = new LdFlda(new AddressOf(target), (IField)member); + inst = new LdFlda(new AddressOf(target, member.DeclaringType), (IField)member); } } if (typeHint.SkipModifiers() is ByReferenceType brt && !member.ReturnType.IsByRefLike) { diff --git a/ICSharpCode.Decompiler/IL/Transforms/UsingTransform.cs b/ICSharpCode.Decompiler/IL/Transforms/UsingTransform.cs index 471319713..86e0cecf2 100644 --- a/ICSharpCode.Decompiler/IL/Transforms/UsingTransform.cs +++ b/ICSharpCode.Decompiler/IL/Transforms/UsingTransform.cs @@ -224,7 +224,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms return false; var firstArg = callVirt.Arguments.FirstOrDefault(); if (!(firstArg.MatchUnboxAny(out var innerArg1, out var unboxType) && unboxType.IsKnownType(KnownTypeCode.IDisposable))) { - if (!firstArg.MatchAddressOf(out var innerArg2)) + if (!firstArg.MatchAddressOf(out var innerArg2, out _)) return false; return NullableLiftingTransform.MatchGetValueOrDefault(innerArg2, objVar) || (innerArg2 is NullableUnwrap unwrap