Browse Source

Add support for 'ref readonly' return type.

pull/1471/head
Daniel Grunwald 6 years ago
parent
commit
8d99af14c5
  1. 63
      ICSharpCode.Decompiler.Tests/TestCases/Pretty/RefLocalsAndReturns.cs
  2. 3
      ICSharpCode.Decompiler/CSharp/OutputVisitor/CSharpOutputVisitor.cs
  3. 1
      ICSharpCode.Decompiler/CSharp/Resolver/ReducedExtensionMethod.cs
  4. 18
      ICSharpCode.Decompiler/CSharp/Syntax/ComposedType.cs
  5. 3
      ICSharpCode.Decompiler/CSharp/Syntax/TypeSystemAstBuilder.cs
  6. 2
      ICSharpCode.Decompiler/IL/Transforms/ILInlining.cs
  7. 5
      ICSharpCode.Decompiler/TypeSystem/IMethod.cs
  8. 1
      ICSharpCode.Decompiler/TypeSystem/Implementation/FakeMember.cs
  9. 25
      ICSharpCode.Decompiler/TypeSystem/Implementation/MetadataMethod.cs
  10. 3
      ICSharpCode.Decompiler/TypeSystem/Implementation/SpecializedMethod.cs
  11. 1
      ICSharpCode.Decompiler/TypeSystem/VarArgInstanceMethod.cs

63
ICSharpCode.Decompiler.Tests/TestCases/Pretty/RefLocalsAndReturns.cs

@ -1,4 +1,6 @@ @@ -1,4 +1,6 @@
namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty
using System;
namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty
{
internal class RefLocalsAndReturns
{
@ -12,9 +14,68 @@ @@ -12,9 +14,68 @@
private readonly int dummy;
}
public struct NormalStruct
{
private readonly int dummy;
public void Method()
{
}
}
public readonly struct ReadOnlyStruct
{
private readonly int dummy;
public void Method()
{
}
}
public static ref T GetRef<T>()
{
throw new NotImplementedException();
}
public static ref readonly T GetReadonlyRef<T>()
{
throw new NotImplementedException();
}
public void CallOnRefReturn()
{
// Both direct calls:
GetRef<NormalStruct>().Method();
GetRef<ReadOnlyStruct>().Method();
// call on a copy, not the original ref:
NormalStruct @ref = GetRef<NormalStruct>();
@ref.Method();
ReadOnlyStruct ref2 = GetRef<ReadOnlyStruct>();
ref2.Method();
}
public void CallOnReadOnlyRefReturn()
{
// uses implicit temporary:
GetReadonlyRef<NormalStruct>().Method();
// direct call:
GetReadonlyRef<ReadOnlyStruct>().Method();
// call on a copy, not the original ref:
ReadOnlyStruct readonlyRef = GetReadonlyRef<ReadOnlyStruct>();
readonlyRef.Method();
}
public void CallOnInParam(in NormalStruct ns, in ReadOnlyStruct rs)
{
// uses implicit temporary:
ns.Method();
// direct call:
rs.Method();
// call on a copy, not the original ref:
ReadOnlyStruct readOnlyStruct = rs;
readOnlyStruct.Method();
}
}
}

3
ICSharpCode.Decompiler/CSharp/OutputVisitor/CSharpOutputVisitor.cs

@ -2322,6 +2322,9 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor @@ -2322,6 +2322,9 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor
if (composedType.HasRefSpecifier) {
WriteKeyword(ComposedType.RefRole);
}
if (composedType.HasReadOnlySpecifier) {
WriteKeyword(ComposedType.ReadonlyRole);
}
composedType.BaseType.AcceptVisitor(this);
if (composedType.HasNullableSpecifier) {
WriteToken(ComposedType.NullableRole);

1
ICSharpCode.Decompiler/CSharp/Resolver/ReducedExtensionMethod.cs

@ -232,6 +232,7 @@ namespace ICSharpCode.Decompiler.CSharp.Resolver @@ -232,6 +232,7 @@ namespace ICSharpCode.Decompiler.CSharp.Resolver
IEnumerable<IAttribute> IEntity.GetAttributes() => baseMethod.GetAttributes();
IEnumerable<IAttribute> IMethod.GetReturnTypeAttributes() => baseMethod.GetReturnTypeAttributes();
bool IMethod.ReturnTypeIsRefReadOnly => baseMethod.ReturnTypeIsRefReadOnly;
public bool IsStatic {
get {

18
ICSharpCode.Decompiler/CSharp/Syntax/ComposedType.cs

@ -36,6 +36,7 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax @@ -36,6 +36,7 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax
public class ComposedType : AstType
{
public static readonly TokenRole RefRole = new TokenRole("ref");
public static readonly TokenRole ReadonlyRole = new TokenRole("readonly");
public static readonly TokenRole NullableRole = new TokenRole("?");
public static readonly TokenRole PointerRole = new TokenRole("*");
public static readonly Role<ArraySpecifier> ArraySpecifierRole = new Role<ArraySpecifier>("ArraySpecifier");
@ -54,6 +55,20 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax @@ -54,6 +55,20 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax
}
}
/// <summary>
/// Gets/sets whether this type has a 'readonly' specifier.
/// This is used for C# 7.2 'ref readonly' locals/ref return.
/// Parameters use ParameterDeclaration.ParameterModifier instead.
/// </summary>
public bool HasReadOnlySpecifier {
get {
return !GetChildByRole(ReadonlyRole).IsNull;
}
set {
SetChildByRole(ReadonlyRole, value ? new CSharpTokenNode(TextLocation.Empty, null) : null);
}
}
public AstType BaseType {
get { return GetChildByRole(Roles.Type); }
set { SetChildByRole(Roles.Type, value); }
@ -123,6 +138,7 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax @@ -123,6 +138,7 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax
&& this.HasNullableSpecifier == o.HasNullableSpecifier
&& this.PointerRank == o.PointerRank
&& this.HasRefSpecifier == o.HasRefSpecifier
&& this.HasReadOnlySpecifier == o.HasReadOnlySpecifier
&& this.BaseType.DoMatch(o.BaseType, match)
&& this.ArraySpecifiers.DoMatch(o.ArraySpecifiers, match);
}
@ -132,6 +148,8 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax @@ -132,6 +148,8 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax
StringBuilder b = new StringBuilder();
if (this.HasRefSpecifier)
b.Append("ref ");
if (this.HasReadOnlySpecifier)
b.Append("readonly ");
b.Append(this.BaseType.ToString());
if (this.HasNullableSpecifier)
b.Append('?');

3
ICSharpCode.Decompiler/CSharp/Syntax/TypeSystemAstBuilder.cs

@ -1602,6 +1602,9 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax @@ -1602,6 +1602,9 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax
decl.AddAnnotation(new MemberResolveResult(null, method));
}
decl.ReturnType = ConvertType(method.ReturnType);
if (method.ReturnTypeIsRefReadOnly && decl.ReturnType is ComposedType ct && ct.HasRefSpecifier) {
ct.HasReadOnlySpecifier = true;
}
decl.Name = method.Name;
if (this.ShowTypeParameters) {

2
ICSharpCode.Decompiler/IL/Transforms/ILInlining.cs

@ -366,7 +366,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -366,7 +366,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms
case LdLoc ldloc:
return IsReadonlyRefLocal(ldloc.Variable);
case Call call:
// TODO: calls with 'readonly ref' return
return call.Method.ReturnTypeIsRefReadOnly;
default:
return false;
}

5
ICSharpCode.Decompiler/TypeSystem/IMethod.cs

@ -35,6 +35,11 @@ namespace ICSharpCode.Decompiler.TypeSystem @@ -35,6 +35,11 @@ namespace ICSharpCode.Decompiler.TypeSystem
/// </remarks>
IEnumerable<IAttribute> GetReturnTypeAttributes();
/// <summary>
/// Gets whether the return type is 'ref readonly'.
/// </summary>
bool ReturnTypeIsRefReadOnly { get; }
/// <summary>
/// Gets the type parameters of this method; or an empty list if the method is not generic.
/// </summary>

1
ICSharpCode.Decompiler/TypeSystem/Implementation/FakeMember.cs

@ -132,6 +132,7 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation @@ -132,6 +132,7 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation
public override SymbolKind SymbolKind => symbolKind;
IEnumerable<IAttribute> IMethod.GetReturnTypeAttributes() => EmptyList<IAttribute>.Instance;
bool IMethod.ReturnTypeIsRefReadOnly => false;
public IReadOnlyList<ITypeParameter> TypeParameters { get; set; } = EmptyList<ITypeParameter>.Instance;

25
ICSharpCode.Decompiler/TypeSystem/Implementation/MetadataMethod.cs

@ -47,6 +47,7 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation @@ -47,6 +47,7 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation
string name;
IParameter[] parameters;
IType returnType;
byte returnTypeIsRefReadonly = ThreeState.Unknown;
internal MetadataMethod(MetadataModule module, MethodDefinitionHandle handle)
{
@ -203,6 +204,10 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation @@ -203,6 +204,10 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation
i++;
}
Debug.Assert(i == parameters.Length);
bool isRefReadonly = false;
if (signature.ReturnType.Kind == TypeKind.ModReq && signature.ReturnType.SkipModifiers().Kind == TypeKind.ByReference) {
isRefReadonly = ((ModifiedType)signature.ReturnType).Modifier.IsKnownType(KnownAttribute.In);
}
var returnType = ApplyAttributeTypeVisitor.ApplyAttributesToType(signature.ReturnType,
module.Compilation, returnTypeAttributes, metadata, module.TypeSystemOptions);
return (returnType, parameters);
@ -387,6 +392,26 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation @@ -387,6 +392,26 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation
}
return b.Build();
}
public bool ReturnTypeIsRefReadOnly {
get {
if (returnTypeIsRefReadonly != ThreeState.Unknown) {
return returnTypeIsRefReadonly == ThreeState.True;
}
var metadata = module.metadata;
var methodDefinition = metadata.GetMethodDefinition(handle);
var parameters = methodDefinition.GetParameters();
bool hasReadOnlyAttr = false;
if (parameters.Count > 0) {
var retParam = metadata.GetParameter(parameters.First());
if (retParam.SequenceNumber == 0) {
hasReadOnlyAttr = retParam.GetCustomAttributes().HasKnownAttribute(metadata, KnownAttribute.IsReadOnly);
}
}
this.returnTypeIsRefReadonly = ThreeState.From(hasReadOnlyAttr);
return hasReadOnlyAttr;
}
}
#endregion
public Accessibility Accessibility => GetAccessibility(attributes);

3
ICSharpCode.Decompiler/TypeSystem/Implementation/SpecializedMethod.cs

@ -95,7 +95,8 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation @@ -95,7 +95,8 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation
}
public IEnumerable<IAttribute> GetReturnTypeAttributes() => methodDefinition.GetReturnTypeAttributes();
public bool ReturnTypeIsRefReadOnly => methodDefinition.ReturnTypeIsRefReadOnly;
public IReadOnlyList<ITypeParameter> TypeParameters {
get {
return specializedTypeParameters ?? methodDefinition.TypeParameters;

1
ICSharpCode.Decompiler/TypeSystem/VarArgInstanceMethod.cs

@ -113,6 +113,7 @@ namespace ICSharpCode.Decompiler.TypeSystem @@ -113,6 +113,7 @@ namespace ICSharpCode.Decompiler.TypeSystem
IEnumerable<IAttribute> IEntity.GetAttributes() => baseMethod.GetAttributes();
IEnumerable<IAttribute> IMethod.GetReturnTypeAttributes() => baseMethod.GetReturnTypeAttributes();
bool IMethod.ReturnTypeIsRefReadOnly => baseMethod.ReturnTypeIsRefReadOnly;
public IReadOnlyList<ITypeParameter> TypeParameters {
get { return baseMethod.TypeParameters; }

Loading…
Cancel
Save