diff --git a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/CustomAttributes.cs b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/CustomAttributes.cs index 337ca7df2..d7c58f0c3 100644 --- a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/CustomAttributes.cs +++ b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/CustomAttributes.cs @@ -133,7 +133,14 @@ namespace CustomAttributes 1f, 2.0, "text", - null + null, + typeof(int), + new object[] { + 1 + }, + new int[] { + 1 + } })] public static void BoxedLiteralsArray() { diff --git a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/CustomAttributes.il b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/CustomAttributes.il index f1696db65..0f7bb34bb 100644 --- a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/CustomAttributes.il +++ b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/CustomAttributes.il @@ -321,11 +321,18 @@ .method public hidebysig static void BoxedLiteralsArray() cil managed { - .custom instance void CustomAttributes.CustomAttributes/MyAttribute::.ctor(object) = ( 01 00 1D 51 10 00 00 00 08 01 00 00 00 09 02 00 // ...Q............ + .custom instance void CustomAttributes.CustomAttributes/MyAttribute::.ctor(object) = ( 01 00 1D 51 13 00 00 00 08 01 00 00 00 09 02 00 // ...Q............ 00 00 0A 03 00 00 00 00 00 00 00 0B 04 00 00 00 00 00 00 00 06 05 00 07 06 00 05 07 04 08 03 61 // ...............a 00 03 00 00 03 FF FE 03 FF FF 0C 00 00 80 3F 0D // ..............?. 00 00 00 00 00 00 00 40 0E 04 74 65 78 74 0E FF // .......@..text.. + 50 59 53 79 73 74 65 6D 2E 49 6E 74 33 32 2C 20 // PYSystem.Int32, + 6D 73 63 6F 72 6C 69 62 2C 20 56 65 72 73 69 6F // mscorlib, Versio + 6E 3D 34 2E 30 2E 30 2E 30 2C 20 43 75 6C 74 75 // n=4.0.0.0, Cultu + 72 65 3D 6E 65 75 74 72 61 6C 2C 20 50 75 62 6C // re=neutral, Publ + 69 63 4B 65 79 54 6F 6B 65 6E 3D 62 37 37 61 35 // icKeyToken=b77a5 + 63 35 36 31 39 33 34 65 30 38 39 1D 51 01 00 00 // c561934e089.Q... + 00 08 01 00 00 00 1D 08 01 00 00 00 01 00 00 00 00 00 ) // Code size 2 (0x2) .maxstack 8 diff --git a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/CustomAttributes.opt.il b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/CustomAttributes.opt.il index fd63a9ec3..fc4ce3829 100644 --- a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/CustomAttributes.opt.il +++ b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/CustomAttributes.opt.il @@ -306,11 +306,18 @@ .method public hidebysig static void BoxedLiteralsArray() cil managed { - .custom instance void CustomAttributes.CustomAttributes/MyAttribute::.ctor(object) = ( 01 00 1D 51 10 00 00 00 08 01 00 00 00 09 02 00 // ...Q............ + .custom instance void CustomAttributes.CustomAttributes/MyAttribute::.ctor(object) = ( 01 00 1D 51 13 00 00 00 08 01 00 00 00 09 02 00 // ...Q............ 00 00 0A 03 00 00 00 00 00 00 00 0B 04 00 00 00 00 00 00 00 06 05 00 07 06 00 05 07 04 08 03 61 // ...............a 00 03 00 00 03 FF FE 03 FF FF 0C 00 00 80 3F 0D // ..............?. 00 00 00 00 00 00 00 40 0E 04 74 65 78 74 0E FF // .......@..text.. + 50 59 53 79 73 74 65 6D 2E 49 6E 74 33 32 2C 20 // PYSystem.Int32, + 6D 73 63 6F 72 6C 69 62 2C 20 56 65 72 73 69 6F // mscorlib, Versio + 6E 3D 34 2E 30 2E 30 2E 30 2C 20 43 75 6C 74 75 // n=4.0.0.0, Cultu + 72 65 3D 6E 65 75 74 72 61 6C 2C 20 50 75 62 6C // re=neutral, Publ + 69 63 4B 65 79 54 6F 6B 65 6E 3D 62 37 37 61 35 // icKeyToken=b77a5 + 63 35 36 31 39 33 34 65 30 38 39 1D 51 01 00 00 // c561934e089.Q... + 00 08 01 00 00 00 1D 08 01 00 00 00 01 00 00 00 00 00 ) // Code size 1 (0x1) .maxstack 8 diff --git a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/CustomAttributes.opt.roslyn.il b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/CustomAttributes.opt.roslyn.il index 50c786b4d..6336dbada 100644 --- a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/CustomAttributes.opt.roslyn.il +++ b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/CustomAttributes.opt.roslyn.il @@ -310,11 +310,18 @@ .method public hidebysig static void BoxedLiteralsArray() cil managed { - .custom instance void CustomAttributes.CustomAttributes/MyAttribute::.ctor(object) = ( 01 00 1D 51 10 00 00 00 08 01 00 00 00 09 02 00 // ...Q............ + .custom instance void CustomAttributes.CustomAttributes/MyAttribute::.ctor(object) = ( 01 00 1D 51 13 00 00 00 08 01 00 00 00 09 02 00 // ...Q............ 00 00 0A 03 00 00 00 00 00 00 00 0B 04 00 00 00 00 00 00 00 06 05 00 07 06 00 05 07 04 08 03 61 // ...............a 00 03 00 00 03 FF FE 03 FF FF 0C 00 00 80 3F 0D // ..............?. 00 00 00 00 00 00 00 40 0E 04 74 65 78 74 0E FF // .......@..text.. + 50 59 53 79 73 74 65 6D 2E 49 6E 74 33 32 2C 20 // PYSystem.Int32, + 6D 73 63 6F 72 6C 69 62 2C 20 56 65 72 73 69 6F // mscorlib, Versio + 6E 3D 34 2E 30 2E 30 2E 30 2C 20 43 75 6C 74 75 // n=4.0.0.0, Cultu + 72 65 3D 6E 65 75 74 72 61 6C 2C 20 50 75 62 6C // re=neutral, Publ + 69 63 4B 65 79 54 6F 6B 65 6E 3D 62 37 37 61 35 // icKeyToken=b77a5 + 63 35 36 31 39 33 34 65 30 38 39 1D 51 01 00 00 // c561934e089.Q... + 00 08 01 00 00 00 1D 08 01 00 00 00 01 00 00 00 00 00 ) // Code size 1 (0x1) .maxstack 8 diff --git a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/CustomAttributes.roslyn.il b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/CustomAttributes.roslyn.il index 6fb822d67..124951868 100644 --- a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/CustomAttributes.roslyn.il +++ b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/CustomAttributes.roslyn.il @@ -325,11 +325,18 @@ .method public hidebysig static void BoxedLiteralsArray() cil managed { - .custom instance void CustomAttributes.CustomAttributes/MyAttribute::.ctor(object) = ( 01 00 1D 51 10 00 00 00 08 01 00 00 00 09 02 00 // ...Q............ + .custom instance void CustomAttributes.CustomAttributes/MyAttribute::.ctor(object) = ( 01 00 1D 51 13 00 00 00 08 01 00 00 00 09 02 00 // ...Q............ 00 00 0A 03 00 00 00 00 00 00 00 0B 04 00 00 00 00 00 00 00 06 05 00 07 06 00 05 07 04 08 03 61 // ...............a 00 03 00 00 03 FF FE 03 FF FF 0C 00 00 80 3F 0D // ..............?. 00 00 00 00 00 00 00 40 0E 04 74 65 78 74 0E FF // .......@..text.. + 50 59 53 79 73 74 65 6D 2E 49 6E 74 33 32 2C 20 // PYSystem.Int32, + 6D 73 63 6F 72 6C 69 62 2C 20 56 65 72 73 69 6F // mscorlib, Versio + 6E 3D 34 2E 30 2E 30 2E 30 2C 20 43 75 6C 74 75 // n=4.0.0.0, Cultu + 72 65 3D 6E 65 75 74 72 61 6C 2C 20 50 75 62 6C // re=neutral, Publ + 69 63 4B 65 79 54 6F 6B 65 6E 3D 62 37 37 61 35 // icKeyToken=b77a5 + 63 35 36 31 39 33 34 65 30 38 39 1D 51 01 00 00 // c561934e089.Q... + 00 08 01 00 00 00 1D 08 01 00 00 00 01 00 00 00 00 00 ) // Code size 2 (0x2) .maxstack 8 diff --git a/ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj b/ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj index e60af3e58..c39595807 100644 --- a/ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj +++ b/ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj @@ -496,7 +496,7 @@ - + diff --git a/ICSharpCode.Decompiler/IL/Transforms/SplitVariables.cs b/ICSharpCode.Decompiler/IL/Transforms/SplitVariables.cs index e45a7a593..838ee598f 100644 --- a/ICSharpCode.Decompiler/IL/Transforms/SplitVariables.cs +++ b/ICSharpCode.Decompiler/IL/Transforms/SplitVariables.cs @@ -21,6 +21,7 @@ using System.Linq; using ICSharpCode.Decompiler.FlowAnalysis; using ICSharpCode.Decompiler.Util; using System.Threading; +using ICSharpCode.Decompiler.TypeSystem; namespace ICSharpCode.Decompiler.IL.Transforms { @@ -48,7 +49,9 @@ namespace ICSharpCode.Decompiler.IL.Transforms case VariableKind.Local: case VariableKind.Exception: foreach (var ldloca in v.AddressInstructions) { - if (!AddressUsedOnlyForReading(ldloca)) { + if (DetermineAddressUse(ldloca) == AddressUse.Unknown) { + // If the address isn't used immediately, + // we'd need to deal with aliases. return false; } } @@ -67,29 +70,46 @@ namespace ICSharpCode.Decompiler.IL.Transforms } } - static bool AddressUsedOnlyForReading(ILInstruction addressLoadingInstruction) + enum AddressUse + { + Unknown, + LocalRead, + LocalReadWrite + } + + static AddressUse DetermineAddressUse(ILInstruction addressLoadingInstruction) { switch (addressLoadingInstruction.Parent) { case LdObj ldobj: - return true; + return AddressUse.LocalRead; case LdFlda ldflda: - return AddressUsedOnlyForReading(ldflda); + return DetermineAddressUse(ldflda); case Await await: - // Not strictly true as GetAwaiter() could have side-effects, - // but we need to split awaiter variables to make async/await pretty. - return true; + // GetAwaiter() may write to the struct, but shouldn't store the address for later use + return AddressUse.LocalReadWrite; case Call call: - if (call.Method.DeclaringTypeDefinition?.KnownTypeCode == TypeSystem.KnownTypeCode.NullableOfT) { - switch (call.Method.Name) { - case "get_HasValue": - case "get_Value": - case "GetValueOrDefault": - return true; - } + // Address is passed to method. + // We'll assume the method only uses the address locally, + // unless we can see an address being returned from the method: + if (call.Method.ReturnType.IsByRefLike) { + return AddressUse.Unknown; } - return false; + foreach (var p in call.Method.Parameters) { + // catch "out Span" and similar + if (p.Type.SkipModifiers() is ByReferenceType brt && brt.ElementType.IsByRefLike) + return AddressUse.Unknown; + } + var addrParam = call.GetParameter(addressLoadingInstruction.ChildIndex); + bool isReadOnly; + if (addrParam == null) { + isReadOnly = (call.Method.DeclaringTypeDefinition?.HasAttribute(KnownAttribute.IsReadOnly) ?? false) + || (call.Method.DeclaringType?.IsKnownType(KnownTypeCode.NullableOfT) ?? false); + } else { + isReadOnly = false; + } + return isReadOnly ? AddressUse.LocalRead : AddressUse.Unknown; // TODO AddressUse.LocalReadWrite; default: - return false; + return AddressUse.Unknown; } } diff --git a/ICSharpCode.Decompiler/TypeSystem/ByReferenceType.cs b/ICSharpCode.Decompiler/TypeSystem/ByReferenceType.cs index 9c4587b40..592fe53c2 100644 --- a/ICSharpCode.Decompiler/TypeSystem/ByReferenceType.cs +++ b/ICSharpCode.Decompiler/TypeSystem/ByReferenceType.cs @@ -40,7 +40,9 @@ namespace ICSharpCode.Decompiler.TypeSystem public override bool? IsReferenceType { get { return null; } } - + + public override bool IsByRefLike => true; + public override int GetHashCode() { return elementType.GetHashCode() ^ 91725813; diff --git a/ICSharpCode.Decompiler/TypeSystem/IType.cs b/ICSharpCode.Decompiler/TypeSystem/IType.cs index b2018d7cd..36c4e0a37 100644 --- a/ICSharpCode.Decompiler/TypeSystem/IType.cs +++ b/ICSharpCode.Decompiler/TypeSystem/IType.cs @@ -64,6 +64,11 @@ namespace ICSharpCode.Decompiler.TypeSystem /// bool? IsReferenceType { get; } + /// + /// Gets whether this type is "ref-like": a ByReferenceType or "ref struct". + /// + bool IsByRefLike { get; } + /// /// Gets the underlying type definition. /// Can return null for types which do not have a type definition (for example arrays, pointers, type parameters). diff --git a/ICSharpCode.Decompiler/TypeSystem/Implementation/AbstractType.cs b/ICSharpCode.Decompiler/TypeSystem/Implementation/AbstractType.cs index 79c387dd8..ed677aac4 100644 --- a/ICSharpCode.Decompiler/TypeSystem/Implementation/AbstractType.cs +++ b/ICSharpCode.Decompiler/TypeSystem/Implementation/AbstractType.cs @@ -52,7 +52,9 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation } public abstract bool? IsReferenceType { get; } - + + public virtual bool IsByRefLike => false; + public abstract TypeKind Kind { get; } public virtual int TypeParameterCount { diff --git a/ICSharpCode.Decompiler/TypeSystem/Implementation/AbstractResolvedTypeParameter.cs b/ICSharpCode.Decompiler/TypeSystem/Implementation/AbstractTypeParameter.cs similarity index 99% rename from ICSharpCode.Decompiler/TypeSystem/Implementation/AbstractResolvedTypeParameter.cs rename to ICSharpCode.Decompiler/TypeSystem/Implementation/AbstractTypeParameter.cs index 08f18e4d2..565c81629 100644 --- a/ICSharpCode.Decompiler/TypeSystem/Implementation/AbstractResolvedTypeParameter.cs +++ b/ICSharpCode.Decompiler/TypeSystem/Implementation/AbstractTypeParameter.cs @@ -190,6 +190,8 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation return null; } } + + bool IType.IsByRefLike => false; IType IType.DeclaringType { get { return null; } diff --git a/ICSharpCode.Decompiler/TypeSystem/Implementation/MetadataTypeDefinition.cs b/ICSharpCode.Decompiler/TypeSystem/Implementation/MetadataTypeDefinition.cs index b9c581536..ec6fd0abd 100644 --- a/ICSharpCode.Decompiler/TypeSystem/Implementation/MetadataTypeDefinition.cs +++ b/ICSharpCode.Decompiler/TypeSystem/Implementation/MetadataTypeDefinition.cs @@ -44,6 +44,7 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation readonly FullTypeName fullTypeName; readonly TypeAttributes attributes; public TypeKind Kind { get; } + public bool IsByRefLike { get; } public ITypeDefinition DeclaringTypeDefinition { get; } public IReadOnlyList TypeParameters { get; } public KnownTypeCode KnownTypeCode { get; } @@ -99,6 +100,7 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation this.Kind = TypeKind.Void; } else { this.Kind = TypeKind.Struct; + this.IsByRefLike = td.GetCustomAttributes().HasKnownAttribute(metadata, KnownAttribute.IsByRefLike); } } else if (td.IsDelegate(metadata)) { this.Kind = TypeKind.Delegate; diff --git a/ICSharpCode.Decompiler/TypeSystem/Implementation/MinimalCorlib.cs b/ICSharpCode.Decompiler/TypeSystem/Implementation/MinimalCorlib.cs index 3b2ad26c5..09ae63f79 100644 --- a/ICSharpCode.Decompiler/TypeSystem/Implementation/MinimalCorlib.cs +++ b/ICSharpCode.Decompiler/TypeSystem/Implementation/MinimalCorlib.cs @@ -172,6 +172,8 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation } } + bool IType.IsByRefLike => false; + int IType.TypeParameterCount => KnownTypeReference.Get(typeCode).TypeParameterCount; IReadOnlyList IType.TypeParameters => DummyTypeParameter.GetClassTypeParameterList(KnownTypeReference.Get(typeCode).TypeParameterCount); diff --git a/ICSharpCode.Decompiler/TypeSystem/Implementation/TypeSpecification.cs b/ICSharpCode.Decompiler/TypeSystem/Implementation/TypeSpecification.cs index 7951a5275..c87f28f16 100644 --- a/ICSharpCode.Decompiler/TypeSystem/Implementation/TypeSpecification.cs +++ b/ICSharpCode.Decompiler/TypeSystem/Implementation/TypeSpecification.cs @@ -37,6 +37,7 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation public override string NameSuffix => " pinned"; public override bool? IsReferenceType => elementType.IsReferenceType; + public override bool IsByRefLike => elementType.IsByRefLike; public override TypeKind Kind => TypeKind.Other; diff --git a/ICSharpCode.Decompiler/TypeSystem/ModifiedType.cs b/ICSharpCode.Decompiler/TypeSystem/ModifiedType.cs index bcc1fab4a..b813bd32f 100644 --- a/ICSharpCode.Decompiler/TypeSystem/ModifiedType.cs +++ b/ICSharpCode.Decompiler/TypeSystem/ModifiedType.cs @@ -41,6 +41,7 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation public override string NameSuffix => (kind == TypeKind.ModReq ? "modreq" : "modopt") + $"({modifier.FullName})"; public override bool? IsReferenceType => elementType.IsReferenceType; + public override bool IsByRefLike => elementType.IsByRefLike; public override ITypeDefinition GetDefinition() { diff --git a/ICSharpCode.Decompiler/TypeSystem/ParameterizedType.cs b/ICSharpCode.Decompiler/TypeSystem/ParameterizedType.cs index 2ac234e46..0f7de4201 100644 --- a/ICSharpCode.Decompiler/TypeSystem/ParameterizedType.cs +++ b/ICSharpCode.Decompiler/TypeSystem/ParameterizedType.cs @@ -82,9 +82,8 @@ namespace ICSharpCode.Decompiler.TypeSystem get { return genericType; } } - public bool? IsReferenceType { - get { return genericType.IsReferenceType; } - } + public bool? IsReferenceType => genericType.IsReferenceType; + public bool IsByRefLike => genericType.IsByRefLike; public IType DeclaringType { get { diff --git a/ICSharpCode.Decompiler/TypeSystem/TypeSystemExtensions.cs b/ICSharpCode.Decompiler/TypeSystem/TypeSystemExtensions.cs index 529d3a38d..c7ab7f016 100644 --- a/ICSharpCode.Decompiler/TypeSystem/TypeSystemExtensions.cs +++ b/ICSharpCode.Decompiler/TypeSystem/TypeSystemExtensions.cs @@ -255,6 +255,14 @@ namespace ICSharpCode.Decompiler.TypeSystem } #endregion + public static IType SkipModifiers(this IType ty) + { + while (ty is ModifiedType mt) { + ty = mt.ElementType; + } + return ty; + } + #region GetType/Member /// /// Gets all type definitions in the compilation.