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 @@
namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty using System;
namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty
{ {
internal class RefLocalsAndReturns internal class RefLocalsAndReturns
{ {
@ -12,9 +14,68 @@
private readonly int dummy; private readonly int dummy;
} }
public struct NormalStruct
{
private readonly int dummy;
public void Method()
{
}
}
public readonly struct ReadOnlyStruct public readonly struct ReadOnlyStruct
{ {
private readonly int dummy; 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
if (composedType.HasRefSpecifier) { if (composedType.HasRefSpecifier) {
WriteKeyword(ComposedType.RefRole); WriteKeyword(ComposedType.RefRole);
} }
if (composedType.HasReadOnlySpecifier) {
WriteKeyword(ComposedType.ReadonlyRole);
}
composedType.BaseType.AcceptVisitor(this); composedType.BaseType.AcceptVisitor(this);
if (composedType.HasNullableSpecifier) { if (composedType.HasNullableSpecifier) {
WriteToken(ComposedType.NullableRole); WriteToken(ComposedType.NullableRole);

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

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

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

@ -36,6 +36,7 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax
public class ComposedType : AstType public class ComposedType : AstType
{ {
public static readonly TokenRole RefRole = new TokenRole("ref"); 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 NullableRole = new TokenRole("?");
public static readonly TokenRole PointerRole = new TokenRole("*"); public static readonly TokenRole PointerRole = new TokenRole("*");
public static readonly Role<ArraySpecifier> ArraySpecifierRole = new Role<ArraySpecifier>("ArraySpecifier"); public static readonly Role<ArraySpecifier> ArraySpecifierRole = new Role<ArraySpecifier>("ArraySpecifier");
@ -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 { public AstType BaseType {
get { return GetChildByRole(Roles.Type); } get { return GetChildByRole(Roles.Type); }
set { SetChildByRole(Roles.Type, value); } set { SetChildByRole(Roles.Type, value); }
@ -123,6 +138,7 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax
&& this.HasNullableSpecifier == o.HasNullableSpecifier && this.HasNullableSpecifier == o.HasNullableSpecifier
&& this.PointerRank == o.PointerRank && this.PointerRank == o.PointerRank
&& this.HasRefSpecifier == o.HasRefSpecifier && this.HasRefSpecifier == o.HasRefSpecifier
&& this.HasReadOnlySpecifier == o.HasReadOnlySpecifier
&& this.BaseType.DoMatch(o.BaseType, match) && this.BaseType.DoMatch(o.BaseType, match)
&& this.ArraySpecifiers.DoMatch(o.ArraySpecifiers, match); && this.ArraySpecifiers.DoMatch(o.ArraySpecifiers, match);
} }
@ -132,6 +148,8 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax
StringBuilder b = new StringBuilder(); StringBuilder b = new StringBuilder();
if (this.HasRefSpecifier) if (this.HasRefSpecifier)
b.Append("ref "); b.Append("ref ");
if (this.HasReadOnlySpecifier)
b.Append("readonly ");
b.Append(this.BaseType.ToString()); b.Append(this.BaseType.ToString());
if (this.HasNullableSpecifier) if (this.HasNullableSpecifier)
b.Append('?'); b.Append('?');

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

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

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

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

5
ICSharpCode.Decompiler/TypeSystem/IMethod.cs

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

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

@ -132,6 +132,7 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation
public override SymbolKind SymbolKind => symbolKind; public override SymbolKind SymbolKind => symbolKind;
IEnumerable<IAttribute> IMethod.GetReturnTypeAttributes() => EmptyList<IAttribute>.Instance; IEnumerable<IAttribute> IMethod.GetReturnTypeAttributes() => EmptyList<IAttribute>.Instance;
bool IMethod.ReturnTypeIsRefReadOnly => false;
public IReadOnlyList<ITypeParameter> TypeParameters { get; set; } = EmptyList<ITypeParameter>.Instance; 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
string name; string name;
IParameter[] parameters; IParameter[] parameters;
IType returnType; IType returnType;
byte returnTypeIsRefReadonly = ThreeState.Unknown;
internal MetadataMethod(MetadataModule module, MethodDefinitionHandle handle) internal MetadataMethod(MetadataModule module, MethodDefinitionHandle handle)
{ {
@ -203,6 +204,10 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation
i++; i++;
} }
Debug.Assert(i == parameters.Length); 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, var returnType = ApplyAttributeTypeVisitor.ApplyAttributesToType(signature.ReturnType,
module.Compilation, returnTypeAttributes, metadata, module.TypeSystemOptions); module.Compilation, returnTypeAttributes, metadata, module.TypeSystemOptions);
return (returnType, parameters); return (returnType, parameters);
@ -387,6 +392,26 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation
} }
return b.Build(); 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 #endregion
public Accessibility Accessibility => GetAccessibility(attributes); public Accessibility Accessibility => GetAccessibility(attributes);

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

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

1
ICSharpCode.Decompiler/TypeSystem/VarArgInstanceMethod.cs

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

Loading…
Cancel
Save