diff --git a/ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs b/ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs index 73dad0a18..bf315eee2 100644 --- a/ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs +++ b/ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs @@ -1448,9 +1448,11 @@ namespace ICSharpCode.Decompiler.CSharp // We need to convert to inst.TargetType, or to an equivalent type. IType targetType; if (inst.TargetType == NullableType.GetUnderlyingType(context.TypeHint).ToPrimitiveType() - && NullableType.IsNullable(context.TypeHint) == inst.IsLifted) - { + && NullableType.IsNullable(context.TypeHint) == inst.IsLifted) { targetType = context.TypeHint; + } else if (inst.TargetType == IL.PrimitiveType.Ref) { + // converting to unknown ref-type + targetType = new ByReferenceType(compilation.FindType(KnownTypeCode.Byte)); } else { targetType = GetType(inst.TargetType.ToKnownTypeCode()); } @@ -1691,7 +1693,7 @@ namespace ICSharpCode.Decompiler.CSharp protected internal override TranslatedExpression VisitLdObj(LdObj inst, TranslationContext context) { var target = Translate(inst.Target); - if (TypeUtils.IsCompatibleTypeForMemoryAccess(target.Type, inst.Type)) { + if (TypeUtils.IsCompatiblePointerTypeForMemoryAccess(target.Type, inst.Type)) { TranslatedExpression result; if (target.Expression is DirectionExpression dirExpr) { // we can dereference the managed reference by stripping away the 'ref' @@ -1735,27 +1737,35 @@ namespace ICSharpCode.Decompiler.CSharp protected internal override TranslatedExpression VisitStObj(StObj inst, TranslationContext context) { - var target = Translate(inst.Target); - TranslatedExpression result; - if (target.Expression is DirectionExpression && TypeUtils.IsCompatibleTypeForMemoryAccess(target.Type, inst.Type)) { + var pointer = Translate(inst.Target); + TranslatedExpression target; + TranslatedExpression value = default; + if (pointer.Expression is DirectionExpression && TypeUtils.IsCompatiblePointerTypeForMemoryAccess(pointer.Type, inst.Type)) { // we can deference the managed reference by stripping away the 'ref' - result = target.UnwrapChild(((DirectionExpression)target.Expression).Expression); + target = pointer.UnwrapChild(((DirectionExpression)pointer.Expression).Expression); } else { // Cast pointer type if necessary: - if (!TypeUtils.IsCompatibleTypeForMemoryAccess(target.Type, inst.Type)) { - target = target.ConvertTo(new PointerType(inst.Type), this); + if (!TypeUtils.IsCompatiblePointerTypeForMemoryAccess(pointer.Type, inst.Type)) { + value = Translate(inst.Value, typeHint: inst.Type); + if (TypeUtils.IsCompatibleTypeForMemoryAccess(value.Type, inst.Type)) { + pointer = pointer.ConvertTo(new PointerType(value.Type), this); + } else { + pointer = pointer.ConvertTo(new PointerType(inst.Type), this); + } } - if (target.Expression is UnaryOperatorExpression uoe && uoe.Operator == UnaryOperatorType.AddressOf) { + if (pointer.Expression is UnaryOperatorExpression uoe && uoe.Operator == UnaryOperatorType.AddressOf) { // *&ptr -> ptr - result = target.UnwrapChild(uoe.Expression); + target = pointer.UnwrapChild(uoe.Expression); } else { - result = new UnaryOperatorExpression(UnaryOperatorType.Dereference, target.Expression) + target = new UnaryOperatorExpression(UnaryOperatorType.Dereference, pointer.Expression) .WithoutILInstruction() - .WithRR(new ResolveResult(((TypeWithElementType)target.Type).ElementType)); + .WithRR(new ResolveResult(((TypeWithElementType)pointer.Type).ElementType)); } } - var value = Translate(inst.Value, typeHint: result.Type); - return Assignment(result, value).WithILInstruction(inst); + if (value.Expression == null) { + value = Translate(inst.Value, typeHint: target.Type); + } + return Assignment(target, value).WithILInstruction(inst); } protected internal override TranslatedExpression VisitLdLen(LdLen inst, TranslationContext context) @@ -1834,7 +1844,7 @@ namespace ICSharpCode.Decompiler.CSharp { TranslatedExpression arrayExpr = Translate(inst.Array); var arrayType = arrayExpr.Type as ArrayType; - if (arrayType == null || !TypeUtils.IsCompatibleTypeForMemoryAccess(new ByReferenceType(arrayType.ElementType), inst.Type)) { + if (arrayType == null || !TypeUtils.IsCompatibleTypeForMemoryAccess(arrayType.ElementType, inst.Type)) { arrayType = new ArrayType(compilation, inst.Type, inst.Indices.Count); arrayExpr = arrayExpr.ConvertTo(arrayType, this); } diff --git a/ICSharpCode.Decompiler/IL/ControlFlow/DetectPinnedRegions.cs b/ICSharpCode.Decompiler/IL/ControlFlow/DetectPinnedRegions.cs index 90941ee64..45be3e101 100644 --- a/ICSharpCode.Decompiler/IL/ControlFlow/DetectPinnedRegions.cs +++ b/ICSharpCode.Decompiler/IL/ControlFlow/DetectPinnedRegions.cs @@ -300,7 +300,11 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow for (int i = 0; i < instructionCount; i++) { foreach (var branch in workItem.Instructions[i].Descendants.OfType()) { if (branch.TargetBlock.Parent == sourceContainer) { - Debug.Assert(branch.TargetBlock != block); + if (branch.TargetBlock == block) { + // pin instruction is within a loop, and can loop around without an unpin instruction + // This should never happen for C#-compiled code, but may happen with C++/CLI code. + return false; + } if (reachedEdgesPerBlock[branch.TargetBlock.ChildIndex]++ == 0) { // detected first edge to that block: add block as work item workList.Enqueue(branch.TargetBlock); @@ -442,7 +446,7 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow if ((inst is LdLoc || inst is StLoc) && !IsSlotAcceptingBothManagedAndUnmanagedPointers(inst.SlotInfo) && oldVar.StackType != StackType.I) { // wrap inst in Conv, so that the stack types match up var children = inst.Parent.Children; - children[inst.ChildIndex] = new Conv(inst, PrimitiveType.I, false, Sign.None); + children[inst.ChildIndex] = new Conv(inst, oldVar.StackType.ToPrimitiveType(), false, Sign.None); } } else if (inst.MatchLdStr(out var val) && val == "Is this ILSpy?") { inst.ReplaceWith(new LdStr("This is ILSpy!")); // easter egg ;) diff --git a/ICSharpCode.Decompiler/IL/ILReader.cs b/ICSharpCode.Decompiler/IL/ILReader.cs index e5896cd5f..eb3a185a0 100644 --- a/ICSharpCode.Decompiler/IL/ILReader.cs +++ b/ICSharpCode.Decompiler/IL/ILReader.cs @@ -202,7 +202,7 @@ namespace ICSharpCode.Decompiler.IL ILVariable CreateILVariable(int index, IType type) { VariableKind kind; - if (type is PinnedType pinned) { + if (type.SkipModifiers() is PinnedType pinned) { kind = VariableKind.PinnedLocal; type = pinned.ElementType; } else { diff --git a/ICSharpCode.Decompiler/IL/ILTypeExtensions.cs b/ICSharpCode.Decompiler/IL/ILTypeExtensions.cs index 25b34961c..14b67f9ba 100644 --- a/ICSharpCode.Decompiler/IL/ILTypeExtensions.cs +++ b/ICSharpCode.Decompiler/IL/ILTypeExtensions.cs @@ -160,9 +160,8 @@ namespace ICSharpCode.Decompiler.IL return new ByReferenceType(ldsflda.Field.Type); case LdElema ldelema: if (ldelema.Array.InferType() is ArrayType arrayType) { - var refType = new ByReferenceType(arrayType.ElementType); - if (TypeUtils.IsCompatibleTypeForMemoryAccess(refType, ldelema.Type)) { - return refType; + if (TypeUtils.IsCompatibleTypeForMemoryAccess(arrayType.ElementType, ldelema.Type)) { + return new ByReferenceType(arrayType.ElementType); } } return new ByReferenceType(ldelema.Type); diff --git a/ICSharpCode.Decompiler/IL/ILVariable.cs b/ICSharpCode.Decompiler/IL/ILVariable.cs index fdb241b13..64d0c6611 100644 --- a/ICSharpCode.Decompiler/IL/ILVariable.cs +++ b/ICSharpCode.Decompiler/IL/ILVariable.cs @@ -64,6 +64,7 @@ namespace ICSharpCode.Decompiler.IL NamedArgument, } + [DebuggerDisplay("{Name} : {Type}")] public class ILVariable { VariableKind kind; diff --git a/ICSharpCode.Decompiler/IL/Transforms/EarlyExpressionTransforms.cs b/ICSharpCode.Decompiler/IL/Transforms/EarlyExpressionTransforms.cs index bba216c13..c9fa38583 100644 --- a/ICSharpCode.Decompiler/IL/Transforms/EarlyExpressionTransforms.cs +++ b/ICSharpCode.Decompiler/IL/Transforms/EarlyExpressionTransforms.cs @@ -52,7 +52,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms internal static bool StObjToStLoc(StObj inst, ILTransformContext context) { if (inst.Target.MatchLdLoca(out ILVariable v) - && TypeUtils.IsCompatibleTypeForMemoryAccess(new ByReferenceType(v.Type), inst.Type) + && TypeUtils.IsCompatibleTypeForMemoryAccess(v.Type, inst.Type) && inst.UnalignedPrefix == 0 && !inst.IsVolatile) { @@ -72,11 +72,11 @@ namespace ICSharpCode.Decompiler.IL.Transforms internal static bool LdObjToLdLoc(LdObj inst, ILTransformContext context) { if (inst.Target.MatchLdLoca(out ILVariable v) - && TypeUtils.IsCompatibleTypeForMemoryAccess(new ByReferenceType(v.Type), inst.Type) + && TypeUtils.IsCompatibleTypeForMemoryAccess(v.Type, inst.Type) && inst.UnalignedPrefix == 0 && !inst.IsVolatile) { - context.Step($"ldobj(ldloca {v.Name}, ...) => ldloc {v.Name}(...)", inst); + context.Step($"ldobj(ldloca {v.Name}) => ldloc {v.Name}", inst); inst.ReplaceWith(new LdLoc(v) { ILRange = inst.ILRange }); return true; } diff --git a/ICSharpCode.Decompiler/IL/Transforms/TransformAssignment.cs b/ICSharpCode.Decompiler/IL/Transforms/TransformAssignment.cs index 3a0c6ae4e..9a941a244 100644 --- a/ICSharpCode.Decompiler/IL/Transforms/TransformAssignment.cs +++ b/ICSharpCode.Decompiler/IL/Transforms/TransformAssignment.cs @@ -112,19 +112,20 @@ namespace ICSharpCode.Decompiler.IL.Transforms return false; if (!SemanticHelper.IsPure(stobj.Target.Flags) || inst.Variable.IsUsedWithin(stobj.Target)) return false; - var newType = stobj.Target.InferType(); - if (newType is ByReferenceType byref) - newType = byref.ElementType; - else if (newType is PointerType pointer) - newType = pointer.ElementType; - else - newType = stobj.Type; + var pointerType = stobj.Target.InferType(); + IType newType = stobj.Type; + if (TypeUtils.IsCompatiblePointerTypeForMemoryAccess(pointerType, stobj.Type)) { + if (pointerType is ByReferenceType byref) + newType = byref.ElementType; + else if (pointerType is PointerType pointer) + newType = pointer.ElementType; + } if (IsImplicitTruncation(inst.Value, newType)) { // 'stobj' is implicitly truncating the value return false; } - stobj.Type = newType; context.Step("Inline assignment stobj", stobj); + stobj.Type = newType; block.Instructions.Remove(localStore); block.Instructions.Remove(stobj); stobj.Value = inst.Value; diff --git a/ICSharpCode.Decompiler/TypeSystem/NullableType.cs b/ICSharpCode.Decompiler/TypeSystem/NullableType.cs index ec3e91281..eae7190c1 100644 --- a/ICSharpCode.Decompiler/TypeSystem/NullableType.cs +++ b/ICSharpCode.Decompiler/TypeSystem/NullableType.cs @@ -32,7 +32,7 @@ namespace ICSharpCode.Decompiler.TypeSystem { if (type == null) throw new ArgumentNullException("type"); - ParameterizedType pt = type as ParameterizedType; + ParameterizedType pt = type.SkipModifiers() as ParameterizedType; return pt != null && pt.TypeParameterCount == 1 && pt.GenericType.IsKnownType(KnownTypeCode.NullableOfT); } @@ -49,7 +49,7 @@ namespace ICSharpCode.Decompiler.TypeSystem { if (type == null) throw new ArgumentNullException("type"); - ParameterizedType pt = type as ParameterizedType; + ParameterizedType pt = type.SkipModifiers() as ParameterizedType; if (pt != null && pt.TypeParameterCount == 1 && pt.GenericType.IsKnownType(KnownTypeCode.NullableOfT)) return pt.GetTypeArgument(0); else diff --git a/ICSharpCode.Decompiler/TypeSystem/TypeUtils.cs b/ICSharpCode.Decompiler/TypeSystem/TypeUtils.cs index 532749d5b..f406aa36e 100644 --- a/ICSharpCode.Decompiler/TypeSystem/TypeUtils.cs +++ b/ICSharpCode.Decompiler/TypeSystem/TypeUtils.cs @@ -192,13 +192,26 @@ namespace ICSharpCode.Decompiler.TypeSystem /// The access semantics may sligthly differ on read accesses of small integer types, /// due to zero extension vs. sign extension when the signs differ. /// - public static bool IsCompatibleTypeForMemoryAccess(IType pointerType, IType accessType) + public static bool IsCompatiblePointerTypeForMemoryAccess(IType pointerType, IType accessType) { IType memoryType; if (pointerType is PointerType || pointerType is ByReferenceType) memoryType = ((TypeWithElementType)pointerType).ElementType; else return false; + return IsCompatibleTypeForMemoryAccess(memoryType, accessType); + } + + /// + /// Gets whether reading/writing an element of accessType from the pointer + /// is equivalent to reading/writing an element of the memoryType. + /// + /// + /// The access semantics may sligthly differ on read accesses of small integer types, + /// due to zero extension vs. sign extension when the signs differ. + /// + public static bool IsCompatibleTypeForMemoryAccess(IType memoryType, IType accessType) + { memoryType = memoryType.AcceptVisitor(NormalizeTypeVisitor.TypeErasure); accessType = accessType.AcceptVisitor(NormalizeTypeVisitor.TypeErasure); if (memoryType.Equals(accessType)) @@ -363,6 +376,7 @@ namespace ICSharpCode.Decompiler.TypeSystem { type = type.SkipModifiers(); if (type.Kind == TypeKind.Unknown) return PrimitiveType.Unknown; + if (type.Kind == TypeKind.ByReference) return PrimitiveType.Ref; var def = type.GetEnumUnderlyingType().GetDefinition(); return def != null ? def.KnownTypeCode.ToPrimitiveType() : PrimitiveType.None; } diff --git a/ILSpy/TextView/DecompilerTextView.cs b/ILSpy/TextView/DecompilerTextView.cs index 5b03e4854..bf5d7b63f 100644 --- a/ILSpy/TextView/DecompilerTextView.cs +++ b/ILSpy/TextView/DecompilerTextView.cs @@ -730,13 +730,8 @@ namespace ICSharpCode.ILSpy.TextView tcs.SetResult(output); } catch (OperationCanceledException) { tcs.SetCanceled(); - #if DEBUG - } catch (AggregateException ex) { - tcs.SetException(ex); - #else } catch (Exception ex) { tcs.SetException(ex); - #endif } })); thread.Start();