Skip to content

Commit

Permalink
ILReader: handle implicit O->Ref conversions
Browse files Browse the repository at this point in the history
  • Loading branch information
dgrunwald committed Nov 12, 2017
1 parent ed8fc0d commit 9a829a2
Show file tree
Hide file tree
Showing 4 changed files with 38 additions and 6 deletions.
5 changes: 3 additions & 2 deletions ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
20 changes: 17 additions & 3 deletions ICSharpCode.Decompiler/IL/ILReader.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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;
Expand Down Expand Up @@ -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}");
Expand Down Expand Up @@ -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);
Expand Down
14 changes: 13 additions & 1 deletion ICSharpCode.Decompiler/IL/Instructions/Conv.cs
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,15 @@ public enum ConversionKind : byte
/// <summary>
/// Used to convert unmanaged pointers to managed references.
/// </summary>
StartGCTracking
StartGCTracking,
/// <summary>
/// Converts from an object reference (O) to an interior pointer (Ref) pointing to the start of the object.
/// </summary>
/// <remarks>
/// 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.
/// </remarks>
ObjectInterior
}

partial class Conv : UnaryInstruction, ILiftableInstruction
Expand Down Expand Up @@ -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;
}
Expand Down
5 changes: 5 additions & 0 deletions ICSharpCode.Decompiler/TypeSystem/DecompilerTypeSystem.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
Expand Down

0 comments on commit 9a829a2

Please sign in to comment.