Browse Source

Show attributes on type parameter constraints in C# decompilation.

pull/1641/head
Daniel Grunwald 6 years ago
parent
commit
c1510027df
  1. 14
      ICSharpCode.Decompiler/CSharp/OutputVisitor/CSharpOutputVisitor.cs
  2. 4
      ICSharpCode.Decompiler/CSharp/Syntax/ComposedType.cs
  3. 4
      ICSharpCode.Decompiler/CSharp/Syntax/GeneralScope/Constraint.cs
  4. 1
      ICSharpCode.Decompiler/CSharp/Syntax/TypeMembers/EntityDeclaration.cs
  5. 16
      ICSharpCode.Decompiler/CSharp/Syntax/TypeSystemAstBuilder.cs
  6. 4
      ICSharpCode.Decompiler/TypeSystem/ISymbol.cs
  7. 17
      ICSharpCode.Decompiler/TypeSystem/ITypeParameter.cs
  8. 6
      ICSharpCode.Decompiler/TypeSystem/Implementation/AbstractTypeParameter.cs
  9. 20
      ICSharpCode.Decompiler/TypeSystem/Implementation/DefaultTypeParameter.cs
  10. 2
      ICSharpCode.Decompiler/TypeSystem/Implementation/DummyTypeParameter.cs
  11. 29
      ICSharpCode.Decompiler/TypeSystem/Implementation/MetadataTypeParameter.cs
  12. 1
      ICSharpCode.Decompiler/TypeSystem/Implementation/NullabilityAnnotatedType.cs
  13. 11
      ICSharpCode.Decompiler/TypeSystem/Implementation/SpecializedMethod.cs

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

@ -1313,10 +1313,15 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor
} }
WriteCommaSeparatedList(attributeSection.Attributes); WriteCommaSeparatedList(attributeSection.Attributes);
WriteToken(Roles.RBracket); WriteToken(Roles.RBracket);
if (attributeSection.Parent is ParameterDeclaration || attributeSection.Parent is TypeParameterDeclaration) { switch (attributeSection.Parent) {
case ParameterDeclaration _:
case TypeParameterDeclaration _:
case ComposedType _:
Space(); Space();
} else { break;
default:
NewLine(); NewLine();
break;
} }
EndNode(attributeSection); EndNode(attributeSection);
} }
@ -2349,6 +2354,11 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor
public virtual void VisitComposedType(ComposedType composedType) public virtual void VisitComposedType(ComposedType composedType)
{ {
StartNode(composedType); StartNode(composedType);
if (composedType.Attributes.Any()) {
foreach (var attr in composedType.Attributes) {
attr.AcceptVisitor(this);
}
}
if (composedType.HasRefSpecifier) { if (composedType.HasRefSpecifier) {
WriteKeyword(ComposedType.RefRole); WriteKeyword(ComposedType.RefRole);
} }

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

@ -35,11 +35,15 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax
{ {
public class ComposedType : AstType public class ComposedType : AstType
{ {
public static readonly Role<AttributeSection> AttributeRole = EntityDeclaration.AttributeRole;
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 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");
public AstNodeCollection<AttributeSection> Attributes {
get { return base.GetChildrenByRole(AttributeRole); }
}
/// <summary> /// <summary>
/// Gets/sets whether this type has a 'ref' specifier. /// Gets/sets whether this type has a 'ref' specifier.

4
ICSharpCode.Decompiler/CSharp/Syntax/GeneralScope/Constraint.cs

@ -36,9 +36,7 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax
public class Constraint : AstNode public class Constraint : AstNode
{ {
public override NodeType NodeType { public override NodeType NodeType {
get { get { return NodeType.Unknown; }
return NodeType.Unknown;
}
} }
public CSharpTokenNode WhereKeyword { public CSharpTokenNode WhereKeyword {

1
ICSharpCode.Decompiler/CSharp/Syntax/TypeMembers/EntityDeclaration.cs

@ -25,7 +25,6 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax
public abstract class EntityDeclaration : AstNode public abstract class EntityDeclaration : AstNode
{ {
public static readonly Role<AttributeSection> AttributeRole = new Role<AttributeSection>("Attribute"); public static readonly Role<AttributeSection> AttributeRole = new Role<AttributeSection>("Attribute");
public static readonly Role<AttributeSection> UnattachedAttributeRole = new Role<AttributeSection>("UnattachedAttribute");
public static readonly Role<CSharpModifierToken> ModifierRole = new Role<CSharpModifierToken>("Modifier"); public static readonly Role<CSharpModifierToken> ModifierRole = new Role<CSharpModifierToken>("Modifier");
public static readonly Role<AstType> PrivateImplementationTypeRole = new Role<AstType>("PrivateImplementationType", AstType.Null); public static readonly Role<AstType> PrivateImplementationTypeRole = new Role<AstType>("PrivateImplementationType", AstType.Null);

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

@ -1805,9 +1805,19 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax
} else if (tp.NullabilityConstraint == Nullability.NotNullable) { } else if (tp.NullabilityConstraint == Nullability.NotNullable) {
c.BaseTypes.Add(new PrimitiveType("notnull")); c.BaseTypes.Add(new PrimitiveType("notnull"));
} }
foreach (IType t in tp.DirectBaseTypes) { foreach (TypeConstraint t in tp.TypeConstraints) {
if (!IsObjectOrValueType(t)) if (!IsObjectOrValueType(t.Type) || t.Attributes.Count > 0) {
c.BaseTypes.Add(ConvertType(t)); AstType astType = ConvertType(t.Type);
if (t.Attributes.Count > 0) {
var attrSection = new AttributeSection();
attrSection.Attributes.AddRange(t.Attributes.Select(ConvertAttribute));
astType = new ComposedType {
Attributes = { attrSection },
BaseType = astType
};
}
c.BaseTypes.Add(astType);
}
} }
if (tp.HasDefaultConstructorConstraint && !tp.HasValueTypeConstraint) { if (tp.HasDefaultConstructorConstraint && !tp.HasValueTypeConstraint) {
c.BaseTypes.Add(new PrimitiveType("new")); c.BaseTypes.Add(new PrimitiveType("new"));

4
ICSharpCode.Decompiler/TypeSystem/ISymbol.cs

@ -69,6 +69,10 @@ namespace ICSharpCode.Decompiler.TypeSystem
Parameter, Parameter,
/// <seealso cref="ITypeParameter"/> /// <seealso cref="ITypeParameter"/>
TypeParameter, TypeParameter,
/// <summary>
/// Constraint on a type parameter.
/// </summary>
Constraint,
} }
/// <summary> /// <summary>

17
ICSharpCode.Decompiler/TypeSystem/ITypeParameter.cs

@ -16,7 +16,9 @@
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE. // DEALINGS IN THE SOFTWARE.
using System;
using System.Collections.Generic; using System.Collections.Generic;
using ICSharpCode.Decompiler.Util;
namespace ICSharpCode.Decompiler.TypeSystem namespace ICSharpCode.Decompiler.TypeSystem
{ {
@ -99,6 +101,21 @@ namespace ICSharpCode.Decompiler.TypeSystem
/// E.g. "T? GetNull&lt;T&gt;() where T : class => null;" /// E.g. "T? GetNull&lt;T&gt;() where T : class => null;"
/// </summary> /// </summary>
Nullability NullabilityConstraint { get; } Nullability NullabilityConstraint { get; }
IReadOnlyList<TypeConstraint> TypeConstraints { get; }
}
public readonly struct TypeConstraint
{
public SymbolKind SymbolKind => SymbolKind.Constraint;
public IType Type { get; }
public IReadOnlyList<IAttribute> Attributes { get; }
public TypeConstraint(IType type, IReadOnlyList<IAttribute> attributes = null)
{
this.Type = type ?? throw new ArgumentNullException(nameof(type));
this.Attributes = attributes ?? EmptyList<IAttribute>.Instance;
}
} }
/// <summary> /// <summary>

6
ICSharpCode.Decompiler/TypeSystem/Implementation/AbstractTypeParameter.cs

@ -220,7 +220,11 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation
get { return EmptyList<IType>.Instance; } get { return EmptyList<IType>.Instance; }
} }
public abstract IEnumerable<IType> DirectBaseTypes { get; } public IEnumerable<IType> DirectBaseTypes {
get { return TypeConstraints.Select(t => t.Type); }
}
public abstract IReadOnlyList<TypeConstraint> TypeConstraints { get; }
public string Name { public string Name {
get { return name; } get { return name; }

20
ICSharpCode.Decompiler/TypeSystem/Implementation/DefaultTypeParameter.cs

@ -16,6 +16,7 @@
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE. // DEALINGS IN THE SOFTWARE.
using System;
using System.Collections.Generic; using System.Collections.Generic;
using ICSharpCode.Decompiler.Util; using ICSharpCode.Decompiler.Util;
@ -43,7 +44,7 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation
this.hasReferenceTypeConstraint = hasReferenceTypeConstraint; this.hasReferenceTypeConstraint = hasReferenceTypeConstraint;
this.hasDefaultConstructorConstraint = hasDefaultConstructorConstraint; this.hasDefaultConstructorConstraint = hasDefaultConstructorConstraint;
this.nullabilityConstraint = nullabilityConstraint; this.nullabilityConstraint = nullabilityConstraint;
this.constraints = constraints ?? EmptyList<IType>.Instance; this.TypeConstraints = MakeConstraints(constraints);
this.attributes = attributes ?? EmptyList<IAttribute>.Instance; this.attributes = attributes ?? EmptyList<IAttribute>.Instance;
} }
@ -60,7 +61,7 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation
this.hasReferenceTypeConstraint = hasReferenceTypeConstraint; this.hasReferenceTypeConstraint = hasReferenceTypeConstraint;
this.hasDefaultConstructorConstraint = hasDefaultConstructorConstraint; this.hasDefaultConstructorConstraint = hasDefaultConstructorConstraint;
this.nullabilityConstraint = nullabilityConstraint; this.nullabilityConstraint = nullabilityConstraint;
this.constraints = constraints ?? EmptyList<IType>.Instance; this.TypeConstraints = MakeConstraints(constraints);
this.attributes = attributes ?? EmptyList<IAttribute>.Instance; this.attributes = attributes ?? EmptyList<IAttribute>.Instance;
} }
@ -72,19 +73,24 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation
public override bool HasUnmanagedConstraint => false; public override bool HasUnmanagedConstraint => false;
public override Nullability NullabilityConstraint => nullabilityConstraint; public override Nullability NullabilityConstraint => nullabilityConstraint;
public override IEnumerable<IType> DirectBaseTypes { public override IReadOnlyList<TypeConstraint> TypeConstraints { get; }
get {
IReadOnlyList<TypeConstraint> MakeConstraints(IReadOnlyList<IType> constraints)
{
var result = new List<TypeConstraint>();
bool hasNonInterfaceConstraint = false; bool hasNonInterfaceConstraint = false;
if (constraints != null) {
foreach (IType c in constraints) { foreach (IType c in constraints) {
yield return c; result.Add(new TypeConstraint(c));
if (c.Kind != TypeKind.Interface) if (c.Kind != TypeKind.Interface)
hasNonInterfaceConstraint = true; hasNonInterfaceConstraint = true;
} }
}
// Do not add the 'System.Object' constraint if there is another constraint with a base class. // Do not add the 'System.Object' constraint if there is another constraint with a base class.
if (this.HasValueTypeConstraint || !hasNonInterfaceConstraint) { if (this.HasValueTypeConstraint || !hasNonInterfaceConstraint) {
yield return this.Compilation.FindType(this.HasValueTypeConstraint ? KnownTypeCode.ValueType : KnownTypeCode.Object); result.Add(new TypeConstraint(this.Compilation.FindType(this.HasValueTypeConstraint ? KnownTypeCode.ValueType : KnownTypeCode.Object)));
}
} }
return result;
} }
} }
} }

2
ICSharpCode.Decompiler/TypeSystem/Implementation/DummyTypeParameter.cs

@ -169,6 +169,8 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation
bool ITypeParameter.HasUnmanagedConstraint => false; bool ITypeParameter.HasUnmanagedConstraint => false;
Nullability ITypeParameter.NullabilityConstraint => Nullability.Oblivious; Nullability ITypeParameter.NullabilityConstraint => Nullability.Oblivious;
IReadOnlyList<TypeConstraint> ITypeParameter.TypeConstraints => EmptyList<TypeConstraint>.Instance;
public override IType ChangeNullability(Nullability nullability) public override IType ChangeNullability(Nullability nullability)
{ {
if (nullability == Nullability.Oblivious) { if (nullability == Nullability.Oblivious) {

29
ICSharpCode.Decompiler/TypeSystem/Implementation/MetadataTypeParameter.cs

@ -19,6 +19,7 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Diagnostics; using System.Diagnostics;
using System.Linq;
using System.Reflection; using System.Reflection;
using System.Reflection.Metadata; using System.Reflection.Metadata;
using System.Reflection.Metadata.Ecma335; using System.Reflection.Metadata.Ecma335;
@ -34,7 +35,7 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation
readonly GenericParameterAttributes attr; readonly GenericParameterAttributes attr;
// lazy-loaded: // lazy-loaded:
IReadOnlyList<IType> constraints; IReadOnlyList<TypeConstraint> constraints;
byte unmanagedConstraint = ThreeState.Unknown; byte unmanagedConstraint = ThreeState.Unknown;
const byte nullabilityNotYetLoaded = 255; const byte nullabilityNotYetLoaded = 255;
byte nullabilityConstraint = nullabilityNotYetLoaded; byte nullabilityConstraint = nullabilityNotYetLoaded;
@ -170,16 +171,17 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation
} }
} }
public override IEnumerable<IType> DirectBaseTypes { public override IReadOnlyList<TypeConstraint> TypeConstraints {
get { get {
var constraints = LazyInit.VolatileRead(ref this.constraints); var constraints = LazyInit.VolatileRead(ref this.constraints);
if (constraints != null) if (constraints == null) {
constraints = LazyInit.GetOrSet(ref this.constraints, DecodeConstraints());
}
return constraints; return constraints;
return LazyInit.GetOrSet(ref this.constraints, DecodeConstraints());
} }
} }
private IReadOnlyList<IType> DecodeConstraints() private IReadOnlyList<TypeConstraint> DecodeConstraints()
{ {
var metadata = module.metadata; var metadata = module.metadata;
var gp = metadata.GetGenericParameter(handle); var gp = metadata.GetGenericParameter(handle);
@ -193,18 +195,25 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation
} }
var constraintHandleCollection = gp.GetConstraints(); var constraintHandleCollection = gp.GetConstraints();
List<IType> result = new List<IType>(constraintHandleCollection.Count + 1); var result = new List<TypeConstraint>(constraintHandleCollection.Count + 1);
bool hasNonInterfaceConstraint = false; bool hasNonInterfaceConstraint = false;
foreach (var constraintHandle in constraintHandleCollection) { foreach (var constraintHandle in constraintHandleCollection) {
var constraint = metadata.GetGenericParameterConstraint(constraintHandle); var constraint = metadata.GetGenericParameterConstraint(constraintHandle);
var ty = module.ResolveType(constraint.Type, new GenericContext(Owner), constraint.GetCustomAttributes(), nullableContext); var attrs = constraint.GetCustomAttributes();
result.Add(ty); var ty = module.ResolveType(constraint.Type, new GenericContext(Owner), attrs, nullableContext);
if (attrs.Count == 0) {
result.Add(new TypeConstraint(ty));
} else {
AttributeListBuilder b = new AttributeListBuilder(module);
b.Add(attrs, SymbolKind.Constraint);
result.Add(new TypeConstraint(ty, b.Build()));
}
hasNonInterfaceConstraint |= (ty.Kind != TypeKind.Interface); hasNonInterfaceConstraint |= (ty.Kind != TypeKind.Interface);
} }
if (this.HasValueTypeConstraint) { if (this.HasValueTypeConstraint) {
result.Add(Compilation.FindType(KnownTypeCode.ValueType)); result.Add(new TypeConstraint(Compilation.FindType(KnownTypeCode.ValueType)));
} else if (!hasNonInterfaceConstraint) { } else if (!hasNonInterfaceConstraint) {
result.Add(Compilation.FindType(KnownTypeCode.Object)); result.Add(new TypeConstraint(Compilation.FindType(KnownTypeCode.Object)));
} }
return result; return result;
} }

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

@ -107,6 +107,7 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation
bool ITypeParameter.HasValueTypeConstraint => baseType.HasValueTypeConstraint; bool ITypeParameter.HasValueTypeConstraint => baseType.HasValueTypeConstraint;
bool ITypeParameter.HasUnmanagedConstraint => baseType.HasUnmanagedConstraint; bool ITypeParameter.HasUnmanagedConstraint => baseType.HasUnmanagedConstraint;
Nullability ITypeParameter.NullabilityConstraint => baseType.NullabilityConstraint; Nullability ITypeParameter.NullabilityConstraint => baseType.NullabilityConstraint;
IReadOnlyList<TypeConstraint> ITypeParameter.TypeConstraints => baseType.TypeConstraints;
SymbolKind ISymbol.SymbolKind => SymbolKind.TypeParameter; SymbolKind ISymbol.SymbolKind => SymbolKind.TypeParameter;
IEnumerable<IAttribute> ITypeParameter.GetAttributes() => baseType.GetAttributes(); IEnumerable<IAttribute> ITypeParameter.GetAttributes() => baseType.GetAttributes();
} }

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

@ -257,9 +257,16 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation
public override Nullability NullabilityConstraint => baseTp.NullabilityConstraint; public override Nullability NullabilityConstraint => baseTp.NullabilityConstraint;
public override IEnumerable<IType> DirectBaseTypes { IReadOnlyList<TypeConstraint> typeConstraints;
public override IReadOnlyList<TypeConstraint> TypeConstraints {
get { get {
return baseTp.DirectBaseTypes.Select(t => t.AcceptVisitor(substitution)); var typeConstraints = LazyInit.VolatileRead(ref this.typeConstraints);
if (typeConstraints == null) {
typeConstraints = baseTp.TypeConstraints.SelectReadOnlyArray(c => new TypeConstraint(c.Type.AcceptVisitor(substitution), c.Attributes));
typeConstraints = LazyInit.GetOrSet(ref this.typeConstraints, typeConstraints);
}
return typeConstraints;
} }
} }
} }

Loading…
Cancel
Save