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.