From 9a829a28200bcb1e6aa66800b4a379e0f2968a4d Mon Sep 17 00:00:00 2001 From: Daniel Grunwald Date: Sun, 12 Nov 2017 21:22:07 +0100 Subject: [PATCH] ILReader: handle implicit O->Ref conversions --- .../CSharp/ExpressionBuilder.cs | 5 +++-- ICSharpCode.Decompiler/IL/ILReader.cs | 20 ++++++++++++++++--- .../IL/Instructions/Conv.cs | 14 ++++++++++++- .../TypeSystem/DecompilerTypeSystem.cs | 5 +++++ 4 files changed, 38 insertions(+), 6 deletions(-) diff --git a/ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs b/ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs index c29cd893b8..2c729e3901 100644 --- a/ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs +++ b/ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs @@ -1236,8 +1236,9 @@ IType GetType(KnownTypeCode typeCode) var pointerType = new PointerType(((ByReferenceType)inputType).ElementType); return arg.ConvertTo(pointerType, this).WithILInstruction(inst); } else { - Debug.Fail("ConversionKind.StopGCTracking should only be used with managed references"); - goto default; + // ConversionKind.StopGCTracking should only be used with managed references, + // but it's possible that we're supposed to stop tracking something we just started to track. + return arg; } case ConversionKind.SignExtend: // We just need to ensure the input type before the conversion is signed. diff --git a/ICSharpCode.Decompiler/IL/ILReader.cs b/ICSharpCode.Decompiler/IL/ILReader.cs index 16d564bae2..6ba846dc97 100644 --- a/ICSharpCode.Decompiler/IL/ILReader.cs +++ b/ICSharpCode.Decompiler/IL/ILReader.cs @@ -129,7 +129,7 @@ void InitParameterVariables() ILVariable CreateILVariable(Cil.VariableDefinition v) { - VariableKind kind = v.IsPinned ? VariableKind.PinnedLocal : VariableKind.Local; + VariableKind kind = IsPinned(v.VariableType) ? VariableKind.PinnedLocal : VariableKind.Local; ILVariable ilVar = new ILVariable(kind, typeSystem.Resolve(v.VariableType), v.Index); if (!UseDebugSymbols || debugInfo == null || !debugInfo.TryGetName(v, out string name)) { ilVar.Name = "V_" + v.Index; @@ -143,6 +143,14 @@ ILVariable CreateILVariable(Cil.VariableDefinition v) return ilVar; } + bool IsPinned(TypeReference type) + { + while (type is OptionalModifierType || type is RequiredModifierType) { + type = ((TypeSpecification)type).ElementType; + } + return type is PinnedType; + } + ILVariable CreateILVariable(ParameterDefinition p) { IType parameterType; @@ -1028,8 +1036,8 @@ ILInstruction Pop(StackType expectedType) // to a method expecting a native pointer. inst = new Conv(inst, PrimitiveType.I, false, Sign.None); } else if (expectedType == StackType.Ref) { - // implicitly start GC tracking - if (!inst.ResultType.IsIntegerType()) { + // implicitly start GC tracking / object to interior + if (!inst.ResultType.IsIntegerType() && inst.ResultType != StackType.O) { // We also handle the invalid to-ref cases here because the else case // below uses expectedType.ToKnownTypeCode(), which doesn't work for Ref. Warn($"Expected {expectedType}, but got {inst.ResultType}"); @@ -1332,6 +1340,12 @@ ILInstruction DecodeConditionalBranch(bool shortForm, bool negate) negate ? ComparisonKind.Equality : ComparisonKind.Inequality, Sign.None, condition, new LdcI8(0)); break; + case StackType.Ref: + // introduce explicit comparison with null ref + condition = new Comp( + negate ? ComparisonKind.Equality : ComparisonKind.Inequality, + Sign.None, new Conv(condition, PrimitiveType.I, false, Sign.None), new Conv(new LdcI4(0), PrimitiveType.I, false, Sign.None)); + break; default: if (negate) { condition = Comp.LogicNot(condition); diff --git a/ICSharpCode.Decompiler/IL/Instructions/Conv.cs b/ICSharpCode.Decompiler/IL/Instructions/Conv.cs index 5f6c26add2..9562466fac 100644 --- a/ICSharpCode.Decompiler/IL/Instructions/Conv.cs +++ b/ICSharpCode.Decompiler/IL/Instructions/Conv.cs @@ -79,7 +79,15 @@ public enum ConversionKind : byte /// /// Used to convert unmanaged pointers to managed references. /// - StartGCTracking + StartGCTracking, + /// + /// Converts from an object reference (O) to an interior pointer (Ref) pointing to the start of the object. + /// + /// + /// C++/CLI emits "ldarg.1; stloc.0" where arg1 is a string and loc0 is "ref byte" (e.g. as part of the PtrToStringChars codegen); + /// we represent this type conversion explicitly in the ILAst. + /// + ObjectInterior } partial class Conv : UnaryInstruction, ILiftableInstruction @@ -250,11 +258,15 @@ static ConversionKind GetConversionKind(PrimitiveType targetType, StackType inpu return ConversionKind.Invalid; } case PrimitiveType.Ref: + // There's no "conv.ref" in IL, but IL allows these conversions implicitly, + // whereas we represent them explicitly in the ILAst. switch (inputType) { case StackType.I4: case StackType.I: case StackType.I8: return ConversionKind.StartGCTracking; + case StackType.O: + return ConversionKind.ObjectInterior; default: return ConversionKind.Invalid; } diff --git a/ICSharpCode.Decompiler/TypeSystem/DecompilerTypeSystem.cs b/ICSharpCode.Decompiler/TypeSystem/DecompilerTypeSystem.cs index 7cc9960731..25c06d8a92 100644 --- a/ICSharpCode.Decompiler/TypeSystem/DecompilerTypeSystem.cs +++ b/ICSharpCode.Decompiler/TypeSystem/DecompilerTypeSystem.cs @@ -132,6 +132,11 @@ public IType Resolve(TypeReference typeReference) { if (typeReference == null) return SpecialType.UnknownType; + // We need to skip SentinelType and PinnedType. + // But PinnedType can be nested within modopt, so we'll also skip those. + while (typeReference is OptionalModifierType || typeReference is RequiredModifierType) { + typeReference = ((TypeSpecification)typeReference).ElementType; + } if (typeReference is SentinelType || typeReference is PinnedType) { typeReference = ((TypeSpecification)typeReference).ElementType; }