Browse Source

Extend InferType() to every ILInstruction

Change InferType() to an abstract method and implement for every ILInstruction.
With this change, we now always have enough information to create a variable of an appropriate type to store the result of evaluating the instruction.
This previously was not the case for instructions producing "other value type", for which the stacktype-based fallback incorrectly produced `object`.
pull/4090/head
Daniel Grunwald 2 weeks ago
parent
commit
5526400b17
  1. 12
      ICSharpCode.Decompiler.Tests/TestCases/ILPretty/StackAllocDuplicateStore.cs
  2. 19
      ICSharpCode.Decompiler.Tests/TestCases/Pretty/CS73_StackAllocInitializers.cs
  3. 8
      ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs
  4. 8
      ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj
  5. 6
      ICSharpCode.Decompiler/IL/BlockBuilder.cs
  6. 4
      ICSharpCode.Decompiler/IL/ControlFlow/AsyncAwaitDecompiler.cs
  7. 2
      ICSharpCode.Decompiler/IL/ControlFlow/ConditionDetection.cs
  8. 19
      ICSharpCode.Decompiler/IL/ControlFlow/DetectPinnedRegions.cs
  9. 13
      ICSharpCode.Decompiler/IL/ILReader.cs
  10. 127
      ICSharpCode.Decompiler/IL/ILTypeExtensions.cs
  11. 243
      ICSharpCode.Decompiler/IL/Instructions.cs
  12. 166
      ICSharpCode.Decompiler/IL/Instructions.tt
  13. 12
      ICSharpCode.Decompiler/IL/Instructions/BinaryNumericInstruction.cs
  14. 5
      ICSharpCode.Decompiler/IL/Instructions/Block.cs
  15. 26
      ICSharpCode.Decompiler/IL/Instructions/BlockContainer.cs
  16. 1
      ICSharpCode.Decompiler/IL/Instructions/CallIndirect.cs
  17. 11
      ICSharpCode.Decompiler/IL/Instructions/CallInstruction.cs
  18. 14
      ICSharpCode.Decompiler/IL/Instructions/Comp.cs
  19. 1
      ICSharpCode.Decompiler/IL/Instructions/CompoundAssignmentInstruction.cs
  20. 20
      ICSharpCode.Decompiler/IL/Instructions/Conv.cs
  21. 6
      ICSharpCode.Decompiler/IL/Instructions/DeconstructResultInstruction.cs
  22. 26
      ICSharpCode.Decompiler/IL/Instructions/DynamicInstructions.cs
  23. 29
      ICSharpCode.Decompiler/IL/Instructions/ILInstruction.cs
  24. 48
      ICSharpCode.Decompiler/IL/Instructions/IfInstruction.cs
  25. 10
      ICSharpCode.Decompiler/IL/Instructions/LdLen.cs
  26. 6
      ICSharpCode.Decompiler/IL/Instructions/LogicInstructions.cs
  27. 6
      ICSharpCode.Decompiler/IL/Instructions/NullCoalescingInstruction.cs
  28. 8
      ICSharpCode.Decompiler/IL/Instructions/NullableInstructions.cs
  29. 12
      ICSharpCode.Decompiler/IL/Instructions/SimpleInstruction.cs
  30. 7
      ICSharpCode.Decompiler/IL/Instructions/SwitchInstruction.cs
  31. 26
      ICSharpCode.Decompiler/IL/Instructions/TryInstruction.cs
  32. 7
      ICSharpCode.Decompiler/IL/Instructions/UnaryInstruction.cs
  33. 12
      ICSharpCode.Decompiler/IL/StackType.cs
  34. 2
      ICSharpCode.Decompiler/IL/Transforms/CombineExitsTransform.cs
  35. 8
      ICSharpCode.Decompiler/IL/Transforms/DeconstructionTransform.cs
  36. 2
      ICSharpCode.Decompiler/IL/Transforms/ExpandNestedConditionals.cs
  37. 13
      ICSharpCode.Decompiler/IL/Transforms/ExpressionTransforms.cs
  38. 9
      ICSharpCode.Decompiler/IL/Transforms/HighLevelLoopTransform.cs
  39. 15
      ICSharpCode.Decompiler/IL/Transforms/NullCoalescingTransform.cs
  40. 18
      ICSharpCode.Decompiler/IL/Transforms/NullPropagationTransform.cs
  41. 6
      ICSharpCode.Decompiler/IL/Transforms/NullableLiftingTransform.cs
  42. 24
      ICSharpCode.Decompiler/IL/Transforms/RemoveDeadVariableInit.cs
  43. 8
      ICSharpCode.Decompiler/IL/Transforms/TransformExpressionTrees.cs
  44. 2
      ICSharpCode.Decompiler/IL/Transforms/UsingTransform.cs
  45. 16
      ICSharpCode.Decompiler/TypeSystem/KnownTypeReference.cs
  46. 2
      ICSharpCode.Decompiler/TypeSystem/ReflectionHelper.cs
  47. 4
      ICSharpCode.Decompiler/TypeSystem/TypeSystemExtensions.cs

12
ICSharpCode.Decompiler.Tests/TestCases/ILPretty/StackAllocDuplicateStore.cs

@ -6,12 +6,12 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.ILPretty @@ -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<int> span = new Span<int>(num, 3);
byte* ptr = stackalloc byte[12];
*(int*)ptr = a;
*(int*)ptr = 99;
((int*)ptr)[1] = b;
((int*)ptr)[2] = c;
Span<int> span = new Span<int>(ptr, 3);
return span[0] + span[1] + span[2];
}
}

19
ICSharpCode.Decompiler.Tests/TestCases/Pretty/CS73_StackAllocInitializers.cs

@ -274,21 +274,12 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty @@ -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 @@ -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 @@ -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)

8
ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs

@ -803,7 +803,7 @@ namespace ICSharpCode.Decompiler.CSharp @@ -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 @@ -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 @@ -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 @@ -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)

8
ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj

@ -177,4 +177,12 @@ @@ -177,4 +177,12 @@
<Compile Remove="Properties\DecompilerVersionInfo.template.cs" />
</ItemGroup>
<ItemGroup>
<Compile Update="IL\Instructions.cs">
<DesignTime>True</DesignTime>
<AutoGen>True</AutoGen>
<DependentUpon>Instructions.tt</DependentUpon>
</Compile>
</ItemGroup>
</Project>

6
ICSharpCode.Decompiler/IL/BlockBuilder.cs

@ -96,7 +96,7 @@ namespace ICSharpCode.Decompiler.IL @@ -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 @@ -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:

4
ICSharpCode.Decompiler/IL/ControlFlow/AsyncAwaitDecompiler.cs

@ -158,11 +158,11 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow @@ -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,

2
ICSharpCode.Decompiler/IL/ControlFlow/ConditionDetection.cs

@ -440,7 +440,7 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow @@ -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;
}
}

19
ICSharpCode.Decompiler/IL/ControlFlow/DetectPinnedRegions.cs

@ -162,10 +162,11 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow @@ -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 @@ -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 @@ -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 @@ -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 @@ -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;

13
ICSharpCode.Decompiler/IL/ILReader.cs

@ -190,14 +190,16 @@ namespace ICSharpCode.Decompiler.IL @@ -190,14 +190,16 @@ namespace ICSharpCode.Decompiler.IL
this.reader = body.GetILReader();
this.currentStack = ImmutableStack<ILVariable>.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 @@ -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 @@ -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);

127
ICSharpCode.Decompiler/IL/ILTypeExtensions.cs

@ -166,7 +166,7 @@ namespace ICSharpCode.Decompiler.IL @@ -166,7 +166,7 @@ namespace ICSharpCode.Decompiler.IL
/// <summary>
/// Infers the C# type an instruction expects of the child in <paramref name="childIndex"/>,
/// i.e. the counterpart to <see cref="InferType"/>: that one asks what a value is, this one
/// i.e. the counterpart to <see cref="ILInstruction.InferType"/>: 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 @@ -202,130 +202,5 @@ namespace ICSharpCode.Decompiler.IL
return SpecialType.UnknownType;
}
}
/// <summary>
/// Infers the C# type for an IL instruction.
///
/// Returns SpecialType.UnknownType for unsupported instructions.
/// </summary>
/// <remarks>
/// 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.
/// </remarks>
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;
}
}
}
}

243
ICSharpCode.Decompiler/IL/Instructions.cs

@ -540,7 +540,8 @@ namespace ICSharpCode.Decompiler.IL.Patterns @@ -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 @@ -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 @@ -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 @@ -844,7 +847,7 @@ namespace ICSharpCode.Decompiler.IL
/// <summary>A container of IL blocks.</summary>
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 @@ -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 @@ -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 @@ -1142,7 +1147,8 @@ namespace ICSharpCode.Decompiler.IL
/// <summary>Common instruction for dynamic compound assignments.</summary>
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 @@ -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 @@ -1229,7 +1236,8 @@ namespace ICSharpCode.Decompiler.IL
/// <summary>Unconditional branch. <c>goto target;</c></summary>
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 @@ -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 @@ -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 @@ -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 @@ -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 @@ -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 @@ -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 @@ -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 @@ -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 @@ -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 @@ -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 @@ -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;
/// <summary>Returns the type operand.</summary>
public IType Type {
@ -2690,7 +2710,7 @@ namespace ICSharpCode.Decompiler.IL @@ -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 @@ -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 @@ -2784,10 +2804,26 @@ namespace ICSharpCode.Decompiler.IL
/// If the input evaluates normally, evaluates to the input value (wrapped in Nullable&lt;T&gt; 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.</summary>
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;
/// <summary>Returns the type operand.</summary>
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 @@ -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 @@ -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 @@ -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 @@ -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 @@ -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 @@ -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 @@ -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 @@ -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 @@ -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 @@ -3106,7 +3150,8 @@ namespace ICSharpCode.Decompiler.IL
readonly IMethod method;
/// <summary>Returns the method operand.</summary>
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 @@ -3148,7 +3193,8 @@ namespace ICSharpCode.Decompiler.IL
readonly IMethod method;
/// <summary>Returns the method operand.</summary>
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 @@ -3209,7 +3255,8 @@ namespace ICSharpCode.Decompiler.IL
readonly IMethod method;
/// <summary>Returns the method operand.</summary>
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 @@ -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 @@ -3307,7 +3355,8 @@ namespace ICSharpCode.Decompiler.IL
readonly IMember member;
/// <summary>Returns the token operand.</summary>
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 @@ -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 @@ -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 @@ -3524,7 +3575,8 @@ namespace ICSharpCode.Decompiler.IL
public bool IsVolatile { get; set; }
/// <summary>Returns the alignment specified by the 'unaligned' prefix; or 0 if there was no 'unaligned' prefix.</summary>
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 @@ -3675,7 +3727,8 @@ namespace ICSharpCode.Decompiler.IL
public bool IsVolatile { get; set; }
/// <summary>Returns the alignment specified by the 'unaligned' prefix; or 0 if there was no 'unaligned' prefix.</summary>
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 @@ -3791,7 +3844,8 @@ namespace ICSharpCode.Decompiler.IL
readonly IField @field;
/// <summary>Returns the field operand.</summary>
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 @@ -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;
/// <summary>Returns the field operand.</summary>
public IField Field { get { return @field; } }
@ -3886,7 +3941,8 @@ namespace ICSharpCode.Decompiler.IL @@ -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 @@ -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 @@ -4040,7 +4097,8 @@ namespace ICSharpCode.Decompiler.IL
public bool IsVolatile { get; set; }
/// <summary>Returns the alignment specified by the 'unaligned' prefix; or 0 if there was no 'unaligned' prefix.</summary>
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 @@ -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 @@ -4287,7 +4346,8 @@ namespace ICSharpCode.Decompiler.IL
public bool IsVolatile { get; set; }
/// <summary>Returns the alignment specified by the 'unaligned' prefix; or 0 if there was no 'unaligned' prefix.</summary>
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 @@ -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 @@ -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 @@ -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 @@ -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 @@ -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 @@ -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 @@ -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 @@ -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 @@ -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 @@ -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);
/// <summary>Gets whether the 'readonly' prefix was applied to this instruction.</summary>
public bool IsReadOnly { get; set; }
protected override InstructionFlags ComputeFlags()
@ -5063,7 +5133,8 @@ namespace ICSharpCode.Decompiler.IL @@ -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);
/// <summary>Gets whether the 'readonly' prefix was applied to this instruction.</summary>
public bool IsReadOnly { get; set; }
protected override InstructionFlags ComputeFlags()
@ -5120,9 +5191,10 @@ namespace ICSharpCode.Decompiler.IL @@ -5120,9 +5191,10 @@ namespace ICSharpCode.Decompiler.IL
/// </summary>
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 @@ -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;
/// <summary>Returns the type operand.</summary>
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;
/// <summary>Returns the method operand.</summary>
public IMethod? Method => method;
@ -5192,6 +5271,8 @@ namespace ICSharpCode.Decompiler.IL @@ -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 @@ -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 @@ -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 @@ -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 @@ -5368,7 +5451,8 @@ namespace ICSharpCode.Decompiler.IL
readonly IMethod method;
/// <summary>Returns the method operand.</summary>
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 @@ -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 @@ -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 @@ -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 @@ -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 @@ -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 @@ -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 @@ -6941,7 +7031,8 @@ namespace ICSharpCode.Decompiler.IL
/// <summary>Deconstruction statement</summary>
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 @@ -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 @@ -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;
}

166
ICSharpCode.Decompiler/IL/Instructions.tt

@ -35,7 +35,7 @@ @@ -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 @@ @@ -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 @@ @@ -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 @@ @@ -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 @@ @@ -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. <c>goto target;</c>",
CustomClassName("Branch"), NoArguments, CustomConstructor, UnconditionalBranch, MayBranch,
MatchCondition("this.TargetBlock == o.TargetBlock")),
@ -123,7 +123,7 @@ @@ -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 @@ @@ -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 @@ @@ -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 @@ @@ -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 @@ @@ -200,114 +201,132 @@
+ "If the input evaluates normally, evaluates to the input value (wrapped in Nullable&lt;T&gt; 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&lt;byte&gt;).",
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.<para>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).</para>",
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.<para>" +
"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).</para>",
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 &amp;&amp; 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 @@ @@ -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 @@ @@ -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 @@ -715,24 +735,28 @@ namespace ICSharpCode.Decompiler.IL
}
// ResultType trait: the instruction has the specified result type.
static Action<OpCode> ResultType(string type)
static Action<OpCode> 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<OpCode> VoidResult = ResultType("Void");
static Action<OpCode> VoidResult = ResultType("Void", "Void");
// ResultTypeParam trait: the instruction takes its result type as ctor parameter
static Action<OpCode> 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

12
ICSharpCode.Decompiler/IL/Instructions/BinaryNumericInstruction.cs

@ -129,6 +129,14 @@ namespace ICSharpCode.Decompiler.IL @@ -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 @@ -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;
}

5
ICSharpCode.Decompiler/IL/Instructions/Block.cs

@ -235,6 +235,11 @@ namespace ICSharpCode.Decompiler.IL @@ -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)

26
ICSharpCode.Decompiler/IL/Instructions/BlockContainer.cs

@ -44,7 +44,11 @@ namespace ICSharpCode.Decompiler.IL @@ -44,7 +44,11 @@ namespace ICSharpCode.Decompiler.IL
public readonly InstructionCollection<Block> 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 @@ -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<Block>(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 @@ -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;

1
ICSharpCode.Decompiler/IL/Instructions/CallIndirect.cs

@ -67,6 +67,7 @@ namespace ICSharpCode.Decompiler.IL @@ -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)
{

11
ICSharpCode.Decompiler/IL/Instructions/CallInstruction.cs

@ -87,14 +87,9 @@ namespace ICSharpCode.Decompiler.IL @@ -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;
/// <summary>
/// Gets the expected stack type for passing the this pointer in a method call.

14
ICSharpCode.Decompiler/IL/Instructions/Comp.cs

@ -161,6 +161,20 @@ namespace ICSharpCode.Decompiler.IL @@ -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;

1
ICSharpCode.Decompiler/IL/Instructions/CompoundAssignmentInstruction.cs

@ -336,6 +336,7 @@ namespace ICSharpCode.Decompiler.IL @@ -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)
{

20
ICSharpCode.Decompiler/IL/Instructions/Conv.cs

@ -307,7 +307,25 @@ namespace ICSharpCode.Decompiler.IL @@ -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 {

6
ICSharpCode.Decompiler/IL/Instructions/DeconstructResultInstruction.cs

@ -28,14 +28,18 @@ namespace ICSharpCode.Decompiler.IL @@ -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)
{

26
ICSharpCode.Decompiler/IL/Instructions/DynamicInstructions.cs

@ -156,6 +156,8 @@ namespace ICSharpCode.Decompiler.IL @@ -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 @@ -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 @@ -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 @@ -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 @@ -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 @@ -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 @@ -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 @@ -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 @@ -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 @@ -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 @@ -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 @@ -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);

29
ICSharpCode.Decompiler/IL/Instructions/ILInstruction.cs

@ -102,6 +102,8 @@ namespace ICSharpCode.Decompiler.IL @@ -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);
}
/// <summary>
@ -216,19 +218,26 @@ namespace ICSharpCode.Decompiler.IL @@ -216,19 +218,26 @@ namespace ICSharpCode.Decompiler.IL
/// </summary>
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.
/// <summary>
/// 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.
/// </summary>
/// <remarks>
/// 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.
/// </summary>
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.
/// </remarks>
public abstract IType InferType(ICompilation compilation);
internal static StackType CommonResultType(StackType a, StackType b)
{

48
ICSharpCode.Decompiler/IL/Instructions/IfInstruction.cs

@ -38,46 +38,60 @@ namespace ICSharpCode.Decompiler.IL @@ -38,46 +38,60 @@ namespace ICSharpCode.Decompiler.IL
/// </remarks>
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()
{

10
ICSharpCode.Decompiler/IL/Instructions/LdLen.cs

@ -19,11 +19,10 @@ @@ -19,11 +19,10 @@
using System.Diagnostics;
using ICSharpCode.Decompiler.TypeSystem;
namespace ICSharpCode.Decompiler.IL
{
/// <summary>
/// Description of LdLen.
/// </summary>
public sealed partial class LdLen
{
readonly StackType resultType;
@ -39,6 +38,11 @@ namespace ICSharpCode.Decompiler.IL @@ -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);

6
ICSharpCode.Decompiler/IL/Instructions/LogicInstructions.cs

@ -31,6 +31,9 @@ namespace ICSharpCode.Decompiler.IL @@ -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 @@ -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);

6
ICSharpCode.Decompiler/IL/Instructions/NullCoalescingInstruction.cs

@ -56,13 +56,16 @@ namespace ICSharpCode.Decompiler.IL @@ -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 @@ -78,6 +81,7 @@ namespace ICSharpCode.Decompiler.IL
return fallbackInst.ResultType;
}
}
public override IType InferType(ICompilation compilation) => Type;
public override InstructionFlags DirectFlags {
get {

8
ICSharpCode.Decompiler/IL/Instructions/NullableInstructions.cs

@ -65,10 +65,12 @@ namespace ICSharpCode.Decompiler.IL @@ -65,10 +65,12 @@ namespace ICSharpCode.Decompiler.IL
/// RefOutput can only be used if RefInput is also used.
/// </summary>
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 @@ -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 @@ -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)
{

12
ICSharpCode.Decompiler/IL/Instructions/SimpleInstruction.cs

@ -17,6 +17,8 @@ @@ -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
{
/// <summary>
@ -73,6 +75,11 @@ 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 @@ -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);

7
ICSharpCode.Decompiler/IL/Instructions/SwitchInstruction.cs

@ -143,11 +143,12 @@ namespace ICSharpCode.Decompiler.IL @@ -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;
}

26
ICSharpCode.Decompiler/IL/Instructions/TryInstruction.cs

@ -35,6 +35,9 @@ namespace ICSharpCode.Decompiler.IL @@ -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 @@ -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 @@ -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 @@ -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 @@ -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 @@ -386,9 +371,4 @@ namespace ICSharpCode.Decompiler.IL
}
}
}
public partial class Throw
{
internal StackType resultType = StackType.Void;
}
}

7
ICSharpCode.Decompiler/IL/Instructions/UnaryInstruction.cs

@ -39,11 +39,8 @@ namespace ICSharpCode.Decompiler.IL @@ -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)
{

12
ICSharpCode.Decompiler/IL/StackType.cs

@ -67,9 +67,17 @@ namespace ICSharpCode.Decompiler.IL @@ -67,9 +67,17 @@ namespace ICSharpCode.Decompiler.IL
F8,
/// <summary>Another stack type. Includes objects, value types, ...</summary>
O,
/// <summary>A managed pointer</summary>
// TODO: delete O and turn Obj/VT into separate enumerators.
/// <summary>Reference type: class type, boxed value type, etc.</summary>
Obj = O,
/// <summary>
/// A value type other than the primitive types listed above.
/// This includes unconstrained generic types which might be value types at runtime.
/// </summary>
VT = O,
/// <summary>A managed pointer (C# `ref T`, C++/CLI `interior_ptr&lt;T&gt;`)</summary>
Ref,
/// <summary>Represents the lack of a stack slot</summary>
Void
Void,
}
}

2
ICSharpCode.Decompiler/IL/Transforms/CombineExitsTransform.cs

@ -65,7 +65,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -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);

8
ICSharpCode.Decompiler/IL/Transforms/DeconstructionTransform.cs

@ -1147,7 +1147,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -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 @@ -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 @@ -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 @@ -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))
)
);
}

2
ICSharpCode.Decompiler/IL/Transforms/ExpandNestedConditionals.cs

@ -191,7 +191,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -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);

13
ICSharpCode.Decompiler/IL/Transforms/ExpressionTransforms.cs

@ -294,7 +294,9 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -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 @@ -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 @@ -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 @@ -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 @@ -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 @@ -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 @@ -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)

9
ICSharpCode.Decompiler/IL/Transforms/HighLevelLoopTransform.cs

@ -223,11 +223,11 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -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 @@ -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++;
}

15
ICSharpCode.Decompiler/IL/Transforms/NullCoalescingTransform.cs

@ -66,7 +66,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -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 @@ -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 @@ -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 @@ -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 @@ -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 @@ -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;

18
ICSharpCode.Decompiler/IL/Transforms/NullPropagationTransform.cs

@ -115,7 +115,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -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 @@ -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 @@ -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 @@ -154,8 +154,9 @@ namespace ICSharpCode.Decompiler.IL.Transforms
// Span<T> is excluded because it cannot be wrapped in Nullable<T> 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 @@ -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 @@ -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 @@ -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

6
ICSharpCode.Decompiler/IL/Transforms/NullableLiftingTransform.cs

@ -771,7 +771,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -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 @@ -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 @@ -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
};
}

24
ICSharpCode.Decompiler/IL/Transforms/RemoveDeadVariableInit.cs

@ -80,30 +80,6 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -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<StLoc>())
{
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)

8
ICSharpCode.Decompiler/IL/Transforms/TransformExpressionTrees.cs

@ -183,7 +183,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -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 @@ -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 @@ -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<ILInstruction>, IType) ConvertConstant(CallInstruction invocation)
@ -1008,7 +1008,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -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);

2
ICSharpCode.Decompiler/IL/Transforms/UsingTransform.cs

@ -272,7 +272,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -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;
}

16
ICSharpCode.Decompiler/TypeSystem/KnownTypeReference.cs

@ -154,7 +154,15 @@ namespace ICSharpCode.Decompiler.TypeSystem @@ -154,7 +154,15 @@ namespace ICSharpCode.Decompiler.TypeSystem
/// <summary><c>System.Index</c></summary>
Index,
/// <summary><c>System.Range</c></summary>
Range
Range,
/// <summary><c>System.RuntimeArgumentHandle</c></summary>
RuntimeArgumentHandle,
/// <summary><c>System.RuntimeTypeHandle</c></summary>
RuntimeTypeHandle,
/// <summary><c>System.RuntimeFieldHandle</c></summary>
RuntimeFieldHandle,
/// <summary><c>System.RuntimeMethodHandle</c></summary>
RuntimeMethodHandle,
}
/// <summary>
@ -163,7 +171,7 @@ namespace ICSharpCode.Decompiler.TypeSystem @@ -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 @@ -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),
};
/// <summary>

2
ICSharpCode.Decompiler/TypeSystem/ReflectionHelper.cs

@ -51,7 +51,7 @@ namespace ICSharpCode.Decompiler.TypeSystem @@ -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));
}

4
ICSharpCode.Decompiler/TypeSystem/TypeSystemExtensions.cs

@ -310,8 +310,8 @@ namespace ICSharpCode.Decompiler.TypeSystem @@ -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)

Loading…
Cancel
Save