diff --git a/ICSharpCode.Decompiler.Tests/TestCases/ILPretty/StackAllocDuplicateStore.cs b/ICSharpCode.Decompiler.Tests/TestCases/ILPretty/StackAllocDuplicateStore.cs index 421a41c8a..c6d0fdaa7 100644 --- a/ICSharpCode.Decompiler.Tests/TestCases/ILPretty/StackAllocDuplicateStore.cs +++ b/ICSharpCode.Decompiler.Tests/TestCases/ILPretty/StackAllocDuplicateStore.cs @@ -6,12 +6,12 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.ILPretty { public unsafe static int Seq(int a, int b, int c) { - byte* num = stackalloc byte[12]; - *(int*)num = a; - *(int*)num = 99; - ((int*)num)[1] = b; - ((int*)num)[2] = c; - Span span = new Span(num, 3); + byte* ptr = stackalloc byte[12]; + *(int*)ptr = a; + *(int*)ptr = 99; + ((int*)ptr)[1] = b; + ((int*)ptr)[2] = c; + Span span = new Span(ptr, 3); return span[0] + span[1] + span[2]; } } diff --git a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/CS73_StackAllocInitializers.cs b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/CS73_StackAllocInitializers.cs index 9485527df..aaa641053 100644 --- a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/CS73_StackAllocInitializers.cs +++ b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/CS73_StackAllocInitializers.cs @@ -274,21 +274,12 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty // rather than be reconstructed as 'stackalloc int[4] { 1, v, 3 }' (too few elements). public unsafe string PartialReinterpret(int v) { -#if OPT - byte* num = stackalloc byte[16]; - *(int*)num = 1; - ((int*)num)[1] = v; - ((int*)num)[2] = 3; - long num2 = 0L; - return UseBytePointer(num, &num2); -#else byte* ptr = stackalloc byte[16]; *(int*)ptr = 1; ((int*)ptr)[1] = v; ((int*)ptr)[2] = 3; long num = 0L; return UseBytePointer(ptr, &num); -#endif } public unsafe static string UseBytePointer(byte* ptr, long* length) @@ -298,15 +289,6 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty public unsafe string NegativeOffsets(int a, int b, int c) { -#if OPT - byte* num = stackalloc byte[12]; - *(int*)num = 1; - *((int*)num - 1) = 2; - *((int*)num - 2) = 3; - int* ptr = (int*)num; - Console.WriteLine(*ptr); - return UsePointer((byte*)ptr); -#else byte* ptr = stackalloc byte[12]; *(int*)ptr = 1; *((int*)ptr - 1) = 2; @@ -314,7 +296,6 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty int* ptr2 = (int*)ptr; Console.WriteLine(*ptr2); return UsePointer((byte*)ptr2); -#endif } public unsafe string UsePointer(byte* ptr) diff --git a/ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs b/ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs index c2b2d787b..92aa24f1f 100644 --- a/ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs +++ b/ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs @@ -803,7 +803,7 @@ namespace ICSharpCode.Decompiler.CSharp .WithRR(new TypeOfResolveResult(compilation.FindType(KnownTypeCode.Type), inst.Type)); return new MemberReferenceExpression(typeofExpr, "TypeHandle") .WithILInstruction(inst) - .WithRR(new TypeOfResolveResult(compilation.FindType(new TopLevelTypeName("System", "RuntimeTypeHandle")), inst.Type)); + .WithRR(new TypeOfResolveResult(compilation.FindType(KnownTypeCode.RuntimeTypeHandle), inst.Type)); } protected internal override TranslatedExpression VisitBitNot(BitNot inst, TranslationContext context) @@ -3554,7 +3554,7 @@ namespace ICSharpCode.Decompiler.CSharp { return new UndocumentedExpression { UndocumentedExpressionType = UndocumentedExpressionType.ArgListAccess } .WithILInstruction(inst) - .WithRR(new TypeResolveResult(compilation.FindType(new TopLevelTypeName("System", "RuntimeArgumentHandle")))); + .WithRR(new TypeResolveResult(compilation.FindType(KnownTypeCode.RuntimeArgumentHandle))); } protected internal override TranslatedExpression VisitMakeRefAny(MakeRefAny inst, TranslationContext context) @@ -3569,7 +3569,7 @@ namespace ICSharpCode.Decompiler.CSharp Arguments = { arg.Detach() } } .WithILInstruction(inst) - .WithRR(new TypeResolveResult(compilation.FindType(new TopLevelTypeName("System", "TypedReference")))); + .WithRR(new TypeResolveResult(compilation.FindType(KnownTypeCode.TypedReference))); } protected internal override TranslatedExpression VisitRefAnyType(RefAnyType inst, TranslationContext context) @@ -3579,7 +3579,7 @@ namespace ICSharpCode.Decompiler.CSharp Arguments = { Translate(inst.Argument).Expression.Detach() } }, "TypeHandle") .WithILInstruction(inst) - .WithRR(new TypeResolveResult(compilation.FindType(new TopLevelTypeName("System", "RuntimeTypeHandle")))); + .WithRR(new TypeResolveResult(compilation.FindType(KnownTypeCode.RuntimeTypeHandle))); } protected internal override TranslatedExpression VisitRefAnyValue(RefAnyValue inst, TranslationContext context) diff --git a/ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj b/ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj index cfd3320a9..df011f87c 100644 --- a/ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj +++ b/ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj @@ -177,4 +177,12 @@ + + + True + True + Instructions.tt + + + diff --git a/ICSharpCode.Decompiler/IL/BlockBuilder.cs b/ICSharpCode.Decompiler/IL/BlockBuilder.cs index fc53f15bc..1c598d2fe 100644 --- a/ICSharpCode.Decompiler/IL/BlockBuilder.cs +++ b/ICSharpCode.Decompiler/IL/BlockBuilder.cs @@ -96,7 +96,7 @@ namespace ICSharpCode.Decompiler.IL ILInstruction filter; if (eh.Kind == System.Reflection.Metadata.ExceptionRegionKind.Filter) { - var filterBlock = new BlockContainer(expectedResultType: StackType.I4); + var filterBlock = new BlockContainer(expectedResultType: compilation.FindType(KnownTypeCode.Int32)); filterBlock.AddILRange(new Interval(eh.FilterOffset, eh.HandlerOffset)); handlerContainers.Add(filterBlock.StartILOffset, filterBlock); filter = filterBlock; @@ -191,7 +191,9 @@ namespace ICSharpCode.Decompiler.IL { // assign the finally/filter container leave.TargetContainer = containerStack.Peek(); - leave.Value = ILReader.Cast(leave.Value, leave.TargetContainer.ExpectedResultType, null, leave.StartILOffset); + leave.Value = ILReader.Cast(leave.Value, + leave.TargetContainer.ResultType, + null, leave.StartILOffset); } break; case BlockContainer container: diff --git a/ICSharpCode.Decompiler/IL/ControlFlow/AsyncAwaitDecompiler.cs b/ICSharpCode.Decompiler/IL/ControlFlow/AsyncAwaitDecompiler.cs index 471dfa67b..f80a60a62 100644 --- a/ICSharpCode.Decompiler/IL/ControlFlow/AsyncAwaitDecompiler.cs +++ b/ICSharpCode.Decompiler/IL/ControlFlow/AsyncAwaitDecompiler.cs @@ -158,11 +158,11 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow FinalizeInlineMoveNext(function); if (methodType == AsyncMethodType.AsyncEnumerable || methodType == AsyncMethodType.AsyncEnumerator) { - ((BlockContainer)function.Body).ExpectedResultType = StackType.Void; + ((BlockContainer)function.Body).ExpectedResultType = context.TypeSystem.FindType(KnownTypeCode.Void); } else { - ((BlockContainer)function.Body).ExpectedResultType = underlyingReturnType.GetStackType(); + ((BlockContainer)function.Body).ExpectedResultType = underlyingReturnType; } // Re-run control flow simplification over the newly constructed set of gotos, diff --git a/ICSharpCode.Decompiler/IL/ControlFlow/ConditionDetection.cs b/ICSharpCode.Decompiler/IL/ControlFlow/ConditionDetection.cs index 5f7087a9f..b4194f3df 100644 --- a/ICSharpCode.Decompiler/IL/ControlFlow/ConditionDetection.cs +++ b/ICSharpCode.Decompiler/IL/ControlFlow/ConditionDetection.cs @@ -440,7 +440,7 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow && trueBlock.Instructions[0].MatchIfInstruction(out var nestedCondition, out var nestedTrueInst)) { context.Step("Combine 'if (cond1 && cond2)' in then-branch", ifInst); - ifInst.Condition = IfInstruction.LogicAnd(ifInst.Condition, nestedCondition); + ifInst.Condition = IfInstruction.LogicAnd(ifInst.Condition, nestedCondition, context.TypeSystem); ifInst.TrueInst = nestedTrueInst; } } diff --git a/ICSharpCode.Decompiler/IL/ControlFlow/DetectPinnedRegions.cs b/ICSharpCode.Decompiler/IL/ControlFlow/DetectPinnedRegions.cs index 3475d4d61..c06a35bc0 100644 --- a/ICSharpCode.Decompiler/IL/ControlFlow/DetectPinnedRegions.cs +++ b/ICSharpCode.Decompiler/IL/ControlFlow/DetectPinnedRegions.cs @@ -162,10 +162,11 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow for (int i = 0; i < container.Blocks.Count; i++) { var block = container.Blocks[i]; - if (IsNullSafeArrayToPointerPattern(block, out ILVariable v, out ILVariable p, out Block targetBlock)) + if (IsNullSafeArrayToPointerPattern(block, out ILVariable v, out ILVariable p, out Block targetBlock) + && v.Type is ArrayType arrayType) { context.Step("NullSafeArrayToPointerPattern", block); - ILInstruction arrayToPointer = new GetPinnableReference(new LdLoc(v), null); + ILInstruction arrayToPointer = new GetPinnableReference(new LdLoc(v), arrayType.ElementType, null); if (p.StackType != StackType.Ref) { arrayToPointer = new Conv(arrayToPointer, p.StackType.ToPrimitiveType(), false, Sign.None); @@ -180,16 +181,18 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow { context.Step("CustomRefPinPattern", block); ILInstruction gpr; - if (context.Settings.PatternBasedFixedStatement) + if (context.Settings.PatternBasedFixedStatement + && callGPR.Method.ReturnType is ByReferenceType brt) { - gpr = new GetPinnableReference(ldlocMem, callGPR.Method); + gpr = new GetPinnableReference(ldlocMem, brt.ElementType, callGPR.Method); } else { gpr = new IfInstruction( condition: new Comp(ComparisonKind.Inequality, Sign.None, ldlocMem, new LdNull()), trueInst: callGPR, - falseInst: new Conv(new LdcI4(0), PrimitiveType.Ref, checkForOverflow: false, inputSign: Sign.None) + falseInst: new Conv(new LdcI4(0), PrimitiveType.Ref, checkForOverflow: false, inputSign: Sign.None), + resultType: callGPR.Method.ReturnType ); } block.Instructions[block.Instructions.Count - 2] = new StLoc(v, gpr) @@ -818,7 +821,7 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow newVar.HasGeneratedName = oldVar.HasGeneratedName; oldVar.Function.Variables.Add(newVar); pinnedRegion.Variable = newVar; - pinnedRegion.Init = new GetPinnableReference(pinnedRegion.Init, arrayToPointer.Method).WithILRange(arrayToPointer); + pinnedRegion.Init = new GetPinnableReference(pinnedRegion.Init, arrayToPointer.Type, arrayToPointer.Method).WithILRange(arrayToPointer); conv.ReplaceWith(new LdLoc(newVar).WithILRange(conv)); } @@ -895,7 +898,7 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow newVar.HasGeneratedName = pinnedRegion.Variable.HasGeneratedName; pinnedRegion.Variable.Function.Variables.Add(newVar); pinnedRegion.Variable = newVar; - pinnedRegion.Init = new GetPinnableReference(pinnedRegion.Init, null); + pinnedRegion.Init = new GetPinnableReference(pinnedRegion.Init, context.TypeSystem.FindType(KnownTypeCode.Char), null); } return; } @@ -948,7 +951,7 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow body.Blocks.RemoveRange(1, body.Blocks.Count - 1); body.Blocks[0].Instructions.Add(new Branch(targetBlock)); } - pinnedRegion.Init = new GetPinnableReference(pinnedRegion.Init, null); + pinnedRegion.Init = new GetPinnableReference(pinnedRegion.Init, context.TypeSystem.FindType(KnownTypeCode.Char), null); ILVariable otherVar; ILInstruction otherVarInit; diff --git a/ICSharpCode.Decompiler/IL/ILReader.cs b/ICSharpCode.Decompiler/IL/ILReader.cs index 8db321ba5..365dffaed 100644 --- a/ICSharpCode.Decompiler/IL/ILReader.cs +++ b/ICSharpCode.Decompiler/IL/ILReader.cs @@ -190,14 +190,16 @@ namespace ICSharpCode.Decompiler.IL this.reader = body.GetILReader(); this.currentStack = ImmutableStack.Empty; this.expressionStack.Clear(); + IType methodReturnType; if (isRuntimeAsync) { - this.methodReturnStackType = TaskType.UnpackAnyTask(compilation, method.ReturnType).GetStackType(); + methodReturnType = TaskType.UnpackAnyTask(compilation, method.ReturnType); } else { - this.methodReturnStackType = method.ReturnType.GetStackType(); + methodReturnType = method.ReturnType; } + this.methodReturnStackType = methodReturnType.GetStackType(); InitParameterVariables(); localVariables = InitLocalVariables(); foreach (var v in localVariables) @@ -205,7 +207,7 @@ namespace ICSharpCode.Decompiler.IL v.InitialValueIsInitialized = body.LocalVariablesInitialized; v.UsesInitialValue = true; } - this.mainContainer = new BlockContainer(expectedResultType: methodReturnStackType); + this.mainContainer = new BlockContainer(expectedResultType: methodReturnType); this.blocksByOffset.Clear(); this.importQueue.Clear(); this.isBranchTarget = new BitSet(reader.Length); @@ -2128,10 +2130,7 @@ namespace ICSharpCode.Decompiler.IL // (note: if the variable is merged across control-flow branches, // we'll reset the type to be based on the StackType) IType type = inst.InferType(compilation); - if (type.GetStackType() != inst.ResultType) - { - type = compilation.FindType(inst.ResultType); - } + Debug.Assert(type.GetStackType() == inst.ResultType); var v = new ILVariable(VariableKind.StackSlot, type, inst.ResultType); v.HasGeneratedName = true; currentStack = currentStack.Push(v); diff --git a/ICSharpCode.Decompiler/IL/ILTypeExtensions.cs b/ICSharpCode.Decompiler/IL/ILTypeExtensions.cs index 1c0a436cb..5b6087792 100644 --- a/ICSharpCode.Decompiler/IL/ILTypeExtensions.cs +++ b/ICSharpCode.Decompiler/IL/ILTypeExtensions.cs @@ -166,7 +166,7 @@ namespace ICSharpCode.Decompiler.IL /// /// Infers the C# type an instruction expects of the child in , - /// i.e. the counterpart to : that one asks what a value is, this one + /// i.e. the counterpart to : that one asks what a value is, this one /// asks what the position it flows into says it should be. /// /// Returns SpecialType.UnknownType where the position names nothing. @@ -202,130 +202,5 @@ namespace ICSharpCode.Decompiler.IL return SpecialType.UnknownType; } } - - /// - /// Infers the C# type for an IL instruction. - /// - /// Returns SpecialType.UnknownType for unsupported instructions. - /// - /// - /// For instructions with StackType.O that produce a value type, or - /// instructions with StackType.Ref, we should aim to return the actual type - /// instead of SpecialType.UnknownType. - /// - /// If not returning UnknownType, must return a type that can store - /// the result of the instruction without loss of information. - /// - public static IType InferType(this ILInstruction inst, ICompilation compilation) - { - Debug.Assert(compilation != null); - switch (inst) - { - case NewObj newObj: - return newObj.Method.DeclaringType ?? SpecialType.UnknownType; - case NewArr newArr: - return new ArrayType(compilation, newArr.Type, newArr.Indices.Count); - case Call call: - return call.Method.ReturnType; - case CallVirt callVirt: - return callVirt.Method.ReturnType; - case CallIndirect calli: - return calli.FunctionPointerType.ReturnType; - case UserDefinedLogicOperator logicOp: - return logicOp.Method.ReturnType; - case LdObj ldobj: - return ldobj.Type; - case StObj stobj: - return stobj.Type; - case LdLoc ldloc: - return ldloc.Variable.Type; - case StLoc stloc: - return stloc.Variable.Type; - case LdLoca ldloca: - return new ByReferenceType(ldloca.Variable.Type); - case LdFlda ldflda: - return new ByReferenceType(ldflda.Field.Type); - case LdsFlda ldsflda: - return new ByReferenceType(ldsflda.Field.Type); - case LdElema ldelema: - if (ldelema.Array.InferType(compilation) is ArrayType arrayType) - { - if (TypeUtils.IsCompatibleTypeForMemoryAccess(arrayType.ElementType, ldelema.Type)) - { - return new ByReferenceType(arrayType.ElementType); - } - } - return new ByReferenceType(ldelema.Type); - case Comp comp: - switch (comp.LiftingKind) - { - case ComparisonLiftingKind.None: - case ComparisonLiftingKind.CSharp: - return compilation.FindType(KnownTypeCode.Boolean); - case ComparisonLiftingKind.ThreeValuedLogic: - return NullableType.Create(compilation, compilation.FindType(KnownTypeCode.Boolean)); - default: - return SpecialType.UnknownType; - } - case BinaryNumericInstruction bni: - if (bni.IsLifted) - return SpecialType.UnknownType; - switch (bni.Operator) - { - case BinaryNumericOperator.BitAnd: - case BinaryNumericOperator.BitOr: - case BinaryNumericOperator.BitXor: - var left = bni.Left.InferType(compilation); - var right = bni.Right.InferType(compilation); - if (left.Equals(right) && (left.IsCSharpPrimitiveIntegerType() || left.IsCSharpNativeIntegerType() || left.IsKnownType(KnownTypeCode.Boolean))) - return left; - else - return SpecialType.UnknownType; - default: - return SpecialType.UnknownType; - } - case LdLen ldLen: - if (compilation == null) - return SpecialType.UnknownType; - // Mirrors ExpressionBuilder.VisitLdLen, which picks Array.Length or - // Array.LongLength based on the result type alone. - return compilation.FindType(ldLen.ResultType == StackType.I4 ? KnownTypeCode.Int32 : KnownTypeCode.Int64); - case DefaultValue defaultValue: - return defaultValue.Type; - case ILFunction func when func.DelegateType != null: - return func.DelegateType; - case IfInstruction ifInst: - // For structs and byrefs, we don't want to return Unknown as a fallback to - // to FindType(StackType) wouldn't work. Valid IL should have the same - // type on both branches so we just return the first that works. - var thenType = ifInst.TrueInst.InferType(compilation); - if (thenType.CannotBeReconstructedFromStackType()) - { - return thenType; - } - var elseType = ifInst.FalseInst.InferType(compilation); - if (elseType.CannotBeReconstructedFromStackType()) - { - return elseType; - } - if (thenType.Equals(elseType)) - { - return thenType; - } - return SpecialType.UnknownType; - case SwitchInstruction switchInst: - foreach (var section in switchInst.Sections) - { - var bodyType = section.Body.InferType(compilation); - if (bodyType.CannotBeReconstructedFromStackType()) - { - return bodyType; - } - } - return SpecialType.UnknownType; - default: - return SpecialType.UnknownType; - } - } } } diff --git a/ICSharpCode.Decompiler/IL/Instructions.cs b/ICSharpCode.Decompiler/IL/Instructions.cs index 308ba2a73..18072653c 100644 --- a/ICSharpCode.Decompiler/IL/Instructions.cs +++ b/ICSharpCode.Decompiler/IL/Instructions.cs @@ -540,7 +540,8 @@ namespace ICSharpCode.Decompiler.IL.Patterns protected PatternInstruction(OpCode opCode) : base(opCode) { } - public override StackType ResultType { get { return StackType.Unknown; } } + public override StackType ResultType => (SpecialType.UnknownType).GetStackType(); + public override IType InferType(ICompilation compilation) => SpecialType.UnknownType; } } namespace ICSharpCode.Decompiler.IL @@ -738,7 +739,8 @@ namespace ICSharpCode.Decompiler.IL public Nop() : base(OpCode.Nop) { } - public override StackType ResultType { get { return StackType.Void; } } + public override StackType ResultType => StackType.Void; + public override IType InferType(ICompilation compilation) => compilation.FindType(KnownTypeCode.Void); public override void AcceptVisitor(ILVisitor visitor) { visitor.VisitNop(this); @@ -819,7 +821,8 @@ namespace ICSharpCode.Decompiler.IL clone.CloneVariables(); return clone; } - public override StackType ResultType { get { return DelegateType?.GetStackType() ?? StackType.O; } } + public override StackType ResultType => (DelegateType ?? SpecialType.UnknownType).GetStackType(); + public override IType InferType(ICompilation compilation) => DelegateType ?? SpecialType.UnknownType; public override void AcceptVisitor(ILVisitor visitor) { visitor.VisitILFunction(this); @@ -844,7 +847,7 @@ namespace ICSharpCode.Decompiler.IL /// A container of IL blocks. public sealed partial class BlockContainer : ILInstruction { - public override StackType ResultType { get { return this.ExpectedResultType; } } + public override void AcceptVisitor(ILVisitor visitor) { visitor.VisitBlockContainer(this); @@ -900,7 +903,8 @@ namespace ICSharpCode.Decompiler.IL this.Init = init; this.Body = body; } - public override StackType ResultType { get { return StackType.Void; } } + public override StackType ResultType => StackType.Void; + public override IType InferType(ICompilation compilation) => compilation.FindType(KnownTypeCode.Void); ILVariable variable; public ILVariable Variable { get { return variable; } @@ -1083,7 +1087,8 @@ namespace ICSharpCode.Decompiler.IL get { return type; } set { type = value; InvalidateFlags(); } } - public override StackType ResultType { get { return type.GetStackType(); } } + public override StackType ResultType => (this.type).GetStackType(); + public override IType InferType(ICompilation compilation) => this.type; public override void AcceptVisitor(ILVisitor visitor) { visitor.VisitNumericCompoundAssign(this); @@ -1142,7 +1147,8 @@ namespace ICSharpCode.Decompiler.IL /// Common instruction for dynamic compound assignments. public sealed partial class DynamicCompoundAssign : CompoundAssignmentInstruction { - public override StackType ResultType { get { return StackType.O; } } + public override StackType ResultType => StackType.O; + public override IType InferType(ICompilation compilation) => SpecialType.Dynamic; protected override InstructionFlags ComputeFlags() { return base.ComputeFlags() | InstructionFlags.MayThrow | InstructionFlags.SideEffect; @@ -1204,7 +1210,8 @@ namespace ICSharpCode.Decompiler.IL public Arglist() : base(OpCode.Arglist) { } - public override StackType ResultType { get { return StackType.O; } } + public override StackType ResultType => StackType.VT; + public override IType InferType(ICompilation compilation) => compilation.FindType(KnownTypeCode.RuntimeArgumentHandle); public override void AcceptVisitor(ILVisitor visitor) { visitor.VisitArglist(this); @@ -1229,7 +1236,8 @@ namespace ICSharpCode.Decompiler.IL /// Unconditional branch. goto target; public sealed partial class Branch : SimpleInstruction { - public override StackType ResultType { get { return StackType.Void; } } + public override StackType ResultType => StackType.Void; + public override IType InferType(ICompilation compilation) => compilation.FindType(KnownTypeCode.Void); protected override InstructionFlags ComputeFlags() { return InstructionFlags.EndPointUnreachable | InstructionFlags.MayBranch; @@ -1313,7 +1321,8 @@ namespace ICSharpCode.Decompiler.IL clone.Value = this.value.Clone(); return clone; } - public override StackType ResultType { get { return StackType.Void; } } + public override StackType ResultType => StackType.Void; + public override IType InferType(ICompilation compilation) => compilation.FindType(KnownTypeCode.Void); public override void AcceptVisitor(ILVisitor visitor) { visitor.VisitLeave(this); @@ -1612,7 +1621,8 @@ namespace ICSharpCode.Decompiler.IL clone.Body = this.body.Clone(); return clone; } - public override StackType ResultType { get { return StackType.Void; } } + public override StackType ResultType => StackType.Void; + public override IType InferType(ICompilation compilation) => compilation.FindType(KnownTypeCode.Void); public override void AcceptVisitor(ILVisitor visitor) { visitor.VisitSwitchSection(this); @@ -1767,6 +1777,8 @@ namespace ICSharpCode.Decompiler.IL base.Disconnected(); } + public override StackType ResultType => StackType.Void; + public override IType InferType(ICompilation compilation) => compilation.FindType(KnownTypeCode.Void); public override void AcceptVisitor(ILVisitor visitor) { visitor.VisitTryCatchHandler(this); @@ -1913,7 +1925,8 @@ namespace ICSharpCode.Decompiler.IL clone.Body = this.body.Clone(); return clone; } - public override StackType ResultType { get { return StackType.Void; } } + public override StackType ResultType => StackType.Void; + public override IType InferType(ICompilation compilation) => compilation.FindType(KnownTypeCode.Void); protected override InstructionFlags ComputeFlags() { return onExpression.Flags | body.Flags | InstructionFlags.ControlFlow | InstructionFlags.SideEffect; @@ -2057,7 +2070,8 @@ namespace ICSharpCode.Decompiler.IL clone.Body = this.body.Clone(); return clone; } - public override StackType ResultType { get { return StackType.Void; } } + public override StackType ResultType => StackType.Void; + public override IType InferType(ICompilation compilation) => compilation.FindType(KnownTypeCode.Void); protected override InstructionFlags ComputeFlags() { return InstructionFlags.MayWriteLocals | resourceExpression.Flags | body.Flags | InstructionFlags.ControlFlow | InstructionFlags.SideEffect; @@ -2101,7 +2115,8 @@ namespace ICSharpCode.Decompiler.IL public DebugBreak() : base(OpCode.DebugBreak) { } - public override StackType ResultType { get { return StackType.Void; } } + public override StackType ResultType => StackType.Void; + public override IType InferType(ICompilation compilation) => compilation.FindType(KnownTypeCode.Void); protected override InstructionFlags ComputeFlags() { return InstructionFlags.SideEffect; @@ -2234,7 +2249,8 @@ namespace ICSharpCode.Decompiler.IL public Ckfinite(ILInstruction argument) : base(OpCode.Ckfinite, argument) { } - public override StackType ResultType { get { return StackType.Void; } } + public override StackType ResultType => StackType.Void; + public override IType InferType(ICompilation compilation) => compilation.FindType(KnownTypeCode.Void); protected override InstructionFlags ComputeFlags() { return base.ComputeFlags() | InstructionFlags.MayThrow; @@ -2329,7 +2345,8 @@ namespace ICSharpCode.Decompiler.IL base.Disconnected(); } - public override StackType ResultType { get { return variable.StackType; } } + public override StackType ResultType => variable.StackType; + public override IType InferType(ICompilation compilation) => variable.Type; protected override InstructionFlags ComputeFlags() { return InstructionFlags.MayReadLocals; @@ -2380,7 +2397,8 @@ namespace ICSharpCode.Decompiler.IL { this.variable = variable ?? throw new ArgumentNullException(nameof(variable)); } - public override StackType ResultType { get { return StackType.Ref; } } + public override StackType ResultType => StackType.Ref; + public override IType InferType(ICompilation compilation) => new ByReferenceType(variable.Type); ILVariable variable; public ILVariable Variable { get { return variable; } @@ -2538,7 +2556,8 @@ namespace ICSharpCode.Decompiler.IL clone.Value = this.value.Clone(); return clone; } - public override StackType ResultType { get { return variable.StackType; } } + public override StackType ResultType => variable.StackType; + public override IType InferType(ICompilation compilation) => variable.Type; protected override InstructionFlags ComputeFlags() { return InstructionFlags.MayWriteLocals | value.Flags; @@ -2637,7 +2656,8 @@ namespace ICSharpCode.Decompiler.IL clone.Value = this.value.Clone(); return clone; } - public override StackType ResultType { get { return StackType.Ref; } } + public override StackType ResultType => StackType.Ref; + public override IType InferType(ICompilation compilation) => new ByReferenceType(type); IType type; /// Returns the type operand. public IType Type { @@ -2690,7 +2710,7 @@ namespace ICSharpCode.Decompiler.IL public ThreeValuedBoolAnd(ILInstruction left, ILInstruction right) : base(OpCode.ThreeValuedBoolAnd, left, right) { } - public override StackType ResultType { get { return StackType.O; } } + public override void AcceptVisitor(ILVisitor visitor) { visitor.VisitThreeValuedBoolAnd(this); @@ -2718,7 +2738,7 @@ namespace ICSharpCode.Decompiler.IL public ThreeValuedBoolOr(ILInstruction left, ILInstruction right) : base(OpCode.ThreeValuedBoolOr, left, right) { } - public override StackType ResultType { get { return StackType.O; } } + public override void AcceptVisitor(ILVisitor visitor) { visitor.VisitThreeValuedBoolOr(this); @@ -2784,10 +2804,26 @@ namespace ICSharpCode.Decompiler.IL /// If the input evaluates normally, evaluates to the input value (wrapped in Nullable<T> if the input is a non-nullable value type).If a nullable.unwrap instruction encounters a null input and jumps to the (endpoint of the) nullable.rewrap instruction,the nullable.rewrap instruction evaluates to null. public sealed partial class NullableRewrap : UnaryInstruction { - public NullableRewrap(ILInstruction argument) : base(OpCode.NullableRewrap, argument) + public NullableRewrap(ILInstruction argument, IType type) : base(OpCode.NullableRewrap, argument) { + this.type = type; + } + IType type; + /// Returns the type operand. + public IType Type { + get { return type; } + set { type = value; InvalidateFlags(); } + } + protected override void WriteToCore(ITextOutput output, ILAstWritingOptions options) + { + WriteILRange(output, options); + output.Write(OpCode); + output.Write(' '); + type.WriteTo(output); + output.Write('('); + Argument.WriteTo(output, options); + output.Write(')'); } - public override void AcceptVisitor(ILVisitor visitor) { visitor.VisitNullableRewrap(this); @@ -2803,7 +2839,7 @@ namespace ICSharpCode.Decompiler.IL protected internal override bool PerformMatch(ILInstruction? other, ref Patterns.Match match) { var o = other as NullableRewrap; - return o != null && this.Argument.PerformMatch(o.Argument, ref match); + return o != null && this.Argument.PerformMatch(o.Argument, ref match) && type.Equals(o.type); } } } @@ -2817,7 +2853,8 @@ namespace ICSharpCode.Decompiler.IL this.Value = value; } public readonly string Value; - public override StackType ResultType { get { return StackType.O; } } + public override StackType ResultType => StackType.Obj; + public override IType InferType(ICompilation compilation) => compilation.FindType(KnownTypeCode.String); protected override void WriteToCore(ITextOutput output, ILAstWritingOptions options) { WriteILRange(output, options); @@ -2854,7 +2891,8 @@ namespace ICSharpCode.Decompiler.IL this.Value = value; } public readonly string Value; - public override StackType ResultType { get { return StackType.O; } } + public override StackType ResultType => StackType.VT; + public override IType InferType(ICompilation compilation) => new ParameterizedType(compilation.FindType(KnownTypeCode.ReadOnlySpanOfT), compilation.FindType(KnownTypeCode.Boolean)); protected override void WriteToCore(ITextOutput output, ILAstWritingOptions options) { WriteILRange(output, options); @@ -2891,7 +2929,8 @@ namespace ICSharpCode.Decompiler.IL this.Value = value; } public readonly int Value; - public override StackType ResultType { get { return StackType.I4; } } + public override StackType ResultType => StackType.I4; + public override IType InferType(ICompilation compilation) => compilation.FindType(KnownTypeCode.Int32); protected override void WriteToCore(ITextOutput output, ILAstWritingOptions options) { WriteILRange(output, options); @@ -2928,7 +2967,8 @@ namespace ICSharpCode.Decompiler.IL this.Value = value; } public readonly long Value; - public override StackType ResultType { get { return StackType.I8; } } + public override StackType ResultType => StackType.I8; + public override IType InferType(ICompilation compilation) => compilation.FindType(KnownTypeCode.Int64); protected override void WriteToCore(ITextOutput output, ILAstWritingOptions options) { WriteILRange(output, options); @@ -2965,7 +3005,8 @@ namespace ICSharpCode.Decompiler.IL this.Value = value; } public readonly float Value; - public override StackType ResultType { get { return StackType.F4; } } + public override StackType ResultType => StackType.F4; + public override IType InferType(ICompilation compilation) => compilation.FindType(KnownTypeCode.Single); protected override void WriteToCore(ITextOutput output, ILAstWritingOptions options) { WriteILRange(output, options); @@ -3002,7 +3043,8 @@ namespace ICSharpCode.Decompiler.IL this.Value = value; } public readonly double Value; - public override StackType ResultType { get { return StackType.F8; } } + public override StackType ResultType => StackType.F8; + public override IType InferType(ICompilation compilation) => compilation.FindType(KnownTypeCode.Double); protected override void WriteToCore(ITextOutput output, ILAstWritingOptions options) { WriteILRange(output, options); @@ -3039,7 +3081,8 @@ namespace ICSharpCode.Decompiler.IL this.Value = value; } public readonly decimal Value; - public override StackType ResultType { get { return StackType.O; } } + public override StackType ResultType => StackType.O; + public override IType InferType(ICompilation compilation) => compilation.FindType(KnownTypeCode.Decimal); protected override void WriteToCore(ITextOutput output, ILAstWritingOptions options) { WriteILRange(output, options); @@ -3074,7 +3117,8 @@ namespace ICSharpCode.Decompiler.IL public LdNull() : base(OpCode.LdNull) { } - public override StackType ResultType { get { return StackType.O; } } + public override StackType ResultType => StackType.Obj; + public override IType InferType(ICompilation compilation) => compilation.FindType(KnownTypeCode.Object); public override void AcceptVisitor(ILVisitor visitor) { visitor.VisitLdNull(this); @@ -3106,7 +3150,8 @@ namespace ICSharpCode.Decompiler.IL readonly IMethod method; /// Returns the method operand. public IMethod Method => method; - public override StackType ResultType { get { return StackType.I; } } + public override StackType ResultType => StackType.I; + public override IType InferType(ICompilation compilation) => compilation.FindType(KnownTypeCode.IntPtr); protected override void WriteToCore(ITextOutput output, ILAstWritingOptions options) { WriteILRange(output, options); @@ -3148,7 +3193,8 @@ namespace ICSharpCode.Decompiler.IL readonly IMethod method; /// Returns the method operand. public IMethod Method => method; - public override StackType ResultType { get { return StackType.I; } } + public override StackType ResultType => StackType.I; + public override IType InferType(ICompilation compilation) => compilation.FindType(KnownTypeCode.IntPtr); protected override InstructionFlags ComputeFlags() { return base.ComputeFlags() | InstructionFlags.MayThrow; @@ -3209,7 +3255,8 @@ namespace ICSharpCode.Decompiler.IL readonly IMethod method; /// Returns the method operand. public IMethod Method => method; - public override StackType ResultType { get { return StackType.O; } } + public override StackType ResultType => StackType.Obj; + public override IType InferType(ICompilation compilation) => this.type; protected override InstructionFlags ComputeFlags() { return base.ComputeFlags() | InstructionFlags.MayThrow; @@ -3268,7 +3315,8 @@ namespace ICSharpCode.Decompiler.IL get { return type; } set { type = value; InvalidateFlags(); } } - public override StackType ResultType { get { return StackType.O; } } + public override StackType ResultType => StackType.VT; + public override IType InferType(ICompilation compilation) => compilation.FindType(KnownTypeCode.RuntimeTypeHandle); protected override void WriteToCore(ITextOutput output, ILAstWritingOptions options) { WriteILRange(output, options); @@ -3307,7 +3355,8 @@ namespace ICSharpCode.Decompiler.IL readonly IMember member; /// Returns the token operand. public IMember Member { get { return member; } } - public override StackType ResultType { get { return StackType.O; } } + public override StackType ResultType => StackType.VT; + public override IType InferType(ICompilation compilation) => compilation.FindType(member is IField ? KnownTypeCode.RuntimeFieldHandle : KnownTypeCode.RuntimeMethodHandle); protected override void WriteToCore(ITextOutput output, ILAstWritingOptions options) { WriteILRange(output, options); @@ -3342,7 +3391,8 @@ namespace ICSharpCode.Decompiler.IL public LocAlloc(ILInstruction argument) : base(OpCode.LocAlloc, argument) { } - public override StackType ResultType { get { return StackType.I; } } + public override StackType ResultType => StackType.I; + public override IType InferType(ICompilation compilation) => new PointerType(compilation.FindType(KnownTypeCode.Void)); protected override InstructionFlags ComputeFlags() { return base.ComputeFlags() | InstructionFlags.MayThrow; @@ -3386,7 +3436,8 @@ namespace ICSharpCode.Decompiler.IL get { return type; } set { type = value; InvalidateFlags(); } } - public override StackType ResultType { get { return StackType.O; } } + public override StackType ResultType => StackType.VT; + public override IType InferType(ICompilation compilation) => this.type; protected override InstructionFlags ComputeFlags() { return base.ComputeFlags() | InstructionFlags.MayThrow; @@ -3524,7 +3575,8 @@ namespace ICSharpCode.Decompiler.IL public bool IsVolatile { get; set; } /// Returns the alignment specified by the 'unaligned' prefix; or 0 if there was no 'unaligned' prefix. public byte UnalignedPrefix { get; set; } - public override StackType ResultType { get { return StackType.Void; } } + public override StackType ResultType => StackType.Void; + public override IType InferType(ICompilation compilation) => compilation.FindType(KnownTypeCode.Void); protected override InstructionFlags ComputeFlags() { return destAddress.Flags | sourceAddress.Flags | size.Flags | InstructionFlags.MayThrow | InstructionFlags.SideEffect; @@ -3675,7 +3727,8 @@ namespace ICSharpCode.Decompiler.IL public bool IsVolatile { get; set; } /// Returns the alignment specified by the 'unaligned' prefix; or 0 if there was no 'unaligned' prefix. public byte UnalignedPrefix { get; set; } - public override StackType ResultType { get { return StackType.Void; } } + public override StackType ResultType => StackType.Void; + public override IType InferType(ICompilation compilation) => compilation.FindType(KnownTypeCode.Void); protected override InstructionFlags ComputeFlags() { return address.Flags | value.Flags | size.Flags | InstructionFlags.MayThrow | InstructionFlags.SideEffect; @@ -3791,7 +3844,8 @@ namespace ICSharpCode.Decompiler.IL readonly IField @field; /// Returns the field operand. public IField Field { get { return @field; } } - public override StackType ResultType { get { return target.ResultType.IsIntegerType() ? StackType.I : StackType.Ref; } } + public override StackType ResultType => target.ResultType.IsIntegerType() ? StackType.I : StackType.Ref; + public override IType InferType(ICompilation compilation) => target.ResultType.IsIntegerType() ? new PointerType(field.Type) : new ByReferenceType(field.Type); protected override InstructionFlags ComputeFlags() { return target.Flags | (DelayExceptions ? InstructionFlags.None : InstructionFlags.MayThrow); @@ -3841,7 +3895,8 @@ namespace ICSharpCode.Decompiler.IL { this.@field = @field; } - public override StackType ResultType { get { return StackType.Ref; } } + public override StackType ResultType => StackType.Ref; + public override IType InferType(ICompilation compilation) => new ByReferenceType(field.Type); readonly IField @field; /// Returns the field operand. public IField Field { get { return @field; } } @@ -3886,7 +3941,8 @@ namespace ICSharpCode.Decompiler.IL get { return type; } set { type = value; InvalidateFlags(); } } - public override StackType ResultType { get { return type.GetStackType(); } } + public override StackType ResultType => (this.type).GetStackType(); + public override IType InferType(ICompilation compilation) => this.type; protected override InstructionFlags ComputeFlags() { return base.ComputeFlags() | InstructionFlags.MayThrow; @@ -3940,7 +3996,8 @@ namespace ICSharpCode.Decompiler.IL get { return type; } set { type = value; InvalidateFlags(); } } - public override StackType ResultType { get { return StackType.O; } } + public override StackType ResultType => StackType.Obj; + public override IType InferType(ICompilation compilation) => compilation.FindType(KnownTypeCode.Object); protected override void WriteToCore(ITextOutput output, ILAstWritingOptions options) { WriteILRange(output, options); @@ -4040,7 +4097,8 @@ namespace ICSharpCode.Decompiler.IL public bool IsVolatile { get; set; } /// Returns the alignment specified by the 'unaligned' prefix; or 0 if there was no 'unaligned' prefix. public byte UnalignedPrefix { get; set; } - public override StackType ResultType { get { return type.GetStackType(); } } + public override StackType ResultType => (this.type).GetStackType(); + public override IType InferType(ICompilation compilation) => this.type; protected override InstructionFlags ComputeFlags() { return target.Flags | InstructionFlags.SideEffect | InstructionFlags.MayThrow; @@ -4154,7 +4212,8 @@ namespace ICSharpCode.Decompiler.IL get { return type; } set { type = value; InvalidateFlags(); } } - public override StackType ResultType { get { return StackType.Ref; } } + public override StackType ResultType => StackType.Ref; + public override IType InferType(ICompilation compilation) => new ByReferenceType(this.type); protected override InstructionFlags ComputeFlags() { return target.Flags | InstructionFlags.SideEffect | InstructionFlags.MayThrow; @@ -4287,7 +4346,8 @@ namespace ICSharpCode.Decompiler.IL public bool IsVolatile { get; set; } /// Returns the alignment specified by the 'unaligned' prefix; or 0 if there was no 'unaligned' prefix. public byte UnalignedPrefix { get; set; } - public override StackType ResultType { get { return UnalignedPrefix == 0 ? type.GetStackType() : StackType.Void; } } + public override StackType ResultType => UnalignedPrefix == 0 ? type.GetStackType() : StackType.Void; + public override IType InferType(ICompilation compilation) => UnalignedPrefix == 0 ? type : compilation.FindType(KnownTypeCode.Void); protected override InstructionFlags ComputeFlags() { return target.Flags | value.Flags | InstructionFlags.SideEffect | InstructionFlags.MayThrow; @@ -4354,7 +4414,8 @@ namespace ICSharpCode.Decompiler.IL get { return type; } set { type = value; InvalidateFlags(); } } - public override StackType ResultType { get { return StackType.O; } } + public override StackType ResultType => StackType.Obj; + public override IType InferType(ICompilation compilation) => compilation.FindType(KnownTypeCode.Object); protected override void WriteToCore(ITextOutput output, ILAstWritingOptions options) { WriteILRange(output, options); @@ -4399,7 +4460,8 @@ namespace ICSharpCode.Decompiler.IL get { return type; } set { type = value; InvalidateFlags(); } } - public override StackType ResultType { get { return StackType.Ref; } } + public override StackType ResultType => StackType.Ref; + public override IType InferType(ICompilation compilation) => new ByReferenceType(this.type); protected override InstructionFlags ComputeFlags() { return base.ComputeFlags() | InstructionFlags.MayThrow; @@ -4453,7 +4515,8 @@ namespace ICSharpCode.Decompiler.IL get { return type; } set { type = value; InvalidateFlags(); } } - public override StackType ResultType { get { return type.GetStackType(); } } + public override StackType ResultType => (this.type).GetStackType(); + public override IType InferType(ICompilation compilation) => this.type; protected override InstructionFlags ComputeFlags() { return base.ComputeFlags() | InstructionFlags.SideEffect | InstructionFlags.MayThrow; @@ -4500,7 +4563,8 @@ namespace ICSharpCode.Decompiler.IL public NewObj(IMethod method) : base(OpCode.NewObj, method) { } - public override StackType ResultType { get { return Method.DeclaringType.GetStackType(); } } + public override StackType ResultType => (Method.DeclaringType).GetStackType(); + public override IType InferType(ICompilation compilation) => Method.DeclaringType; public override void AcceptVisitor(ILVisitor visitor) { visitor.VisitNewObj(this); @@ -4570,7 +4634,8 @@ namespace ICSharpCode.Decompiler.IL clone.Indices.AddRange(this.Indices.Select(arg => (ILInstruction)arg.Clone())); return clone; } - public override StackType ResultType { get { return StackType.O; } } + public override StackType ResultType => StackType.Obj; + public override IType InferType(ICompilation compilation) => new ArrayType(compilation, this.Type, this.Indices.Count); protected override InstructionFlags ComputeFlags() { return Indices.Aggregate(InstructionFlags.None, (f, arg) => f | arg.Flags) | InstructionFlags.MayThrow; @@ -4632,7 +4697,8 @@ namespace ICSharpCode.Decompiler.IL get { return type; } set { type = value; InvalidateFlags(); } } - public override StackType ResultType { get { return type.GetStackType(); } } + public override StackType ResultType => (this.type).GetStackType(); + public override IType InferType(ICompilation compilation) => this.type; protected override void WriteToCore(ITextOutput output, ILAstWritingOptions options) { WriteILRange(output, options); @@ -4667,7 +4733,8 @@ namespace ICSharpCode.Decompiler.IL public Throw(ILInstruction argument) : base(OpCode.Throw, argument) { } - public override StackType ResultType { get { return this.resultType; } } + public override StackType ResultType => StackType.Void; + public override IType InferType(ICompilation compilation) => compilation.FindType(KnownTypeCode.Void); protected override InstructionFlags ComputeFlags() { return base.ComputeFlags() | InstructionFlags.MayThrow | InstructionFlags.EndPointUnreachable; @@ -4704,7 +4771,8 @@ namespace ICSharpCode.Decompiler.IL public Rethrow() : base(OpCode.Rethrow) { } - public override StackType ResultType { get { return StackType.Void; } } + public override StackType ResultType => StackType.Void; + public override IType InferType(ICompilation compilation) => compilation.FindType(KnownTypeCode.Void); protected override InstructionFlags ComputeFlags() { return InstructionFlags.MayThrow | InstructionFlags.EndPointUnreachable; @@ -4748,7 +4816,8 @@ namespace ICSharpCode.Decompiler.IL get { return type; } set { type = value; InvalidateFlags(); } } - public override StackType ResultType { get { return StackType.I4; } } + public override StackType ResultType => StackType.I4; + public override IType InferType(ICompilation compilation) => compilation.FindType(KnownTypeCode.Int32); protected override void WriteToCore(ITextOutput output, ILAstWritingOptions options) { WriteILRange(output, options); @@ -4938,7 +5007,8 @@ namespace ICSharpCode.Decompiler.IL } public bool WithSystemIndex; public bool DelayExceptions; // NullReferenceException/IndexOutOfBoundsException only occurs when the reference is dereferenced - public override StackType ResultType { get { return StackType.Ref; } } + public override StackType ResultType => StackType.Ref; + public override IType InferType(ICompilation compilation) => new ByReferenceType(this.Type); /// Gets whether the 'readonly' prefix was applied to this instruction. public bool IsReadOnly { get; set; } protected override InstructionFlags ComputeFlags() @@ -5063,7 +5133,8 @@ namespace ICSharpCode.Decompiler.IL clone.Indices.AddRange(this.Indices.Select(arg => (ILInstruction)arg.Clone())); return clone; } - public override StackType ResultType { get { return StackType.Ref; } } + public override StackType ResultType => StackType.Ref; + public override IType InferType(ICompilation compilation) => new ByReferenceType(this.Type); /// Gets whether the 'readonly' prefix was applied to this instruction. public bool IsReadOnly { get; set; } protected override InstructionFlags ComputeFlags() @@ -5120,9 +5191,10 @@ namespace ICSharpCode.Decompiler.IL /// public sealed partial class GetPinnableReference : ILInstruction, IInstructionWithMethodOperand { - public GetPinnableReference(ILInstruction argument, IMethod? method) : base(OpCode.GetPinnableReference) + public GetPinnableReference(ILInstruction argument, IType type, IMethod? method) : base(OpCode.GetPinnableReference) { this.Argument = argument; + this.type = type; this.method = method; } public static readonly SlotInfo ArgumentSlot = new SlotInfo("Argument", canInlineInto: true); @@ -5175,7 +5247,14 @@ namespace ICSharpCode.Decompiler.IL clone.Argument = this.argument.Clone(); 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(); } + } + public override StackType ResultType => StackType.Ref; + public override IType InferType(ICompilation compilation) => new ByReferenceType(this.Type); readonly IMethod? method; /// Returns the method operand. public IMethod? Method => method; @@ -5192,6 +5271,8 @@ namespace ICSharpCode.Decompiler.IL { WriteILRange(output, options); output.Write(OpCode); + output.Write(' '); + type.WriteTo(output); if (method != null) { output.Write(' '); @@ -5216,7 +5297,7 @@ namespace ICSharpCode.Decompiler.IL protected internal override bool PerformMatch(ILInstruction? other, ref Patterns.Match match) { var o = other as GetPinnableReference; - return o != null && this.argument.PerformMatch(o.argument, ref match) && object.Equals(method, o.method); + return o != null && this.argument.PerformMatch(o.argument, ref match) && type.Equals(o.type) && object.Equals(method, o.method); } internal override void CheckInvariant(ILPhase phase, ICompilation compilation) { @@ -5280,7 +5361,8 @@ namespace ICSharpCode.Decompiler.IL clone.Argument = this.argument.Clone(); return clone; } - public override StackType ResultType { get { return StackType.I4; } } + public override StackType ResultType => StackType.I4; + public override IType InferType(ICompilation compilation) => compilation.FindType(KnownTypeCode.Int32); protected override InstructionFlags ComputeFlags() { return argument.Flags; @@ -5325,7 +5407,8 @@ namespace ICSharpCode.Decompiler.IL get { return type; } set { type = value; InvalidateFlags(); } } - public override StackType ResultType { get { return type.GetStackType(); } } + public override StackType ResultType => (this.type).GetStackType(); + public override IType InferType(ICompilation compilation) => this.type; protected override InstructionFlags ComputeFlags() { return base.ComputeFlags() | InstructionFlags.MayThrow; @@ -5368,7 +5451,8 @@ namespace ICSharpCode.Decompiler.IL readonly IMethod method; /// Returns the method operand. public IMethod Method => method; - public override StackType ResultType { get { return StackType.O; } } + public override StackType ResultType => (method.ReturnType).GetStackType(); + public override IType InferType(ICompilation compilation) => method.ReturnType; public static readonly SlotInfo LeftSlot = new SlotInfo("Left", canInlineInto: true); ILInstruction left = null!; public ILInstruction Left { @@ -6581,7 +6665,8 @@ namespace ICSharpCode.Decompiler.IL clone.SubPatterns.AddRange(this.SubPatterns.Select(arg => (ILInstruction)arg.Clone())); return clone; } - public override StackType ResultType { get { return StackType.I4; } } + public override StackType ResultType => StackType.I4; + public override IType InferType(ICompilation compilation) => compilation.FindType(KnownTypeCode.Boolean); protected override InstructionFlags ComputeFlags() { return InstructionFlags.MayWriteLocals | testedOperand.Flags | SubPatterns.Aggregate(InstructionFlags.None, (f, arg) => f | arg.Flags) | InstructionFlags.SideEffect | InstructionFlags.MayThrow | InstructionFlags.ControlFlow; @@ -6632,7 +6717,8 @@ namespace ICSharpCode.Decompiler.IL get { return type; } set { type = value; InvalidateFlags(); } } - public override StackType ResultType { get { return StackType.O; } } + public override StackType ResultType => StackType.VT; + public override IType InferType(ICompilation compilation) => compilation.FindType(KnownTypeCode.TypedReference); protected override void WriteToCore(ITextOutput output, ILAstWritingOptions options) { WriteILRange(output, options); @@ -6670,7 +6756,8 @@ namespace ICSharpCode.Decompiler.IL public RefAnyType(ILInstruction argument) : base(OpCode.RefAnyType, argument) { } - public override StackType ResultType { get { return StackType.O; } } + public override StackType ResultType => StackType.VT; + public override IType InferType(ICompilation compilation) => compilation.FindType(KnownTypeCode.RuntimeTypeHandle); public override void AcceptVisitor(ILVisitor visitor) { visitor.VisitRefAnyType(this); @@ -6705,7 +6792,8 @@ namespace ICSharpCode.Decompiler.IL get { return type; } set { type = value; InvalidateFlags(); } } - public override StackType ResultType { get { return StackType.Ref; } } + public override StackType ResultType => StackType.Ref; + public override IType InferType(ICompilation compilation) => new ByReferenceType(type); protected override InstructionFlags ComputeFlags() { return base.ComputeFlags() | InstructionFlags.MayThrow; @@ -6803,7 +6891,8 @@ namespace ICSharpCode.Decompiler.IL clone.Value = this.value.Clone(); return clone; } - public override StackType ResultType { get { return StackType.Void; } } + public override StackType ResultType => StackType.Void; + public override IType InferType(ICompilation compilation) => compilation.FindType(KnownTypeCode.Void); protected override InstructionFlags ComputeFlags() { return InstructionFlags.MayBranch | InstructionFlags.SideEffect | value.Flags; @@ -6899,7 +6988,8 @@ namespace ICSharpCode.Decompiler.IL clone.Value = this.value.Clone(); return clone; } - public override StackType ResultType { get { return GetResultMethod?.ReturnType.GetStackType() ?? StackType.Unknown; } } + public override StackType ResultType => (GetResultMethod?.ReturnType ?? SpecialType.UnknownType).GetStackType(); + public override IType InferType(ICompilation compilation) => GetResultMethod?.ReturnType ?? SpecialType.UnknownType; protected override InstructionFlags ComputeFlags() { return InstructionFlags.SideEffect | value.Flags; @@ -6941,7 +7031,8 @@ namespace ICSharpCode.Decompiler.IL /// Deconstruction statement public sealed partial class DeconstructInstruction : ILInstruction { - public override StackType ResultType { get { return StackType.Void; } } + public override StackType ResultType => StackType.Void; + public override IType InferType(ICompilation compilation) => compilation.FindType(KnownTypeCode.Void); public override void AcceptVisitor(ILVisitor visitor) { visitor.VisitDeconstructInstruction(this); @@ -8568,15 +8659,17 @@ namespace ICSharpCode.Decompiler.IL right = default(ILInstruction); return false; } - public bool MatchNullableRewrap([NotNullWhen(true)] out ILInstruction? argument) + public bool MatchNullableRewrap([NotNullWhen(true)] out ILInstruction? argument, [NotNullWhen(true)] out IType? type) { var inst = this as NullableRewrap; if (inst != null) { argument = inst.Argument; + type = inst.Type; return true; } argument = default(ILInstruction); + type = default(IType); return false; } public bool MatchLdStr([NotNullWhen(true)] out string? value) @@ -8989,16 +9082,18 @@ namespace ICSharpCode.Decompiler.IL array = default(ILInstruction); return false; } - public bool MatchGetPinnableReference([NotNullWhen(true)] out ILInstruction? argument, out IMethod? method) + public bool MatchGetPinnableReference([NotNullWhen(true)] out ILInstruction? argument, [NotNullWhen(true)] out IType? type, out IMethod? method) { var inst = this as GetPinnableReference; if (inst != null) { argument = inst.Argument; + type = inst.Type; method = inst.Method; return true; } argument = default(ILInstruction); + type = default(IType); method = default(IMethod?); return false; } diff --git a/ICSharpCode.Decompiler/IL/Instructions.tt b/ICSharpCode.Decompiler/IL/Instructions.tt index 2311fb5af..8a20bff22 100644 --- a/ICSharpCode.Decompiler/IL/Instructions.tt +++ b/ICSharpCode.Decompiler/IL/Instructions.tt @@ -35,7 +35,7 @@ new OpCode("CallInstruction", "Instruction with a list of arguments.", AbstractBaseClass, CustomChildren(new []{ new ArgumentInfo("arguments") { IsCollection = true }}), CustomConstructor, CustomWriteTo, MayThrow, SideEffect), - new OpCode("PatternInstruction", "Base class for pattern matching in ILAst.", AbstractBaseClass, ResultType("Unknown")) { Namespace = "ICSharpCode.Decompiler.IL.Patterns" }, + new OpCode("PatternInstruction", "Base class for pattern matching in ILAst.", AbstractBaseClass, ResultType("SpecialType.UnknownType")) { Namespace = "ICSharpCode.Decompiler.IL.Patterns" }, new OpCode("CompoundAssignmentInstruction", "Common instruction for compound assignments.", AbstractBaseClass, CustomConstructor, CustomArguments(("target", null), ("value", null))), new OpCode("DynamicInstruction", "Instruction representing a dynamic call site.", @@ -53,10 +53,10 @@ CustomChildren(new [] { new ChildInfo("body"), new ChildInfo("localFunctions") { IsCollection = true, Type = "ILFunction" } - }), CustomConstructor, CustomWriteTo, CustomComputeFlags, CustomVariableName("function"), ResultType("DelegateType?.GetStackType() ?? StackType.O") + }), CustomConstructor, CustomWriteTo, CustomComputeFlags, CustomVariableName("function"), ResultType("DelegateType ?? SpecialType.UnknownType") ), new OpCode("BlockContainer", "A container of IL blocks.", - ResultType("this.ExpectedResultType"), CustomConstructor, CustomVariableName("container"), + CustomConstructor, CustomVariableName("container"), MatchCondition("Patterns.ListMatch.DoMatch(this.Blocks, o.Blocks, ref match)")), new OpCode("Block", "A block of IL instructions.", CustomConstructor, CustomVariableName("block"), @@ -64,7 +64,7 @@ MatchCondition("Patterns.ListMatch.DoMatch(this.Instructions, o.Instructions, ref match)"), MatchCondition("this.FinalInstruction.PerformMatch(o.FinalInstruction, ref match)")), new OpCode("PinnedRegion", "A region where a pinned variable is used (initial representation of future fixed statement).", - ResultType("Void"), + VoidResult, HasVariableOperand("Store"), CustomChildren(new []{ new ChildInfo("init") { CanInlineInto = true }, @@ -76,7 +76,7 @@ MatchCondition("CheckForOverflow == o.CheckForOverflow && Sign == o.Sign && Operator == o.Operator && IsLifted == o.IsLifted")), new OpCode("numeric.compound", "Common instruction for numeric compound assignments.", CustomClassName("NumericCompoundAssign"), BaseClass("CompoundAssignmentInstruction"), CustomConstructor, CustomComputeFlags, - MayThrow, HasTypeOperand, ResultType("type.GetStackType()"), CustomWriteTo, + MayThrow, HasTypeOperand, ResultType("this.type"), CustomWriteTo, MatchCondition("CheckForOverflow == o.CheckForOverflow && Sign == o.Sign && Operator == o.Operator"), MatchCondition("this.EvalMode == o.EvalMode"), MatchCondition("this.TargetKind == o.TargetKind"), @@ -92,13 +92,13 @@ MatchCondition("Value.PerformMatch(o.Value, ref match)")), new OpCode("dynamic.compound", "Common instruction for dynamic compound assignments.", CustomClassName("DynamicCompoundAssign"), BaseClass("CompoundAssignmentInstruction"), - MayThrow, SideEffect, CustomWriteTo, CustomConstructor, ResultType("O"), + MayThrow, SideEffect, CustomWriteTo, CustomConstructor, ResultType("SpecialType.Dynamic", "O"), MatchCondition("this.EvalMode == o.EvalMode"), MatchCondition("this.TargetKind == o.TargetKind"), MatchCondition("Target.PerformMatch(o.Target, ref match)"), MatchCondition("Value.PerformMatch(o.Value, ref match)")), 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("arglist", "Retrieves the RuntimeArgumentHandle.", NoArguments, ResultType("RuntimeArgumentHandle", "VT")), new OpCode("br", "Unconditional branch. goto target;", CustomClassName("Branch"), NoArguments, CustomConstructor, UnconditionalBranch, MayBranch, MatchCondition("this.TargetBlock == o.TargetBlock")), @@ -123,7 +123,7 @@ MatchCondition("IsLifted == o.IsLifted && Value.PerformMatch(o.Value, ref match) && Patterns.ListMatch.DoMatch(this.Sections, o.Sections, ref match)")), new OpCode("switch.section", "Switch section within a switch statement", CustomClassName("SwitchSection"), CustomChildren(new [] { new ChildInfo("body") }), - CustomConstructor, CustomComputeFlags, CustomWriteTo, ResultType("Void"), + CustomConstructor, CustomComputeFlags, CustomWriteTo, VoidResult, MatchCondition("this.Labels.SetEquals(o.Labels) && this.HasNullLabel == o.HasNullLabel")), new OpCode("try.catch", "Try-catch statement.", BaseClass("TryInstruction"), CustomConstructor, CustomComputeFlags, CustomWriteTo, @@ -133,7 +133,8 @@ CustomChildren(new [] { new ChildInfo("filter"), new ChildInfo("body"), - }), HasVariableOperand("Store", generateCheckInvariant: false), CustomWriteTo, CustomComputeFlags), + }), HasVariableOperand("Store", generateCheckInvariant: false), + CustomWriteTo, CustomComputeFlags, VoidResult), new OpCode("try.finally", "Try-finally statement", BaseClass("TryInstruction"), CustomConstructor, CustomWriteTo, CustomComputeFlags, MatchCondition("TryBlock.PerformMatch(o.TryBlock, ref match) && finallyBlock.PerformMatch(o.finallyBlock, ref match)")), @@ -145,12 +146,12 @@ CustomChildren(new [] { new ArgumentInfo("onExpression") { ExpectedTypes = new[] { "O" }}, new ChildInfo("body") - }), CustomWriteTo, ControlFlow, SideEffect, ResultType("Void")), + }), CustomWriteTo, ControlFlow, SideEffect, VoidResult), new OpCode("using", "Using statement", CustomClassName("UsingInstruction"), HasVariableOperand("Store"), CustomChildren(new [] { new ArgumentInfo("resourceExpression") { ExpectedTypes = new[] { "O" }}, new ChildInfo("body") - }), CustomWriteTo, ControlFlow, SideEffect, ResultType("Void")), + }), CustomWriteTo, ControlFlow, SideEffect, VoidResult), new OpCode("debug.break", "Breakpoint instruction", NoArguments, VoidResult, SideEffect), new OpCode("comp", "Comparison. The inputs must be both integers; or both floats; or both object references. " @@ -174,19 +175,19 @@ Unary, CustomConstructor, MatchCondition("CheckForOverflow == o.CheckForOverflow && Kind == o.Kind && InputSign == o.InputSign && TargetType == o.TargetType && IsLifted == o.IsLifted")), new OpCode("ldloc", "Loads the value of a local variable. (ldarg/ldloc)", - CustomClassName("LdLoc"), NoArguments, HasVariableOperand("Load"), ResultType("variable.StackType")), + CustomClassName("LdLoc"), NoArguments, HasVariableOperand("Load"), ResultType("variable.Type", "variable.StackType")), new OpCode("ldloca", "Loads the address of a local variable. (ldarga/ldloca)", - CustomClassName("LdLoca"), NoArguments, ResultType("Ref"), HasVariableOperand("Address")), + CustomClassName("LdLoca"), NoArguments, ResultType("new ByReferenceType(variable.Type)", "Ref"), HasVariableOperand("Address")), new OpCode("stloc", "Stores a value into a local variable. (IL: starg/stloc)" + Environment.NewLine + "Evaluates to the value that was stored (for byte/short variables: evaluates to the truncated value, sign/zero extended back to I4 based on variable.Type.GetSign())", CustomClassName("StLoc"), HasVariableOperand("Store", generateCheckInvariant: false), CustomArguments(("value", null)), - ResultType("variable.StackType")), + ResultType("variable.Type", "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"), HasTypeOperand), + CustomClassName("AddressOf"), CustomArguments(("value", null)), ResultType("new ByReferenceType(type)", "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")), + CustomClassName("ThreeValuedBoolAnd"), Binary), 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.", - CustomClassName("ThreeValuedBoolOr"), Binary, ResultType("O")), + CustomClassName("ThreeValuedBoolOr"), Binary), new OpCode("nullable.unwrap", "The input operand must be one of:" + Environment.NewLine + " 1. a nullable value type" + Environment.NewLine + " 2. a reference type" + Environment.NewLine @@ -200,114 +201,132 @@ + "If the input evaluates normally, evaluates to the input value (wrapped in Nullable<T> if the input is a non-nullable value type)." + "If a nullable.unwrap instruction encounters a null input and jumps to the (endpoint of the) nullable.rewrap instruction," + "the nullable.rewrap instruction evaluates to null.", - Unary, CustomComputeFlags), + Unary, CustomComputeFlags, HasTypeOperand), new OpCode("ldstr", "Loads a constant string.", - CustomClassName("LdStr"), LoadConstant("string"), ResultType("O")), + CustomClassName("LdStr"), LoadConstant("string"), ResultType("String", "Obj")), new OpCode("ldstr.utf8", "Loads a constant byte string (as ReadOnlySpan<byte>).", - CustomClassName("LdStrUtf8"), LoadConstant("string"), ResultType("O")), + CustomClassName("LdStrUtf8"), LoadConstant("string"), + ResultType("new ParameterizedType(compilation.FindType(KnownTypeCode.ReadOnlySpanOfT), compilation.FindType(KnownTypeCode.Boolean))", "VT")), new OpCode("ldc.i4", "Loads a constant 32-bit integer.", - LoadConstant("int"), ResultType("I4")), + LoadConstant("int"), ResultType("Int32", "I4")), new OpCode("ldc.i8", "Loads a constant 64-bit integer.", - LoadConstant("long"), ResultType("I8")), + LoadConstant("long"), ResultType("Int64", "I8")), new OpCode("ldc.f4", "Loads a constant 32-bit floating-point number.", - LoadConstant("float"), ResultType("F4")), + LoadConstant("float"), ResultType("Single", "F4")), new OpCode("ldc.f8", "Loads a constant 64-bit floating-point number.", - LoadConstant("double"), ResultType("F8")), + LoadConstant("double"), ResultType("Double", "F8")), new OpCode("ldc.decimal", "Loads a constant decimal.", - LoadConstant("decimal"), ResultType("O")), + LoadConstant("decimal"), ResultType("Decimal", "O")), new OpCode("ldnull", "Loads the null reference.", - CustomClassName("LdNull"), NoArguments, ResultType("O")), + CustomClassName("LdNull"), NoArguments, ResultType("Object", "Obj")), new OpCode("ldftn", "Load method pointer", - CustomClassName("LdFtn"), NoArguments, HasMethodOperand(), ResultType("I")), + CustomClassName("LdFtn"), NoArguments, HasMethodOperand(), ResultType("IntPtr", "I")), new OpCode("ldvirtftn", "Load method pointer", - CustomClassName("LdVirtFtn"), Unary, HasMethodOperand(), MayThrow, ResultType("I")), + CustomClassName("LdVirtFtn"), Unary, HasMethodOperand(), MayThrow, ResultType("IntPtr", "I")), new OpCode("ldvirtdelegate", "Virtual delegate construction", CustomClassName("LdVirtDelegate"), Unary, HasTypeOperand, HasMethodOperand(), - MayThrow, ResultType("O")), + MayThrow, ResultType("this.type", "Obj")), new OpCode("ldtypetoken", "Loads runtime representation of metadata token", - CustomClassName("LdTypeToken"), NoArguments, HasTypeOperand, ResultType("O")), + CustomClassName("LdTypeToken"), NoArguments, HasTypeOperand, ResultType("RuntimeTypeHandle", "VT")), new OpCode("ldmembertoken", "Loads runtime representation of metadata token", - CustomClassName("LdMemberToken"), NoArguments, HasMemberOperand, ResultType("O")), + CustomClassName("LdMemberToken"), NoArguments, HasMemberOperand, + ResultType("compilation.FindType(member is IField ? KnownTypeCode.RuntimeFieldHandle : KnownTypeCode.RuntimeMethodHandle)", "VT")), new OpCode("localloc", "Allocates space in the stack frame", - CustomClassName("LocAlloc"), Unary, ResultType("I"), MayThrow), + ResultType("new PointerType(compilation.FindType(KnownTypeCode.Void))", "I"), + CustomClassName("LocAlloc"), Unary, MayThrow), new OpCode("localloc.span", "Allocates space in the stack frame and wraps it in a Span", - CustomClassName("LocAllocSpan"), Unary, HasTypeOperand, ResultType("O"), MayThrow), + CustomClassName("LocAllocSpan"), Unary, HasTypeOperand, ResultType("this.type", "VT"), MayThrow), new OpCode("cpblk", "memcpy(destAddress, sourceAddress, size);", CustomArguments(("destAddress", new[] { "I", "Ref" }), ("sourceAddress", new[] { "I", "Ref" }), ("size", new[] { "I4" })), MayThrow, MemoryAccess, - SupportsVolatilePrefix, SupportsUnalignedPrefix, ResultType("Void")), + SupportsVolatilePrefix, SupportsUnalignedPrefix, VoidResult), new OpCode("initblk", "memset(address, value, size)", CustomArguments(("address", new[] { "I", "Ref" }), ("value", new[] { "I4" }), ("size", new[] { "I4" })), MayThrow, MemoryAccess, - SupportsVolatilePrefix, SupportsUnalignedPrefix, ResultType("Void")), + SupportsVolatilePrefix, SupportsUnalignedPrefix, VoidResult), new OpCode("ldflda", "Load address of instance field", CustomClassName("LdFlda"), CustomArguments(("target", null)), MayThrowIfNotDelayed, HasFieldOperand, - ResultType("target.ResultType.IsIntegerType() ? StackType.I : StackType.Ref")), + ResultType( + "target.ResultType.IsIntegerType() ? new PointerType(field.Type) : new ByReferenceType(field.Type)", + "target.ResultType.IsIntegerType() ? StackType.I : StackType.Ref")), new OpCode("ldsflda", "Load static field address", - CustomClassName("LdsFlda"), NoArguments, ResultType("Ref"), HasFieldOperand), + CustomClassName("LdsFlda"), NoArguments, ResultType("new ByReferenceType(field.Type)", "Ref"), HasFieldOperand), new OpCode("castclass", "Casts an object to a class.", - CustomClassName("CastClass"), Unary, HasTypeOperand, MayThrow, ResultType("type.GetStackType()")), + CustomClassName("CastClass"), Unary, HasTypeOperand, MayThrow, ResultType("this.type")), new OpCode("isinst", "Test if object is instance of class or interface.", - CustomClassName("IsInst"), Unary, HasTypeOperand, ResultType("O")), + CustomClassName("IsInst"), Unary, HasTypeOperand, ResultType("Object", "Obj")), new OpCode("ldobj", "Indirect load (ref/pointer dereference).", CustomClassName("LdObj"), CustomArguments(("target", new[] { "Ref", "I" })), HasTypeOperand, MemoryAccess, CustomWriteToButKeepOriginal, - SupportsVolatilePrefix, SupportsUnalignedPrefix, MayThrow, ResultType("type.GetStackType()")), - new OpCode("ldobj.if.ref", "If argument is a ref to a reference type, loads the object reference, stores it in a temporary, and evaluates to the address of that temporary (address.of(ldobj(arg))). Otherwise, returns the argument ref as-is.This instruction represents the memory-load semantics of callvirt with a generic type as receiver (where the IL always takes a ref, but only methods on value types expect one, for method on reference types there's an implicit ldobj, which this instruction makes explicit in order to preserve the order-of-evaluation).", + SupportsVolatilePrefix, SupportsUnalignedPrefix, MayThrow, ResultType("this.type")), + new OpCode("ldobj.if.ref", "If argument is a ref to a reference type, loads the object reference, stores it in a temporary, " + + "and evaluates to the address of that temporary (address.of(ldobj(arg))). Otherwise, returns the argument ref as-is." + + "This instruction represents the memory-load semantics of callvirt with a generic type as receiver (where the IL always takes " + + "a ref, but only methods on value types expect one, for method on reference types there's an implicit ldobj, which this "+ + "instruction makes explicit in order to preserve the order-of-evaluation).", CustomClassName("LdObjIfRef"), CustomArguments(("target", new[] { "Ref", "I" })), HasTypeOperand, MemoryAccess, - MayThrow, ResultType("Ref")), + MayThrow, ResultType("new ByReferenceType(this.type)", "Ref")), new OpCode("stobj", "Indirect store (store to ref/pointer)." + Environment.NewLine - + "Evaluates to the value that was stored (when using type byte/short: evaluates to the truncated value, sign/zero extended back to I4 based on type.GetSign())", - CustomClassName("StObj"), CustomArguments(("target", new[] { "Ref", "I" }), ("value", new[] { "type.GetStackType()" })), HasTypeOperand, MemoryAccess, CustomWriteToButKeepOriginal, + + "Evaluates to the value that was stored (when using type byte/short: evaluates to the truncated value, " + + "sign/zero extended back to I4 based on type.GetSign())", + CustomClassName("StObj"), + CustomArguments(("target", new[] { "Ref", "I" }), ("value", new[] { "type.GetStackType()" })), + HasTypeOperand, MemoryAccess, CustomWriteToButKeepOriginal, SupportsVolatilePrefix, SupportsUnalignedPrefix, MayThrow, - ResultType("UnalignedPrefix == 0 ? type.GetStackType() : StackType.Void"), + ResultType("UnalignedPrefix == 0 ? type : compilation.FindType(KnownTypeCode.Void)", + "UnalignedPrefix == 0 ? type.GetStackType() : StackType.Void"), CustomInvariant("CheckTargetSlot();")), new OpCode("box", "Boxes a value.", - Unary, HasTypeOperand, ResultType("O")), + Unary, HasTypeOperand, ResultType("Object", "Obj")), new OpCode("unbox", "Compute address inside box.", - Unary, HasTypeOperand, MayThrow, ResultType("Ref")), + Unary, HasTypeOperand, MayThrow, ResultType("new ByReferenceType(this.type)", "Ref")), new OpCode("unbox.any", "Unbox a value.", - Unary, HasTypeOperand, MemoryAccess, MayThrow, ResultType("type.GetStackType()")), + Unary, HasTypeOperand, MemoryAccess, MayThrow, ResultType("this.type")), new OpCode("newobj", "Creates an object instance and calls the constructor.", - CustomClassName("NewObj"), Call, ResultType("Method.DeclaringType.GetStackType()")), + CustomClassName("NewObj"), Call, ResultType("Method.DeclaringType")), new OpCode("newarr", "Creates an array instance.", - CustomClassName("NewArr"), HasTypeOperand, CustomChildren(new [] { new ArgumentInfo("indices") { IsCollection = true } }, true), MayThrow, ResultType("O")), + CustomClassName("NewArr"), HasTypeOperand, + CustomChildren(new [] { new ArgumentInfo("indices") { IsCollection = true } }, true), + MayThrow, ResultType("new ArrayType(compilation, this.Type, this.Indices.Count)", "Obj")), new OpCode("default.value", "Returns the default value for a type.", - NoArguments, HasTypeOperand, ResultType("type.GetStackType()")), + NoArguments, HasTypeOperand, ResultType("this.type")), new OpCode("throw", "Throws an exception.", - Unary, MayThrow, HasFlag("InstructionFlags.EndPointUnreachable"), ResultType("this.resultType")), + Unary, MayThrow, VoidResult, HasFlag("InstructionFlags.EndPointUnreachable")), new OpCode("rethrow", "Rethrows the current exception.", NoArguments, MayThrow, UnconditionalBranch), new OpCode("sizeof", "Gets the size of a type in bytes.", - CustomClassName("SizeOf"), NoArguments, HasTypeOperand, ResultType("I4")), + CustomClassName("SizeOf"), NoArguments, HasTypeOperand, ResultType("Int32", "I4")), new OpCode("ldlen", "Returns the length of an array as 'native unsigned int'.", CustomClassName("LdLen"), CustomArguments(("array", new[] { "O" })), CustomConstructor, CustomWriteTo, MayThrow), new OpCode("ldelema", "Load address of array element.", CustomClassName("LdElema"), HasTypeOperand, CustomChildren(new [] { new ArgumentInfo("array"), new ArgumentInfo("indices") { IsCollection = true } }, true), BoolFlag("WithSystemIndex"), - MayThrowIfNotDelayed, ResultType("Ref"), SupportsReadonlyPrefix), + MayThrowIfNotDelayed, ResultType("new ByReferenceType(this.Type)", "Ref"), SupportsReadonlyPrefix), new OpCode("ldelema.inlinearray", "Load address of inline array element.", CustomClassName("LdElemaInlineArray"), HasTypeOperand, CustomChildren(new [] { new ArgumentInfo("array"), new ArgumentInfo("indices") { IsCollection = true } }, true), - MayThrow, ResultType("Ref"), SupportsReadonlyPrefix), + MayThrow, ResultType("new ByReferenceType(this.Type)", "Ref"), SupportsReadonlyPrefix), new OpCode("get.pinnable.reference", "Retrieves a pinnable reference for the input object." + Environment.NewLine + "The input must be an object reference (O)." + Environment.NewLine + "If the input is an array/string, evaluates to a reference to the first element/character, or to a null reference if the array is null or empty." + Environment.NewLine + "Otherwise, uses the GetPinnableReference method to get the reference, or evaluates to a null reference if the input is null." + Environment.NewLine, - CustomArguments(("argument", new[] { "O" })), ResultType("Ref"), HasMethodOperand(nullable: true)), + CustomArguments(("argument", new[] { "O" })), + HasTypeOperand, + ResultType("new ByReferenceType(this.Type)", "Ref"), HasMethodOperand(nullable: true)), new OpCode("string.to.int", "Maps a string value to an integer. This is used in switch(string).", - CustomArguments(("argument", new[] { "O" })), CustomConstructor, CustomWriteTo, ResultType("I4")), + CustomArguments(("argument", new[] { "O" })), CustomConstructor, CustomWriteTo, ResultType("Int32", "I4")), new OpCode("expression.tree.cast", "ILAst representation of Expression.Convert.", - CustomClassName("ExpressionTreeCast"), Unary, HasTypeOperand, MayThrow, CustomConstructor, CustomWriteTo, ResultType("type.GetStackType()"), + CustomClassName("ExpressionTreeCast"), Unary, HasTypeOperand, MayThrow, CustomConstructor, CustomWriteTo, + ResultType("this.type"), MatchCondition("this.IsChecked == o.IsChecked")), new OpCode("user.logic.operator", "Use of user-defined && or || operator.", CustomClassName("UserDefinedLogicOperator"), - HasMethodOperand(), ResultType("O"), + HasMethodOperand(), ResultType("method.ReturnType"), CustomChildren(new []{ new ChildInfo("left") { CanInlineInto = true }, new ChildInfo("right") { CanInlineInto = false } // only executed depending on value of left @@ -352,14 +371,15 @@ CustomChildren(new []{ new ChildInfo("testedOperand") { CanInlineInto = true }, new ChildInfo("subPatterns") { IsCollection = true } - }), ResultType("I4"), CustomWriteTo, SideEffect, MayThrow, ControlFlow, CustomInvariant("AdditionalInvariants();")), + }), ResultType("Boolean", "I4"), CustomWriteTo, SideEffect, MayThrow, ControlFlow, CustomInvariant("AdditionalInvariants();")), new OpCode("mkrefany", "Push a typed reference of type class onto the stack.", - CustomClassName("MakeRefAny"), Unary, HasTypeOperand, ResultType("O")), + CustomClassName("MakeRefAny"), Unary, HasTypeOperand, ResultType("TypedReference", "VT")), new OpCode("refanytype", "Push the type token stored in a typed reference.", - CustomClassName("RefAnyType"), Unary, ResultType("O")), + CustomClassName("RefAnyType"), Unary, ResultType("RuntimeTypeHandle", "VT")), new OpCode("refanyval", "Push the address stored in a typed reference.", - CustomClassName("RefAnyValue"), Unary, HasTypeOperand, MayThrow, ResultType("Ref")), + CustomClassName("RefAnyValue"), Unary, HasTypeOperand, MayThrow, + ResultType("new ByReferenceType(type)", "Ref")), new OpCode("yield.return", "Yield an element from an iterator.", MayBranch, // yield return may end up returning if the consumer disposes the iterator @@ -368,10 +388,10 @@ // note: "yield break" is always represented using a "leave" instruction new OpCode("await", "C# await operator.", SideEffect, // other code can run with arbitrary side effects while we're waiting - CustomArguments(("value", null)), ResultType("GetResultMethod?.ReturnType.GetStackType() ?? StackType.Unknown")), + CustomArguments(("value", null)), ResultType("GetResultMethod?.ReturnType ?? SpecialType.UnknownType")), new OpCode("deconstruct", "Deconstruction statement", - CustomClassName("DeconstructInstruction"), CustomConstructor, ResultType("Void"), CustomWriteTo), + CustomClassName("DeconstructInstruction"), CustomConstructor, VoidResult, CustomWriteTo), new OpCode("deconstruct.result", "Represents a deconstructed value", CustomClassName("DeconstructResultInstruction"), CustomConstructor, CustomInvariant("AdditionalInvariants();"), Unary, CustomWriteTo), @@ -715,24 +735,28 @@ namespace ICSharpCode.Decompiler.IL } // ResultType trait: the instruction has the specified result type. - static Action ResultType(string type) + static Action ResultType(string type, string stackType = null) { - if (!type.Contains(".")) - type = "StackType." + type; + if (!type.Contains(".") && !type.Contains("(")) + type = "compilation.FindType(KnownTypeCode." + type + ")"; + if (stackType != null && !stackType.Contains(".")) + stackType = "StackType." + stackType; + stackType ??= "(" + type + ").GetStackType()"; return opCode => { - opCode.Members.Add("public override StackType ResultType { get { return " + type + "; } }"); + opCode.Members.Add("public override StackType ResultType => " + stackType + ";"); + opCode.Members.Add("public override IType InferType(ICompilation compilation) => " + type + ";"); }; } // VoidResult trait: the instruction has no result and is not usable as an argument - static Action VoidResult = ResultType("Void"); + static Action VoidResult = ResultType("Void", "Void"); // ResultTypeParam trait: the instruction takes its result type as ctor parameter static Action ResultTypeParam = opCode => { opCode.ConstructorParameters.Add("StackType resultType"); opCode.ConstructorBody.Add("this.resultType = resultType;"); opCode.Members.Add("StackType resultType;"); - opCode.Members.Add("public override StackType ResultType { get { return resultType; } }"); + opCode.Members.Add("public override StackType ResultType => resultType;"); }; // MayThrow trait: the instruction may throw exceptions diff --git a/ICSharpCode.Decompiler/IL/Instructions/BinaryNumericInstruction.cs b/ICSharpCode.Decompiler/IL/Instructions/BinaryNumericInstruction.cs index 5100e6894..dd280f2ad 100644 --- a/ICSharpCode.Decompiler/IL/Instructions/BinaryNumericInstruction.cs +++ b/ICSharpCode.Decompiler/IL/Instructions/BinaryNumericInstruction.cs @@ -129,6 +129,14 @@ namespace ICSharpCode.Decompiler.IL get => IsLifted ? StackType.O : resultType; } + public override IType InferType(ICompilation compilation) + { + IType type = compilation.FindType(UnderlyingResultType); + if (IsLifted) + return NullableType.Create(compilation, type); + return type; + } + internal override void CheckInvariant(ILPhase phase, ICompilation compilation) { base.CheckInvariant(phase, compilation); @@ -142,14 +150,14 @@ namespace ICSharpCode.Decompiler.IL protected override InstructionFlags ComputeFlags() { var flags = base.ComputeFlags(); - if (CheckForOverflow || (Operator == BinaryNumericOperator.Div || Operator == BinaryNumericOperator.Rem)) + if (CheckForOverflow || Operator == BinaryNumericOperator.Div || Operator == BinaryNumericOperator.Rem) flags |= InstructionFlags.MayThrow; return flags; } public override InstructionFlags DirectFlags { get { - if (CheckForOverflow || (Operator == BinaryNumericOperator.Div || Operator == BinaryNumericOperator.Rem)) + if (CheckForOverflow || Operator == BinaryNumericOperator.Div || Operator == BinaryNumericOperator.Rem) return base.DirectFlags | InstructionFlags.MayThrow; return base.DirectFlags; } diff --git a/ICSharpCode.Decompiler/IL/Instructions/Block.cs b/ICSharpCode.Decompiler/IL/Instructions/Block.cs index 43b76c78c..b9ba2227f 100644 --- a/ICSharpCode.Decompiler/IL/Instructions/Block.cs +++ b/ICSharpCode.Decompiler/IL/Instructions/Block.cs @@ -235,6 +235,11 @@ namespace ICSharpCode.Decompiler.IL } } + public override IType InferType(ICompilation compilation) + { + return finalInstruction.InferType(compilation); + } + internal override bool CanInlineIntoSlot(int childIndex, ILInstruction expressionBeingMoved) { switch (Kind) diff --git a/ICSharpCode.Decompiler/IL/Instructions/BlockContainer.cs b/ICSharpCode.Decompiler/IL/Instructions/BlockContainer.cs index 6a929d93c..fb678b038 100644 --- a/ICSharpCode.Decompiler/IL/Instructions/BlockContainer.cs +++ b/ICSharpCode.Decompiler/IL/Instructions/BlockContainer.cs @@ -44,7 +44,11 @@ namespace ICSharpCode.Decompiler.IL public readonly InstructionCollection Blocks; public ContainerKind Kind { get; set; } - public StackType ExpectedResultType { get; set; } + IType? expectedResultType; // null means void + public IType? ExpectedResultType { + get => expectedResultType; + set => expectedResultType = value; + } int leaveCount; @@ -80,16 +84,16 @@ namespace ICSharpCode.Decompiler.IL } } - public BlockContainer(ContainerKind kind = ContainerKind.Normal, StackType expectedResultType = StackType.Void) : base(OpCode.BlockContainer) + public BlockContainer(ContainerKind kind = ContainerKind.Normal, IType? expectedResultType = null) : base(OpCode.BlockContainer) { this.Kind = kind; this.Blocks = new InstructionCollection(this, 0); - this.ExpectedResultType = expectedResultType; + this.expectedResultType = expectedResultType; } public override ILInstruction Clone() { - BlockContainer clone = new BlockContainer(this.Kind, this.ExpectedResultType); + BlockContainer clone = new BlockContainer(this.Kind, this.expectedResultType); clone.AddILRange(this); clone.Blocks.AddRange(this.Blocks.Select(block => (Block)block.Clone())); // Adjust branch instructions to point to the new container @@ -236,6 +240,20 @@ namespace ICSharpCode.Decompiler.IL } } + public override StackType ResultType { + get { + if (expectedResultType != null) + return expectedResultType.GetStackType(); + else + return StackType.Void; + } + } + + public override IType InferType(ICompilation compilation) + { + return expectedResultType ?? compilation.FindType(StackType.Void); + } + protected override InstructionFlags ComputeFlags() { InstructionFlags flags = InstructionFlags.ControlFlow; diff --git a/ICSharpCode.Decompiler/IL/Instructions/CallIndirect.cs b/ICSharpCode.Decompiler/IL/Instructions/CallIndirect.cs index a3781c8e6..f77fff566 100644 --- a/ICSharpCode.Decompiler/IL/Instructions/CallIndirect.cs +++ b/ICSharpCode.Decompiler/IL/Instructions/CallIndirect.cs @@ -67,6 +67,7 @@ namespace ICSharpCode.Decompiler.IL } public override StackType ResultType => FunctionPointerType.ReturnType.GetStackType(); + public override IType InferType(ICompilation compilation) => FunctionPointerType.ReturnType; internal override void CheckInvariant(ILPhase phase, ICompilation compilation) { diff --git a/ICSharpCode.Decompiler/IL/Instructions/CallInstruction.cs b/ICSharpCode.Decompiler/IL/Instructions/CallInstruction.cs index 47558a743..d260cbe12 100644 --- a/ICSharpCode.Decompiler/IL/Instructions/CallInstruction.cs +++ b/ICSharpCode.Decompiler/IL/Instructions/CallInstruction.cs @@ -87,14 +87,9 @@ namespace ICSharpCode.Decompiler.IL return Method.Parameters[argumentIndex - firstParamIndex]; } - public override StackType ResultType { - get { - if (OpCode == OpCode.NewObj) - return Method.DeclaringType.GetStackType(); - else - return Method.ReturnType.GetStackType(); - } - } + // Note: NewObj is overriding ResultType+InferType. + public override StackType ResultType => Method.ReturnType.GetStackType(); + public override IType InferType(ICompilation compilation) => Method.ReturnType; /// /// Gets the expected stack type for passing the this pointer in a method call. diff --git a/ICSharpCode.Decompiler/IL/Instructions/Comp.cs b/ICSharpCode.Decompiler/IL/Instructions/Comp.cs index ea320301e..da3c5ef09 100644 --- a/ICSharpCode.Decompiler/IL/Instructions/Comp.cs +++ b/ICSharpCode.Decompiler/IL/Instructions/Comp.cs @@ -161,6 +161,20 @@ namespace ICSharpCode.Decompiler.IL } public override StackType ResultType => LiftingKind == ComparisonLiftingKind.ThreeValuedLogic ? StackType.O : StackType.I4; + + public override IType InferType(ICompilation compilation) + { + IType boolType = compilation.FindType(KnownTypeCode.Boolean); + if (LiftingKind == ComparisonLiftingKind.ThreeValuedLogic) + { + return NullableType.Create(compilation, boolType); + } + else + { + return boolType; + } + } + public bool IsLifted => LiftingKind != ComparisonLiftingKind.None; public StackType UnderlyingResultType => StackType.I4; diff --git a/ICSharpCode.Decompiler/IL/Instructions/CompoundAssignmentInstruction.cs b/ICSharpCode.Decompiler/IL/Instructions/CompoundAssignmentInstruction.cs index 634ecf832..41c7158f4 100644 --- a/ICSharpCode.Decompiler/IL/Instructions/CompoundAssignmentInstruction.cs +++ b/ICSharpCode.Decompiler/IL/Instructions/CompoundAssignmentInstruction.cs @@ -336,6 +336,7 @@ namespace ICSharpCode.Decompiler.IL } public override StackType ResultType => Method.ReturnType.GetStackType(); + public override IType InferType(ICompilation compilation) => Method.ReturnType; protected override void WriteToCore(ITextOutput output, ILAstWritingOptions options) { diff --git a/ICSharpCode.Decompiler/IL/Instructions/Conv.cs b/ICSharpCode.Decompiler/IL/Instructions/Conv.cs index 52f968f1a..bce6575f3 100644 --- a/ICSharpCode.Decompiler/IL/Instructions/Conv.cs +++ b/ICSharpCode.Decompiler/IL/Instructions/Conv.cs @@ -307,7 +307,25 @@ namespace ICSharpCode.Decompiler.IL } public override StackType ResultType { - get => IsLifted ? StackType.O : TargetType.GetStackType(); + get => IsLifted ? StackType.VT : TargetType.GetStackType(); + } + + public override IType InferType(ICompilation compilation) + { + var ktc = TargetType.ToKnownTypeCode(); + IType type; + if (ktc != KnownTypeCode.None) + { + type = compilation.FindType(ktc); + } + else + { + type = compilation.FindType(TargetType.GetStackType()); + } + if (IsLifted) + return NullableType.Create(compilation, type); + else + return type; } public StackType UnderlyingResultType { diff --git a/ICSharpCode.Decompiler/IL/Instructions/DeconstructResultInstruction.cs b/ICSharpCode.Decompiler/IL/Instructions/DeconstructResultInstruction.cs index 608aa4483..6d1582be9 100644 --- a/ICSharpCode.Decompiler/IL/Instructions/DeconstructResultInstruction.cs +++ b/ICSharpCode.Decompiler/IL/Instructions/DeconstructResultInstruction.cs @@ -28,14 +28,18 @@ namespace ICSharpCode.Decompiler.IL public int Index { get; } public override StackType ResultType { get; } + public IType Type { get; } - public DeconstructResultInstruction(int index, StackType resultType, ILInstruction argument) + public DeconstructResultInstruction(int index, IType type, StackType resultType, ILInstruction argument) : base(OpCode.DeconstructResultInstruction, argument) { Debug.Assert(index >= 0); Index = index; + Type = type; ResultType = resultType; + Debug.Assert(type.GetStackType() == resultType); } + public override IType InferType(ICompilation compilation) => Type; protected override void WriteToCore(ITextOutput output, ILAstWritingOptions options) { diff --git a/ICSharpCode.Decompiler/IL/Instructions/DynamicInstructions.cs b/ICSharpCode.Decompiler/IL/Instructions/DynamicInstructions.cs index 2d938d72e..22a4b19d7 100644 --- a/ICSharpCode.Decompiler/IL/Instructions/DynamicInstructions.cs +++ b/ICSharpCode.Decompiler/IL/Instructions/DynamicInstructions.cs @@ -156,6 +156,8 @@ namespace ICSharpCode.Decompiler.IL public override StackType ResultType => type.GetStackType(); + public override IType InferType(ICompilation compilation) => type; + public bool IsChecked => (BinderFlags & CSharpBinderFlags.CheckedContext) != 0; public bool IsExplicit => (BinderFlags & CSharpBinderFlags.ConvertExplicit) != 0; @@ -223,6 +225,8 @@ namespace ICSharpCode.Decompiler.IL public override StackType ResultType => StackType.O; + public override IType InferType(ICompilation compilation) => SpecialType.Dynamic; + public override CSharpArgumentInfo GetArgumentInfoOfChild(int index) { index += ArgumentInfoOffset; @@ -256,6 +260,7 @@ namespace ICSharpCode.Decompiler.IL } public override StackType ResultType => StackType.O; + public override IType InferType(ICompilation compilation) => SpecialType.Dynamic; public override CSharpArgumentInfo GetArgumentInfoOfChild(int index) { @@ -292,6 +297,7 @@ namespace ICSharpCode.Decompiler.IL } public override StackType ResultType => StackType.O; + public override IType InferType(ICompilation compilation) => SpecialType.Dynamic; public override CSharpArgumentInfo GetArgumentInfoOfChild(int index) { @@ -330,6 +336,7 @@ namespace ICSharpCode.Decompiler.IL } public override StackType ResultType => StackType.O; + public override IType InferType(ICompilation compilation) => SpecialType.Dynamic; public override CSharpArgumentInfo GetArgumentInfoOfChild(int index) { @@ -362,6 +369,7 @@ namespace ICSharpCode.Decompiler.IL } public override StackType ResultType => StackType.O; + public override IType InferType(ICompilation compilation) => SpecialType.Dynamic; public override CSharpArgumentInfo GetArgumentInfoOfChild(int index) { @@ -402,6 +410,7 @@ namespace ICSharpCode.Decompiler.IL } public override StackType ResultType => Type.GetStackType(); + public override IType InferType(ICompilation compilation) => Type; public override CSharpArgumentInfo GetArgumentInfoOfChild(int index) { @@ -440,6 +449,7 @@ namespace ICSharpCode.Decompiler.IL } public override StackType ResultType => StackType.O; + public override IType InferType(ICompilation compilation) => SpecialType.Dynamic; public override CSharpArgumentInfo GetArgumentInfoOfChild(int index) { @@ -482,6 +492,7 @@ namespace ICSharpCode.Decompiler.IL } public override StackType ResultType => StackType.O; + public override IType InferType(ICompilation compilation) => SpecialType.Dynamic; protected override InstructionFlags ComputeFlags() { @@ -541,6 +552,18 @@ namespace ICSharpCode.Decompiler.IL } } + public override IType InferType(ICompilation compilation) + { + switch (Operation) + { + case ExpressionType.IsFalse: + case ExpressionType.IsTrue: + return compilation.FindType(KnownTypeCode.Boolean); + default: + return SpecialType.Dynamic; + } + } + public override CSharpArgumentInfo GetArgumentInfoOfChild(int index) { switch (index) @@ -575,6 +598,7 @@ namespace ICSharpCode.Decompiler.IL } public override StackType ResultType => StackType.O; + public override IType InferType(ICompilation compilation) => SpecialType.Dynamic; public override CSharpArgumentInfo GetArgumentInfoOfChild(int index) { @@ -608,6 +632,8 @@ namespace ICSharpCode.Decompiler.IL public override StackType ResultType => StackType.I4; + public override IType InferType(ICompilation compilation) => compilation.FindType(KnownTypeCode.Boolean); + public override CSharpArgumentInfo GetArgumentInfoOfChild(int index) { return default(CSharpArgumentInfo); diff --git a/ICSharpCode.Decompiler/IL/Instructions/ILInstruction.cs b/ICSharpCode.Decompiler/IL/Instructions/ILInstruction.cs index 1b4d135f7..7fdca2a9b 100644 --- a/ICSharpCode.Decompiler/IL/Instructions/ILInstruction.cs +++ b/ICSharpCode.Decompiler/IL/Instructions/ILInstruction.cs @@ -102,6 +102,8 @@ namespace ICSharpCode.Decompiler.IL child.CheckInvariant(phase, compilation); } Debug.Assert((this.DirectFlags & ~this.Flags) == 0, "All DirectFlags must also appear in this.Flags"); + var inferredType = this.InferType(compilation); + Debug.Assert(inferredType.GetStackType() == this.ResultType); } /// @@ -216,19 +218,26 @@ namespace ICSharpCode.Decompiler.IL /// public abstract StackType ResultType { get; } - /* Not sure if it's a good idea to offer this on all instructions -- - * e.g. ldloc for a local of type `int?` would return StackType.O (because it's not a lifted operation), - * even though the underlying type is int = StackType.I4. /// - /// Gets the underlying result type of the value produced by this instruction. + /// Gets a possible C# type that could be used to store the result of this instruction. + /// + /// + /// Post-condition: this.InferType().GetStackType() == this.ResultType. /// - /// If this is a lifted operation, the ResultType will be `StackType.O` (because Nullable{T} is a struct), - /// and UnderlyingResultType will be result type of the corresponding non-lifted operation. + /// This must be a type suitable for use a local variable (i.e. `ldnull` uses `object`, not `NullType`). /// - /// If this is not a lifted operation, the underlying result type is equal to the result type. - /// - public virtual StackType UnderlyingResultType { get => ResultType; } - */ + /// If this function returns a small integer type, the instruction is guaranteed to + /// evaluate to a value that fits into that type (when the I4 evaluation result is + /// interpreted as int/uint depending on the small integer type's sign). + /// + /// For instructions producing an non-nullable value type, this function must return that + /// exact type. + /// For nullable value types, this function may return `int?` when actually the instruction + /// produces a nullable I4, which might end up being a `bool?` expression in C#. + /// Similarly, for reference types, this function may return `object` when actually the C# + /// type is a more specific reference type. + /// + public abstract IType InferType(ICompilation compilation); internal static StackType CommonResultType(StackType a, StackType b) { diff --git a/ICSharpCode.Decompiler/IL/Instructions/IfInstruction.cs b/ICSharpCode.Decompiler/IL/Instructions/IfInstruction.cs index 985317cc4..19d4930c4 100644 --- a/ICSharpCode.Decompiler/IL/Instructions/IfInstruction.cs +++ b/ICSharpCode.Decompiler/IL/Instructions/IfInstruction.cs @@ -38,46 +38,60 @@ namespace ICSharpCode.Decompiler.IL /// partial class IfInstruction : ILInstruction { - public IfInstruction(ILInstruction condition, ILInstruction trueInst, ILInstruction? falseInst = null) : base(OpCode.IfInstruction) + // null means void + readonly IType? resultType; + + public IfInstruction(ILInstruction condition, ILInstruction trueInst, + ILInstruction? falseInst = null, IType? resultType = null) : base(OpCode.IfInstruction) { this.Condition = condition; this.TrueInst = trueInst; - this.FalseInst = falseInst ?? new Nop(); + falseInst ??= new Nop(); + this.FalseInst = falseInst; + this.resultType = resultType; + Debug.Assert(condition.ResultType == StackType.I4); + Debug.Assert(trueInst.ResultType == this.ResultType + || trueInst.HasDirectFlag(InstructionFlags.EndPointUnreachable)); + Debug.Assert(falseInst.ResultType == this.ResultType + || falseInst.HasDirectFlag(InstructionFlags.EndPointUnreachable)); } - public static IfInstruction LogicAnd(ILInstruction lhs, ILInstruction rhs) + public static IfInstruction LogicAnd(ILInstruction lhs, ILInstruction rhs, ICompilation compilation) { - return new IfInstruction(lhs, rhs, new LdcI4(0)); + Debug.Assert(lhs.ResultType == StackType.I4); + Debug.Assert(rhs.ResultType == StackType.I4); + return new IfInstruction(lhs, rhs, new LdcI4(0), rhs.InferType(compilation)); } - public static IfInstruction LogicOr(ILInstruction lhs, ILInstruction? rhs) + public static IfInstruction LogicOr(ILInstruction lhs, ILInstruction rhs, ICompilation compilation) { - return new IfInstruction(lhs, new LdcI4(1), rhs); + return new IfInstruction(lhs, new LdcI4(1), rhs, rhs.InferType(compilation)); } internal override void CheckInvariant(ILPhase phase, ICompilation compilation) { base.CheckInvariant(phase, compilation); Debug.Assert(condition.ResultType == StackType.I4); - Debug.Assert(trueInst.ResultType == falseInst.ResultType - || trueInst.HasDirectFlag(InstructionFlags.EndPointUnreachable) + Debug.Assert(trueInst.ResultType == this.ResultType + || trueInst.HasDirectFlag(InstructionFlags.EndPointUnreachable)); + Debug.Assert(falseInst.ResultType == this.ResultType || falseInst.HasDirectFlag(InstructionFlags.EndPointUnreachable)); } public override StackType ResultType { get { - if (trueInst.HasDirectFlag(InstructionFlags.EndPointUnreachable)) - return falseInst.ResultType; - else - return trueInst.ResultType; + if (resultType != null) + return resultType.GetStackType(); + return StackType.Void; } } - - public override InstructionFlags DirectFlags { - get { - return InstructionFlags.ControlFlow; - } + public override IType InferType(ICompilation compilation) + { + if (resultType != null) + return resultType; + return compilation.FindType(KnownTypeCode.Void); } + public override InstructionFlags DirectFlags => InstructionFlags.ControlFlow; protected override InstructionFlags ComputeFlags() { diff --git a/ICSharpCode.Decompiler/IL/Instructions/LdLen.cs b/ICSharpCode.Decompiler/IL/Instructions/LdLen.cs index 7a3603d5c..73920c0d3 100644 --- a/ICSharpCode.Decompiler/IL/Instructions/LdLen.cs +++ b/ICSharpCode.Decompiler/IL/Instructions/LdLen.cs @@ -19,11 +19,10 @@ using System.Diagnostics; +using ICSharpCode.Decompiler.TypeSystem; + namespace ICSharpCode.Decompiler.IL { - /// - /// Description of LdLen. - /// public sealed partial class LdLen { readonly StackType resultType; @@ -39,6 +38,11 @@ namespace ICSharpCode.Decompiler.IL get { return resultType; } } + public override IType InferType(ICompilation compilation) + { + return compilation.FindType(resultType); + } + protected override void WriteToCore(ITextOutput output, ILAstWritingOptions options) { WriteILRange(output, options); diff --git a/ICSharpCode.Decompiler/IL/Instructions/LogicInstructions.cs b/ICSharpCode.Decompiler/IL/Instructions/LogicInstructions.cs index 822309767..721418769 100644 --- a/ICSharpCode.Decompiler/IL/Instructions/LogicInstructions.cs +++ b/ICSharpCode.Decompiler/IL/Instructions/LogicInstructions.cs @@ -31,6 +31,9 @@ namespace ICSharpCode.Decompiler.IL bool ILiftableInstruction.IsLifted => true; StackType ILiftableInstruction.UnderlyingResultType => StackType.I4; + public override StackType ResultType => StackType.O; + public override IType InferType(ICompilation compilation) => NullableType.Create(compilation, compilation.FindType(KnownTypeCode.Boolean)); + internal override void CheckInvariant(ILPhase phase, ICompilation compilation) { base.CheckInvariant(phase, compilation); @@ -43,6 +46,9 @@ namespace ICSharpCode.Decompiler.IL bool ILiftableInstruction.IsLifted => true; StackType ILiftableInstruction.UnderlyingResultType => StackType.I4; + public override StackType ResultType => StackType.O; + public override IType InferType(ICompilation compilation) => NullableType.Create(compilation, compilation.FindType(KnownTypeCode.Boolean)); + internal override void CheckInvariant(ILPhase phase, ICompilation compilation) { base.CheckInvariant(phase, compilation); diff --git a/ICSharpCode.Decompiler/IL/Instructions/NullCoalescingInstruction.cs b/ICSharpCode.Decompiler/IL/Instructions/NullCoalescingInstruction.cs index 9820a0f7d..15eabbdfd 100644 --- a/ICSharpCode.Decompiler/IL/Instructions/NullCoalescingInstruction.cs +++ b/ICSharpCode.Decompiler/IL/Instructions/NullCoalescingInstruction.cs @@ -56,13 +56,16 @@ namespace ICSharpCode.Decompiler.IL partial class NullCoalescingInstruction { public readonly NullCoalescingKind Kind; + public IType Type { get; } public StackType UnderlyingResultType = StackType.O; - public NullCoalescingInstruction(NullCoalescingKind kind, ILInstruction valueInst, ILInstruction fallbackInst) : base(OpCode.NullCoalescingInstruction) + public NullCoalescingInstruction(IType type, NullCoalescingKind kind, ILInstruction valueInst, ILInstruction fallbackInst) : base(OpCode.NullCoalescingInstruction) { + this.Type = type; this.Kind = kind; this.ValueInst = valueInst; this.FallbackInst = fallbackInst; + Debug.Assert(type.GetStackType() == fallbackInst.ResultType); } internal override void CheckInvariant(ILPhase phase, ICompilation compilation) @@ -78,6 +81,7 @@ namespace ICSharpCode.Decompiler.IL return fallbackInst.ResultType; } } + public override IType InferType(ICompilation compilation) => Type; public override InstructionFlags DirectFlags { get { diff --git a/ICSharpCode.Decompiler/IL/Instructions/NullableInstructions.cs b/ICSharpCode.Decompiler/IL/Instructions/NullableInstructions.cs index 6e828ce22..45c30f9ca 100644 --- a/ICSharpCode.Decompiler/IL/Instructions/NullableInstructions.cs +++ b/ICSharpCode.Decompiler/IL/Instructions/NullableInstructions.cs @@ -65,10 +65,12 @@ namespace ICSharpCode.Decompiler.IL /// RefOutput can only be used if RefInput is also used. /// public bool RefOutput { get => ResultType == StackType.Ref; } + public IType Type { get; } - public NullableUnwrap(StackType unwrappedType, ILInstruction argument, bool refInput = false) + public NullableUnwrap(IType type, StackType unwrappedType, ILInstruction argument, bool refInput = false) : base(OpCode.NullableUnwrap, argument) { + this.Type = type; this.ResultType = unwrappedType; this.RefInput = refInput; if (unwrappedType == StackType.Ref) @@ -105,6 +107,7 @@ namespace ICSharpCode.Decompiler.IL } public override StackType ResultType { get; } + public override IType InferType(ICompilation compilation) => Type; } partial class NullableRewrap @@ -131,9 +134,10 @@ namespace ICSharpCode.Decompiler.IL if (Argument.ResultType == StackType.Void) return StackType.Void; else - return StackType.O; + return StackType.VT; } } + public override IType InferType(ICompilation compilation) => Type; internal override bool PrepareExtract(int childIndex, ExtractionContext ctx) { diff --git a/ICSharpCode.Decompiler/IL/Instructions/SimpleInstruction.cs b/ICSharpCode.Decompiler/IL/Instructions/SimpleInstruction.cs index 661c999fa..1f75a0ded 100644 --- a/ICSharpCode.Decompiler/IL/Instructions/SimpleInstruction.cs +++ b/ICSharpCode.Decompiler/IL/Instructions/SimpleInstruction.cs @@ -17,6 +17,8 @@ // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER // DEALINGS IN THE SOFTWARE. +using ICSharpCode.Decompiler.TypeSystem; + namespace ICSharpCode.Decompiler.IL { /// @@ -73,6 +75,11 @@ namespace ICSharpCode.Decompiler.IL get { return ExpectedResultType; } } + public override IType InferType(ICompilation compilation) + { + return compilation.FindType(ExpectedResultType); + } + protected override void WriteToCore(ITextOutput output, ILAstWritingOptions options) { WriteILRange(output, options); @@ -101,6 +108,11 @@ namespace ICSharpCode.Decompiler.IL get { return ExpectedResultType; } } + public override IType InferType(ICompilation compilation) + { + return compilation.FindType(ExpectedResultType); + } + protected override void WriteToCore(ITextOutput output, ILAstWritingOptions options) { WriteILRange(output, options); diff --git a/ICSharpCode.Decompiler/IL/Instructions/SwitchInstruction.cs b/ICSharpCode.Decompiler/IL/Instructions/SwitchInstruction.cs index 2ea684a36..b8e03487a 100644 --- a/ICSharpCode.Decompiler/IL/Instructions/SwitchInstruction.cs +++ b/ICSharpCode.Decompiler/IL/Instructions/SwitchInstruction.cs @@ -143,11 +143,12 @@ namespace ICSharpCode.Decompiler.IL return clone; } - StackType resultType = StackType.Void; + IType? resultType = null; - public override StackType ResultType => resultType; + public override StackType ResultType => resultType?.GetStackType() ?? StackType.Void; + public override IType InferType(ICompilation compilation) => resultType ?? compilation.FindType(KnownTypeCode.Void); - public void SetResultType(StackType resultType) + public void SetResultType(IType resultType) { this.resultType = resultType; } diff --git a/ICSharpCode.Decompiler/IL/Instructions/TryInstruction.cs b/ICSharpCode.Decompiler/IL/Instructions/TryInstruction.cs index be55739ba..69facffca 100644 --- a/ICSharpCode.Decompiler/IL/Instructions/TryInstruction.cs +++ b/ICSharpCode.Decompiler/IL/Instructions/TryInstruction.cs @@ -35,6 +35,9 @@ namespace ICSharpCode.Decompiler.IL this.TryBlock = tryBlock; } + public sealed override StackType ResultType => StackType.Void; + public sealed override IType InferType(ICompilation compilation) => compilation.FindType(KnownTypeCode.Void); + ILInstruction tryBlock = null!; public ILInstruction TryBlock { get { return this.tryBlock; } @@ -81,10 +84,6 @@ namespace ICSharpCode.Decompiler.IL } } - public override StackType ResultType { - get { return StackType.Void; } - } - protected override InstructionFlags ComputeFlags() { var flags = TryBlock.Flags; @@ -149,10 +148,6 @@ namespace ICSharpCode.Decompiler.IL Debug.Assert(this.IsDescendantOf(variable.Function!)); } - public override StackType ResultType { - get { return StackType.Void; } - } - protected override InstructionFlags ComputeFlags() { return filter.Flags | body.Flags | InstructionFlags.ControlFlow | InstructionFlags.MayWriteLocals; @@ -227,12 +222,6 @@ namespace ICSharpCode.Decompiler.IL finallyBlock.WriteTo(output, options); } - public override StackType ResultType { - get { - return TryBlock.ResultType; - } - } - protected override InstructionFlags ComputeFlags() { // if the endpoint of either the try or the finally is unreachable, the endpoint of the try-finally will be unreachable @@ -324,10 +313,6 @@ namespace ICSharpCode.Decompiler.IL faultBlock.WriteTo(output, options); } - public override StackType ResultType { - get { return TryBlock.ResultType; } - } - protected override InstructionFlags ComputeFlags() { // The endpoint of the try-fault is unreachable iff the try endpoint is unreachable @@ -386,9 +371,4 @@ namespace ICSharpCode.Decompiler.IL } } } - - public partial class Throw - { - internal StackType resultType = StackType.Void; - } } diff --git a/ICSharpCode.Decompiler/IL/Instructions/UnaryInstruction.cs b/ICSharpCode.Decompiler/IL/Instructions/UnaryInstruction.cs index 8aa96a380..0c105608a 100644 --- a/ICSharpCode.Decompiler/IL/Instructions/UnaryInstruction.cs +++ b/ICSharpCode.Decompiler/IL/Instructions/UnaryInstruction.cs @@ -39,11 +39,8 @@ namespace ICSharpCode.Decompiler.IL public bool IsLifted { get; } public StackType UnderlyingResultType { get; } - public override StackType ResultType { - get { - return Argument.ResultType; - } - } + public override StackType ResultType => Argument.ResultType; + public override IType InferType(ICompilation compilation) => Argument.InferType(compilation); internal override void CheckInvariant(ILPhase phase, ICompilation compilation) { diff --git a/ICSharpCode.Decompiler/IL/StackType.cs b/ICSharpCode.Decompiler/IL/StackType.cs index 9bc6e9d0c..259ca2365 100644 --- a/ICSharpCode.Decompiler/IL/StackType.cs +++ b/ICSharpCode.Decompiler/IL/StackType.cs @@ -67,9 +67,17 @@ namespace ICSharpCode.Decompiler.IL F8, /// Another stack type. Includes objects, value types, ... O, - /// A managed pointer + // TODO: delete O and turn Obj/VT into separate enumerators. + /// Reference type: class type, boxed value type, etc. + Obj = O, + /// + /// A value type other than the primitive types listed above. + /// This includes unconstrained generic types which might be value types at runtime. + /// + VT = O, + /// A managed pointer (C# `ref T`, C++/CLI `interior_ptr<T>`) Ref, /// Represents the lack of a stack slot - Void + Void, } } diff --git a/ICSharpCode.Decompiler/IL/Transforms/CombineExitsTransform.cs b/ICSharpCode.Decompiler/IL/Transforms/CombineExitsTransform.cs index c399ac159..a5d35c5ef 100644 --- a/ICSharpCode.Decompiler/IL/Transforms/CombineExitsTransform.cs +++ b/ICSharpCode.Decompiler/IL/Transforms/CombineExitsTransform.cs @@ -65,7 +65,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms // leave (elseValue) // => // leave (if (cond) value else elseValue) - IfInstruction value = new IfInstruction(ifInst.Condition, leave.Value, leaveElse.Value); + IfInstruction value = new IfInstruction(ifInst.Condition, leave.Value, leaveElse.Value, leave.TargetContainer.ExpectedResultType); value.AddILRange(ifInst); Leave combinedLeave = new Leave(leave.TargetContainer, value); combinedLeave.AddILRange(leaveElse); diff --git a/ICSharpCode.Decompiler/IL/Transforms/DeconstructionTransform.cs b/ICSharpCode.Decompiler/IL/Transforms/DeconstructionTransform.cs index e7ff8d78e..d17c240f4 100644 --- a/ICSharpCode.Decompiler/IL/Transforms/DeconstructionTransform.cs +++ b/ICSharpCode.Decompiler/IL/Transforms/DeconstructionTransform.cs @@ -1147,7 +1147,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms { var receiver = nested.Receiver!; match.SubPatterns.Add(BuildPatternMatch(nested, receiver, - new DeconstructResultInstruction(i, receiver.StackType, new LdLoc(matchVariable)))); + new DeconstructResultInstruction(i, receiver.Type, receiver.StackType, new LdLoc(matchVariable)))); } else { @@ -1156,7 +1156,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms match.SubPatterns.Add( new MatchInstruction( result, - new DeconstructResultInstruction(i, result.StackType, new LdLoc(matchVariable)) + new DeconstructResultInstruction(i, result.Type, result.StackType, new LdLoc(matchVariable)) ) ); } @@ -1188,7 +1188,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms if (TupleType.GetTupleElementTypes(nested.Variable.Type).IsDefaultOrEmpty) nested.Variable.Type = nested.Type; match.SubPatterns.Add(BuildTuplePatternMatch(nested, nested.Variable, - new DeconstructResultInstruction(i, nested.Variable.StackType, new LdLoc(matchVariable)))); + new DeconstructResultInstruction(i, node.Type.ElementTypes[i], nested.Variable.StackType, new LdLoc(matchVariable)))); } else { @@ -1207,7 +1207,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms match.SubPatterns.Add( new MatchInstruction( result, - new DeconstructResultInstruction(i, result.StackType, new LdLoc(matchVariable)) + new DeconstructResultInstruction(i, node.Type.ElementTypes[i], result.StackType, new LdLoc(matchVariable)) ) ); } diff --git a/ICSharpCode.Decompiler/IL/Transforms/ExpandNestedConditionals.cs b/ICSharpCode.Decompiler/IL/Transforms/ExpandNestedConditionals.cs index 9c6aae873..85afe3d47 100644 --- a/ICSharpCode.Decompiler/IL/Transforms/ExpandNestedConditionals.cs +++ b/ICSharpCode.Decompiler/IL/Transforms/ExpandNestedConditionals.cs @@ -191,7 +191,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms var falseStore = new StLoc(v, falseValue).WithILRange(falseValue); var trueBlock = new Block { Instructions = { trueStore } }.WithILRange(trueStore); var falseBlock = new Block { Instructions = { falseStore } }.WithILRange(falseStore); - var expanded = new IfInstruction(condition, trueBlock, falseBlock); + var expanded = new IfInstruction(condition, trueBlock, falseBlock, context.TypeSystem.FindType(KnownTypeCode.Void)); expanded.AddILRange(ifInst); expanded.AddILRange(stloc); stloc.ReplaceWith(expanded); diff --git a/ICSharpCode.Decompiler/IL/Transforms/ExpressionTransforms.cs b/ICSharpCode.Decompiler/IL/Transforms/ExpressionTransforms.cs index 7c21e136d..6103fbd73 100644 --- a/ICSharpCode.Decompiler/IL/Transforms/ExpressionTransforms.cs +++ b/ICSharpCode.Decompiler/IL/Transforms/ExpressionTransforms.cs @@ -294,7 +294,9 @@ namespace ICSharpCode.Decompiler.IL.Transforms { context.Step("call Nullable{T}.GetValueOrDefault(a, b) -> a ?? b", inst); var ldObj = new LdObj(nullableValue, inst.Method.DeclaringType); - var replacement = new NullCoalescingInstruction(NullCoalescingKind.NullableWithValueFallback, ldObj, fallback) { + var replacement = new NullCoalescingInstruction( + NullableType.GetUnderlyingType(inst.Method.DeclaringType), + NullCoalescingKind.NullableWithValueFallback, ldObj, fallback) { UnderlyingResultType = fallback.ResultType }; inst.ReplaceWith(replacement.WithILRange(inst)); @@ -603,7 +605,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms if (trueInst.Instructions[0].MatchStLoc(out v, out value1) && falseInst.Instructions[0].MatchStLoc(v, out value2)) { context.Step("conditional operator", inst); - var newIf = new IfInstruction(Comp.LogicNot(inst.Condition), value2, value1); + var newIf = new IfInstruction(Comp.LogicNot(inst.Condition), value2, value1, v.Type); newIf.AddILRange(inst); var stLoc = new StLoc(v, newIf); inst.ReplaceWith(stLoc); @@ -621,7 +623,6 @@ namespace ICSharpCode.Decompiler.IL.Transforms Debug.Assert(container.Kind == ContainerKind.Switch); Debug.Assert(container.ResultType == StackType.Void); var defaultSection = switchInst.GetDefaultSection(); - StackType resultType = StackType.Void; BlockContainer leaveTarget = null; ILVariable resultVariable = null; foreach (var section in switchInst.Sections) @@ -650,7 +651,6 @@ namespace ICSharpCode.Decompiler.IL.Transforms return; leaveTarget ??= leave.TargetContainer; Debug.Assert(leaveTarget == leave.TargetContainer); - resultType = leave.Value.ResultType; } else { @@ -666,7 +666,6 @@ namespace ICSharpCode.Decompiler.IL.Transforms resultVariable ??= v; if (resultVariable != v) return; - resultType = resultVariable.StackType; } else { @@ -676,8 +675,9 @@ namespace ICSharpCode.Decompiler.IL.Transforms // Exactly one of resultVariable/leaveTarget must be null if ((resultVariable == null) == (leaveTarget == null)) return; + IType resultType = resultVariable?.Type ?? leaveTarget?.ExpectedResultType; // C# has no ref-returning switch expression: an arm cannot be `0 => ref x`. - if (resultType == StackType.Ref) + if (resultType.Kind == TypeKind.ByReference) return; if (switchInst.Value is StringToInt str2int) { @@ -699,7 +699,6 @@ namespace ICSharpCode.Decompiler.IL.Transforms { if (block.Instructions[0] is Throw t) { - t.resultType = resultType; section.Body = t; } else if (block.Instructions[0] is Leave leave) diff --git a/ICSharpCode.Decompiler/IL/Transforms/HighLevelLoopTransform.cs b/ICSharpCode.Decompiler/IL/Transforms/HighLevelLoopTransform.cs index 47c156ded..4ba83f0a0 100644 --- a/ICSharpCode.Decompiler/IL/Transforms/HighLevelLoopTransform.cs +++ b/ICSharpCode.Decompiler/IL/Transforms/HighLevelLoopTransform.cs @@ -223,11 +223,11 @@ namespace ICSharpCode.Decompiler.IL.Transforms { if (swap) { - condition.Condition = IfInstruction.LogicAnd(Comp.LogicNot(inst.Condition), condition.Condition); + condition.Condition = IfInstruction.LogicAnd(Comp.LogicNot(inst.Condition), condition.Condition, context.TypeSystem); } else { - condition.Condition = IfInstruction.LogicAnd(inst.Condition, condition.Condition); + condition.Condition = IfInstruction.LogicAnd(inst.Condition, condition.Condition, context.TypeSystem); } } } @@ -433,11 +433,12 @@ namespace ICSharpCode.Decompiler.IL.Transforms break; if (forCondition == null) { - forCondition = new IfInstruction(condition, whileCondition.TrueInst, whileCondition.FalseInst); + forCondition = new IfInstruction(condition, whileCondition.TrueInst, whileCondition.FalseInst, + whileCondition.InferType(context.TypeSystem)); } else { - forCondition.Condition = IfInstruction.LogicAnd(forCondition.Condition, condition); + forCondition.Condition = IfInstruction.LogicAnd(forCondition.Condition, condition, context.TypeSystem); } numberOfConditions++; } diff --git a/ICSharpCode.Decompiler/IL/Transforms/NullCoalescingTransform.cs b/ICSharpCode.Decompiler/IL/Transforms/NullCoalescingTransform.cs index b8ad1e251..9d03cd680 100644 --- a/ICSharpCode.Decompiler/IL/Transforms/NullCoalescingTransform.cs +++ b/ICSharpCode.Decompiler/IL/Transforms/NullCoalescingTransform.cs @@ -66,7 +66,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms if (trueInst.MatchStLoc(stloc.Variable, out var fallbackValue)) { context.Step("NullCoalescingTransform: simple (reference types)", stloc); - stloc.Value = new NullCoalescingInstruction(NullCoalescingKind.Ref, stloc.Value, fallbackValue); + stloc.Value = new NullCoalescingInstruction(stloc.Variable.Type, NullCoalescingKind.Ref, stloc.Value, fallbackValue); block.Instructions.RemoveAt(pos + 1); // remove if instruction ILInlining.InlineOneIfPossible(block, pos, InliningOptions.None, context); return true; @@ -85,7 +85,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms && useOfTemporary.MatchLdLoc(temporary)) { context.Step("NullCoalescingTransform: with temporary variable (reference types)", stloc); - stloc.Value = new NullCoalescingInstruction(NullCoalescingKind.Ref, stloc.Value, fallbackValue); + stloc.Value = new NullCoalescingInstruction(stloc.Variable.Type, NullCoalescingKind.Ref, stloc.Value, fallbackValue); block.Instructions.RemoveAt(pos + 1); // remove if instruction ILInlining.InlineOneIfPossible(block, pos, InliningOptions.None, context); return true; @@ -99,8 +99,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms if (context.Settings.ThrowExpressions && trueInst is Throw throwInst) { context.Step("NullCoalescingTransform (reference types + throw expression)", stloc); - throwInst.resultType = StackType.O; - stloc.Value = new NullCoalescingInstruction(NullCoalescingKind.Ref, stloc.Value, throwInst); + stloc.Value = new NullCoalescingInstruction(stloc.Variable.Type, NullCoalescingKind.Ref, stloc.Value, throwInst); block.Instructions.RemoveAt(pos + 1); // remove if instruction ILInlining.InlineOneIfPossible(block, pos, InliningOptions.None, context); return true; @@ -156,7 +155,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms var paramLoadChildIndex = paramLoad.ChildIndex; var throwInstParent = throwInst.Parent; var throwInstChildIndex = throwInst.ChildIndex; - var expressionWithThrow = new NullCoalescingInstruction(NullCoalescingKind.Ref, paramLoad, throwInst); + var expressionWithThrow = new NullCoalescingInstruction(paramLoad.Variable.Type, NullCoalescingKind.Ref, paramLoad, throwInst); var result = ILInlining.FindLoadInNext(block.Instructions[pos + 1], paramLoad.Variable, expressionWithThrow, InliningOptions.None); if (result.Type != ILInlining.FindResultType.Found || result.LoadInst is not LdLoc firstUse) @@ -173,7 +172,6 @@ namespace ICSharpCode.Decompiler.IL.Transforms var temp = function.RegisterVariable(VariableKind.StackSlot, paramLoad.Variable.Type); firstUse.Variable = temp; - throwInst.resultType = StackType.O; var stloc = new StLoc(temp, expressionWithThrow); stloc.AddILRange(guard); block.Instructions[pos] = stloc; @@ -230,18 +228,19 @@ namespace ICSharpCode.Decompiler.IL.Transforms return false; var throwInstParent = throwInst.Parent; var throwInstChildIndex = throwInst.ChildIndex; + var underlyingType = NullableType.GetUnderlyingType(call.Method.DeclaringType); var nullCoalescingWithThrow = new NullCoalescingInstruction( + underlyingType, NullCoalescingKind.NullableWithValueFallback, stloc.Value, throwInst); - var resultType = NullableType.GetUnderlyingType(call.Method.DeclaringType).GetStackType(); + var resultType = underlyingType.GetStackType(); nullCoalescingWithThrow.UnderlyingResultType = resultType; var result = ILInlining.FindLoadInNext(block.Instructions[pos + 2], v, nullCoalescingWithThrow, InliningOptions.None); if (result.Type == ILInlining.FindResultType.Found && NullableLiftingTransform.MatchGetValueOrDefault(result.LoadInst.Parent, v)) { context.Step("NullCoalescingTransform (value types + throw expression)", stloc); - throwInst.resultType = resultType; result.LoadInst.Parent.ReplaceWith(nullCoalescingWithThrow); block.Instructions.RemoveRange(pos, 2); // remove store(s) and if instruction return true; diff --git a/ICSharpCode.Decompiler/IL/Transforms/NullPropagationTransform.cs b/ICSharpCode.Decompiler/IL/Transforms/NullPropagationTransform.cs index b320ae44b..567996c4f 100644 --- a/ICSharpCode.Decompiler/IL/Transforms/NullPropagationTransform.cs +++ b/ICSharpCode.Decompiler/IL/Transforms/NullPropagationTransform.cs @@ -115,7 +115,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms nonNullInst = arg; removedRewrapOrNullableCtor = true; } - else if (nonNullInst.MatchNullableRewrap(out arg)) + else if (nonNullInst.MatchNullableRewrap(out arg, out _)) { nonNullInst = arg; removedRewrapOrNullableCtor = true; @@ -130,7 +130,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms // testedVar != null ? testedVar.AccessChain : null // => testedVar?.AccessChain IntroduceUnwrap(testedVar, varLoad, mode); - var result = new NullableRewrap(nonNullInst); + var result = new NullableRewrap(nonNullInst, returnType); context.EndStep(result); return result; } @@ -140,7 +140,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms // testedVar != null ? testedVar.AccessChain : default(T?) // => testedVar?.AccessChain IntroduceUnwrap(testedVar, varLoad, mode); - var result = new NullableRewrap(nonNullInst); + var result = new NullableRewrap(nonNullInst, type); context.EndStep(result); return result; } @@ -154,8 +154,9 @@ namespace ICSharpCode.Decompiler.IL.Transforms // Span is excluded because it cannot be wrapped in Nullable for the ?. / ?? form) IntroduceUnwrap(testedVar, varLoad, mode); var result = new NullCoalescingInstruction( + nullInst.InferType(context.TypeSystem), NullCoalescingKind.NullableWithValueFallback, - new NullableRewrap(nonNullInst), + new NullableRewrap(nonNullInst, NullableType.Create(context.TypeSystem, returnType)), nullInst ) { UnderlyingResultType = nullInst.ResultType @@ -231,7 +232,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms if (body == null || body.Instructions.Count != 1) return; var bodyInst = body.Instructions[0]; - if (bodyInst.MatchNullableRewrap(out var arg)) + if (bodyInst.MatchNullableRewrap(out var arg, out _)) { bodyInst = arg; } @@ -242,7 +243,8 @@ namespace ICSharpCode.Decompiler.IL.Transforms // => testedVar?.AccessChain(); IntroduceUnwrap(testedVar, varLoad, mode); var replacement = new NullableRewrap( - bodyInst + bodyInst, + bodyInst.InferType(context.TypeSystem) ).WithILRange(ifInst); ifInst.ReplaceWith(replacement); context.EndStep(replacement); @@ -404,17 +406,19 @@ namespace ICSharpCode.Decompiler.IL.Transforms case Mode.ReferenceType: case Mode.UnconstrainedType: // Wrap varLoad in nullable.unwrap: - replacement = new NullableUnwrap(varLoad.ResultType, varLoad, refInput: varLoad.ResultType == StackType.Ref); + replacement = new NullableUnwrap(varLoad.InferType(context.TypeSystem), varLoad.ResultType, varLoad, refInput: varLoad.ResultType == StackType.Ref); break; case Mode.NullableByValue: Debug.Assert(NullableLiftingTransform.MatchGetValueOrDefault(varLoad, testedVar)); replacement = new NullableUnwrap( + varLoad.InferType(context.TypeSystem), varLoad.ResultType, new LdLoc(testedVar).WithILRange(varLoad.Children[0]) ).WithILRange(varLoad); break; case Mode.NullableByReference: replacement = new NullableUnwrap( + varLoad.InferType(context.TypeSystem), varLoad.ResultType, new LdLoc(testedVar).WithILRange(varLoad.Children[0]), refInput: true diff --git a/ICSharpCode.Decompiler/IL/Transforms/NullableLiftingTransform.cs b/ICSharpCode.Decompiler/IL/Transforms/NullableLiftingTransform.cs index a688fe514..b4ac087d7 100644 --- a/ICSharpCode.Decompiler/IL/Transforms/NullableLiftingTransform.cs +++ b/ICSharpCode.Decompiler/IL/Transforms/NullableLiftingTransform.cs @@ -771,7 +771,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms // v.HasValue ? ldloc v : fallback // => v ?? fallback context.Step("v.HasValue ? v : fallback => v ?? fallback", trueInst); - return new NullCoalescingInstruction(NullCoalescingKind.Nullable, trueInst, falseInst) { + return new NullCoalescingInstruction(nullableVars[0].Type, NullCoalescingKind.Nullable, trueInst, falseInst) { UnderlyingResultType = NullableType.GetUnderlyingType(nullableVars[0].Type).GetStackType() }; } @@ -822,7 +822,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms } if (isNullCoalescingWithNonNullableFallback) { - lifted = new NullCoalescingInstruction(NullCoalescingKind.NullableWithValueFallback, lifted, falseInst) { + lifted = new NullCoalescingInstruction(utype, NullCoalescingKind.NullableWithValueFallback, lifted, falseInst) { UnderlyingResultType = exprToLift.ResultType }; } @@ -830,7 +830,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms { // Normal lifting, but the falseInst isn't `default(utype?)` // => use the `??` operator to provide the fallback value. - lifted = new NullCoalescingInstruction(NullCoalescingKind.Nullable, lifted, falseInst) { + lifted = new NullCoalescingInstruction(NullableType.Create(context.TypeSystem, utype), NullCoalescingKind.Nullable, lifted, falseInst) { UnderlyingResultType = exprToLift.ResultType }; } diff --git a/ICSharpCode.Decompiler/IL/Transforms/RemoveDeadVariableInit.cs b/ICSharpCode.Decompiler/IL/Transforms/RemoveDeadVariableInit.cs index 22fd19f0d..0103939fd 100644 --- a/ICSharpCode.Decompiler/IL/Transforms/RemoveDeadVariableInit.cs +++ b/ICSharpCode.Decompiler/IL/Transforms/RemoveDeadVariableInit.cs @@ -80,30 +80,6 @@ namespace ICSharpCode.Decompiler.IL.Transforms } } } - - // Try to infer IType of stack slots that are of StackType.Ref: - foreach (var v in function.Variables) - { - if (v.Kind == VariableKind.StackSlot && v.StackType == StackType.Ref && v.AddressCount == 0) - { - IType newType = null; - // Multiple store are possible in case of (c ? ref a : ref b) += 1, for example. - foreach (var stloc in v.StoreInstructions.OfType()) - { - var inferredType = stloc.Value.InferType(context.TypeSystem); - // cancel, if types of values do not match exactly - if (newType != null && !newType.Equals(inferredType)) - { - newType = SpecialType.UnknownType; - break; - } - newType = inferredType; - } - // Only overwrite existing type, if a "better" type was found. - if (newType != null && newType != SpecialType.UnknownType) - v.Type = newType; - } - } } internal static void ResetUsesInitialValueFlag(ILFunction function, ILTransformContext context) diff --git a/ICSharpCode.Decompiler/IL/Transforms/TransformExpressionTrees.cs b/ICSharpCode.Decompiler/IL/Transforms/TransformExpressionTrees.cs index c196e0c66..52ed30206 100644 --- a/ICSharpCode.Decompiler/IL/Transforms/TransformExpressionTrees.cs +++ b/ICSharpCode.Decompiler/IL/Transforms/TransformExpressionTrees.cs @@ -183,7 +183,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms lambdaStack.Push(function); var convertedBody = bodyInstruction(); lambdaStack.Pop(); - container.ExpectedResultType = convertedBody.ResultType; + container.ExpectedResultType = type; container.Blocks.Add(new Block() { Instructions = { new Leave(container, convertedBody) } }); // Replace all other usages of the parameter variable foreach (var mapping in parameterMapping) @@ -752,7 +752,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms { targetType = fallbackInstType; } - return (() => new NullCoalescingInstruction(kind, trueInst(), fallbackInst()) { + return (() => new NullCoalescingInstruction(targetType, kind, trueInst(), fallbackInst()) { UnderlyingResultType = trueInstTypeNonNullable.GetStackType() }, targetType); } @@ -820,7 +820,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms return (null, SpecialType.UnknownType); if (!NormalizeTypeVisitor.TypeErasure.EquivalentTypes(trueInstType, falseInstType)) return (null, SpecialType.UnknownType); - return (() => new IfInstruction(condition(), trueInst(), falseInst()), trueInstType); + return (() => new IfInstruction(condition(), trueInst(), falseInst(), trueInstType), trueInstType); } (Func, IType) ConvertConstant(CallInstruction invocation) @@ -1008,7 +1008,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms { case 2: var resultType = context.TypeSystem.FindType(KnownTypeCode.Boolean); - return (() => and ? IfInstruction.LogicAnd(left(), right()) : IfInstruction.LogicOr(left(), right()), resultType); + return (() => and ? IfInstruction.LogicAnd(left(), right(), context.TypeSystem) : IfInstruction.LogicOr(left(), right(), context.TypeSystem), resultType); case 3: if (!MatchGetMethodFromHandle(invocation.Arguments[2], out method)) return (null, SpecialType.UnknownType); diff --git a/ICSharpCode.Decompiler/IL/Transforms/UsingTransform.cs b/ICSharpCode.Decompiler/IL/Transforms/UsingTransform.cs index 39c20c13f..2ca437f71 100644 --- a/ICSharpCode.Decompiler/IL/Transforms/UsingTransform.cs +++ b/ICSharpCode.Decompiler/IL/Transforms/UsingTransform.cs @@ -272,7 +272,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms return false; disposeInvocation = disposeBlock.Instructions[0]; } - else if (checkInst.MatchNullableRewrap(out disposeInst)) + else if (checkInst.MatchNullableRewrap(out disposeInst, out _)) { disposeInvocation = disposeInst; } diff --git a/ICSharpCode.Decompiler/TypeSystem/KnownTypeReference.cs b/ICSharpCode.Decompiler/TypeSystem/KnownTypeReference.cs index a1a344110..41bb68dd9 100644 --- a/ICSharpCode.Decompiler/TypeSystem/KnownTypeReference.cs +++ b/ICSharpCode.Decompiler/TypeSystem/KnownTypeReference.cs @@ -154,7 +154,15 @@ namespace ICSharpCode.Decompiler.TypeSystem /// System.Index Index, /// System.Range - Range + Range, + /// System.RuntimeArgumentHandle + RuntimeArgumentHandle, + /// System.RuntimeTypeHandle + RuntimeTypeHandle, + /// System.RuntimeFieldHandle + RuntimeFieldHandle, + /// System.RuntimeMethodHandle + RuntimeMethodHandle, } /// @@ -163,7 +171,7 @@ namespace ICSharpCode.Decompiler.TypeSystem [Serializable] public sealed class KnownTypeReference : ITypeReference { - internal const int KnownTypeCodeCount = (int)KnownTypeCode.Range + 1; + internal const int KnownTypeCodeCount = (int)KnownTypeCode.RuntimeMethodHandle + 1; static readonly KnownTypeReference?[] knownTypeReferences = new KnownTypeReference?[KnownTypeCodeCount] { null, // None @@ -229,6 +237,10 @@ namespace ICSharpCode.Decompiler.TypeSystem new KnownTypeReference(KnownTypeCode.IAsyncEnumeratorOfT, TypeKind.Interface, "System.Collections.Generic", "IAsyncEnumerator", 1), new KnownTypeReference(KnownTypeCode.Index, TypeKind.Struct, "System", "Index", 0), new KnownTypeReference(KnownTypeCode.Range, TypeKind.Struct, "System", "Range", 0), + new KnownTypeReference(KnownTypeCode.RuntimeArgumentHandle, TypeKind.Struct, "System", "RuntimeArgumentHandle", 0), + new KnownTypeReference(KnownTypeCode.RuntimeTypeHandle, TypeKind.Struct, "System", "RuntimeTypeHandle", 0), + new KnownTypeReference(KnownTypeCode.RuntimeFieldHandle, TypeKind.Struct, "System", "RuntimeFieldHandle", 0), + new KnownTypeReference(KnownTypeCode.RuntimeMethodHandle, TypeKind.Struct, "System", "RuntimeMethodHandle", 0), }; /// diff --git a/ICSharpCode.Decompiler/TypeSystem/ReflectionHelper.cs b/ICSharpCode.Decompiler/TypeSystem/ReflectionHelper.cs index edc2dfc30..677f462ec 100644 --- a/ICSharpCode.Decompiler/TypeSystem/ReflectionHelper.cs +++ b/ICSharpCode.Decompiler/TypeSystem/ReflectionHelper.cs @@ -51,7 +51,7 @@ namespace ICSharpCode.Decompiler.TypeSystem case StackType.Unknown: return SpecialType.UnknownType; case StackType.Ref: - return new ByReferenceType(SpecialType.UnknownType); + return new ByReferenceType(compilation.FindType(KnownTypeCode.Byte)); default: return compilation.FindType(stackType.ToKnownTypeCode(sign)); } diff --git a/ICSharpCode.Decompiler/TypeSystem/TypeSystemExtensions.cs b/ICSharpCode.Decompiler/TypeSystem/TypeSystemExtensions.cs index af109ad1e..ded23ca29 100644 --- a/ICSharpCode.Decompiler/TypeSystem/TypeSystemExtensions.cs +++ b/ICSharpCode.Decompiler/TypeSystem/TypeSystemExtensions.cs @@ -310,8 +310,8 @@ namespace ICSharpCode.Decompiler.TypeSystem case KnownTypeCode.IntPtr: case KnownTypeCode.UIntPtr: case KnownTypeCode.TypedReference: - //case KnownTypeCode.ArgIterator: - //case KnownTypeCode.RuntimeArgumentHandle: + //case KnownTypeCode.ArgIterator: + case KnownTypeCode.RuntimeArgumentHandle: return true; } if (type.Kind == TypeKind.Struct)