Browse Source

First attempt at integrating C# nullable reference types into the type system.

pull/1425/head
Daniel Grunwald 6 years ago
parent
commit
7a058f6262
  1. 1
      ICSharpCode.Decompiler/CSharp/CSharpLanguageVersion.cs
  2. 5
      ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs
  3. 3
      ICSharpCode.Decompiler/CSharp/OutputVisitor/CSharpOutputVisitor.cs
  4. 1
      ICSharpCode.Decompiler/CSharp/OutputVisitor/InsertParenthesesVisitor.cs
  5. 10
      ICSharpCode.Decompiler/CSharp/Syntax/Expressions/UnaryOperatorExpression.cs
  6. 31
      ICSharpCode.Decompiler/CSharp/Syntax/TypeSystemAstBuilder.cs
  7. 20
      ICSharpCode.Decompiler/DecompilerSettings.cs
  8. 4
      ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj
  9. 192
      ICSharpCode.Decompiler/TypeSystem/AnonymousType.cs
  10. 60
      ICSharpCode.Decompiler/TypeSystem/ApplyAttributeTypeVisitor.cs
  11. 10
      ICSharpCode.Decompiler/TypeSystem/ArrayType.cs
  12. 7
      ICSharpCode.Decompiler/TypeSystem/DecompilerTypeSystem.cs
  13. 10
      ICSharpCode.Decompiler/TypeSystem/IType.cs
  14. 9
      ICSharpCode.Decompiler/TypeSystem/Implementation/AbstractType.cs
  15. 11
      ICSharpCode.Decompiler/TypeSystem/Implementation/AbstractTypeParameter.cs
  16. 2
      ICSharpCode.Decompiler/TypeSystem/Implementation/AttributeListBuilder.cs
  17. 109
      ICSharpCode.Decompiler/TypeSystem/Implementation/DecoratedType.cs
  18. 2
      ICSharpCode.Decompiler/TypeSystem/Implementation/KnownAttributes.cs
  19. 10
      ICSharpCode.Decompiler/TypeSystem/Implementation/MetadataTypeDefinition.cs
  20. 9
      ICSharpCode.Decompiler/TypeSystem/Implementation/MinimalCorlib.cs
  21. 55
      ICSharpCode.Decompiler/TypeSystem/Implementation/NullabilityAnnotatedType.cs
  22. 2
      ICSharpCode.Decompiler/TypeSystem/MetadataModule.cs
  23. 10
      ICSharpCode.Decompiler/TypeSystem/ModifiedType.cs
  24. 15
      ICSharpCode.Decompiler/TypeSystem/NormalizeTypeVisitor.cs
  25. 13
      ICSharpCode.Decompiler/TypeSystem/Nullability.cs
  26. 14
      ICSharpCode.Decompiler/TypeSystem/ParameterizedType.cs
  27. 5
      ICSharpCode.Decompiler/TypeSystem/TypeVisitor.cs
  28. 1
      ILSpy/Languages/CSharpLanguage.cs

1
ICSharpCode.Decompiler/CSharp/CSharpLanguageVersion.cs

@ -16,6 +16,7 @@ namespace ICSharpCode.Decompiler.CSharp @@ -16,6 +16,7 @@ namespace ICSharpCode.Decompiler.CSharp
CSharp7_1 = 701,
CSharp7_2 = 702,
CSharp7_3 = 703,
CSharp8_0 = 800,
Latest = 0x7FFFFFFF
}
}

5
ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs

@ -1754,6 +1754,11 @@ namespace ICSharpCode.Decompiler.CSharp @@ -1754,6 +1754,11 @@ namespace ICSharpCode.Decompiler.CSharp
.WithRR(new ResolveResult(NullableType.GetUnderlyingType(translatedTarget.Type)))
.WithoutILInstruction();
}
if (translatedTarget.Type.Nullability == Nullability.Nullable) {
translatedTarget = new UnaryOperatorExpression(UnaryOperatorType.SuppressNullableWarning, translatedTarget)
.WithRR(new ResolveResult(translatedTarget.Type.ChangeNullability(Nullability.Oblivious)))
.WithoutILInstruction();
}
return translatedTarget;
}
} else {

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

@ -1120,7 +1120,8 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor @@ -1120,7 +1120,8 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor
{
return op == UnaryOperatorType.PostIncrement
|| op == UnaryOperatorType.PostDecrement
|| op == UnaryOperatorType.NullConditional;
|| op == UnaryOperatorType.NullConditional
|| op == UnaryOperatorType.SuppressNullableWarning;
}
public virtual void VisitUncheckedExpression(UncheckedExpression uncheckedExpression)

1
ICSharpCode.Decompiler/CSharp/OutputVisitor/InsertParenthesesVisitor.cs

@ -61,6 +61,7 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor @@ -61,6 +61,7 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor
case UnaryOperatorType.PostDecrement:
case UnaryOperatorType.PostIncrement:
case UnaryOperatorType.NullConditional:
case UnaryOperatorType.SuppressNullableWarning:
return Primary;
case UnaryOperatorType.NullConditionalRewrap:
return NullableRewrap;

10
ICSharpCode.Decompiler/CSharp/Syntax/Expressions/UnaryOperatorExpression.cs

@ -44,7 +44,8 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax @@ -44,7 +44,8 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax
public readonly static TokenRole AddressOfRole = new TokenRole ("&");
public readonly static TokenRole AwaitRole = new TokenRole ("await");
public readonly static TokenRole NullConditionalRole = new TokenRole ("?");
public readonly static TokenRole SuppressNullableWarningRole = new TokenRole ("!");
public UnaryOperatorExpression()
{
}
@ -119,6 +120,8 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax @@ -119,6 +120,8 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax
case UnaryOperatorType.NullConditionalRewrap:
case UnaryOperatorType.IsTrue:
return null; // no syntax
case UnaryOperatorType.SuppressNullableWarning:
return SuppressNullableWarningRole;
default:
throw new NotSupportedException("Invalid value for UnaryOperatorType");
}
@ -146,6 +149,7 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax @@ -146,6 +149,7 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax
case UnaryOperatorType.Dereference:
case UnaryOperatorType.AddressOf:
case UnaryOperatorType.Await:
case UnaryOperatorType.SuppressNullableWarning:
return ExpressionType.Extension;
default:
throw new NotSupportedException("Invalid value for UnaryOperatorType");
@ -198,5 +202,9 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax @@ -198,5 +202,9 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax
/// Implicit call of "operator true".
/// </summary>
IsTrue,
/// <summary>
/// C# 8 postfix ! operator (dammit operator)
/// </summary>
SuppressNullableWarning,
}
}

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

@ -251,6 +251,12 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax @@ -251,6 +251,12 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax
}
return ConvertTypeHelper(pt.GenericType, pt.TypeArguments);
}
if (type is NullabilityAnnotatedType nat) {
var astType = ConvertType(nat.ElementType);
if (nat.Nullability == Nullability.Nullable)
astType = astType.MakeNullableType();
return astType;
}
if (type is TupleType tuple) {
var astType = new TupleAstType();
foreach (var (etype, ename) in tuple.ElementTypes.Zip(tuple.ElementNames)) {
@ -278,14 +284,19 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax @@ -278,14 +284,19 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax
AstType ConvertTypeHelper(IType genericType, IReadOnlyList<IType> typeArguments)
{
ITypeDefinition typeDef = genericType.GetDefinition();
Debug.Assert(typeDef != null || genericType.Kind == TypeKind.Unknown);
Debug.Assert(typeArguments.Count >= genericType.TypeParameterCount);
Debug.Assert(genericType is ITypeDefinition || genericType.Kind == TypeKind.Unknown);
ITypeDefinition typeDef = genericType as ITypeDefinition;
if (AlwaysUseBuiltinTypeNames && typeDef != null) {
string keyword = KnownTypeReference.GetCSharpNameByTypeCode(typeDef.KnownTypeCode);
if (keyword != null)
return new PrimitiveType(keyword);
if (keyword != null) {
if (genericType.Nullability == Nullability.Nullable) {
return new PrimitiveType(keyword).MakeNullableType();
} else {
return new PrimitiveType(keyword);
}
}
}
// The number of type parameters belonging to outer classes
@ -328,7 +339,11 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax @@ -328,7 +339,11 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax
if (AlwaysUseShortTypeNames || (typeDef == null && genericType.DeclaringType == null)) {
var shortResult = new SimpleType(genericType.Name);
AddTypeArguments(shortResult, genericType.TypeParameters, typeArguments, outerTypeParameterCount, genericType.TypeParameterCount);
return shortResult;
if (genericType.Nullability == Nullability.Nullable) {
return shortResult.MakeNullableType();
} else {
return shortResult;
}
}
MemberType result = new MemberType();
if (genericType.DeclaringType != null) {
@ -347,7 +362,11 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax @@ -347,7 +362,11 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax
}
result.MemberName = genericType.Name;
AddTypeArguments(result, genericType.TypeParameters, typeArguments, outerTypeParameterCount, genericType.TypeParameterCount);
return result;
if (genericType.Nullability == Nullability.Nullable) {
return result.MakeNullableType();
} else {
return result;
}
}
/// <summary>

20
ICSharpCode.Decompiler/DecompilerSettings.cs

@ -93,6 +93,9 @@ namespace ICSharpCode.Decompiler @@ -93,6 +93,9 @@ namespace ICSharpCode.Decompiler
stackAllocInitializers = false;
tupleComparisons = false;
}
if (languageVersion < CSharp.LanguageVersion.CSharp8_0) {
nullableReferenceTypes = false;
}
}
public CSharp.LanguageVersion GetMinimumRequiredVersion()
@ -745,7 +748,7 @@ namespace ICSharpCode.Decompiler @@ -745,7 +748,7 @@ namespace ICSharpCode.Decompiler
}
}
}
bool namedArguments = true;
/// <summary>
@ -808,6 +811,21 @@ namespace ICSharpCode.Decompiler @@ -808,6 +811,21 @@ namespace ICSharpCode.Decompiler
}
}
bool nullableReferenceTypes = true;
/// <summary>
/// Gets/Sets whether C# 8.0 nullable reference types are enabled.
/// </summary>
public bool NullableReferenceTypes {
get { return nullableReferenceTypes; }
set {
if (nullableReferenceTypes != value) {
nullableReferenceTypes = value;
OnPropertyChanged();
}
}
}
#region Options to aid VB decompilation
bool assumeArrayLengthFitsIntoInt32 = true;

4
ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj

@ -351,6 +351,7 @@ @@ -351,6 +351,7 @@
<Compile Include="TypeSystem\Implementation\AttributeListBuilder.cs" />
<Compile Include="TypeSystem\Implementation\CustomAttribute.cs" />
<Compile Include="TypeSystem\Implementation\DecimalConstantHelper.cs" />
<Compile Include="TypeSystem\Implementation\DecoratedType.cs" />
<Compile Include="TypeSystem\Implementation\DefaultTypeParameter.cs" />
<Compile Include="TypeSystem\Implementation\FakeMember.cs" />
<Compile Include="TypeSystem\Implementation\KnownAttributes.cs" />
@ -370,6 +371,8 @@ @@ -370,6 +371,8 @@
<Compile Include="Metadata\MetadataExtensions.cs" />
<Compile Include="Semantics\TupleResolveResult.cs" />
<Compile Include="TypeSystem\NormalizeTypeVisitor.cs" />
<Compile Include="TypeSystem\Nullability.cs" />
<Compile Include="TypeSystem\Implementation\NullabilityAnnotatedType.cs" />
<Compile Include="TypeSystem\TupleType.cs" />
<Compile Include="TypeSystem\TypeProvider.cs" />
<Compile Include="Util\GraphVizGraph.cs" />
@ -480,7 +483,6 @@ @@ -480,7 +483,6 @@
<Compile Include="Semantics\TypeResolveResult.cs" />
<Compile Include="Semantics\UnknownMemberResolveResult.cs" />
<Compile Include="TypeSystem\Accessibility.cs" />
<Compile Include="TypeSystem\AnonymousType.cs" />
<Compile Include="TypeSystem\ArrayType.cs" />
<Compile Include="TypeSystem\AssemblyQualifiedTypeName.cs" />
<Compile Include="TypeSystem\ByReferenceType.cs" />

192
ICSharpCode.Decompiler/TypeSystem/AnonymousType.cs

@ -1,192 +0,0 @@ @@ -1,192 +0,0 @@
// Copyright (c) 2010-2013 AlphaSierraPapa for the SharpDevelop Team
//
// Permission is hereby granted, free of charge, to any person obtaining a copy of this
// software and associated documentation files (the "Software"), to deal in the Software
// without restriction, including without limitation the rights to use, copy, modify, merge,
// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
// to whom the Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all copies or
// substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.
using System;
using System.Collections.Generic;
using System.Linq;
using ICSharpCode.Decompiler.TypeSystem.Implementation;
using ICSharpCode.Decompiler.Util;
namespace ICSharpCode.Decompiler.TypeSystem
{
/// <summary>
/// Anonymous type.
/// </summary>
public class AnonymousType : AbstractType
{
ICompilation compilation;
public AnonymousType(ICompilation compilation)
{
if (compilation == null)
throw new ArgumentNullException("compilation");
this.compilation = compilation;
throw new NotImplementedException();
}
/*
sealed class AnonymousTypeProperty : DefaultResolvedProperty
{
readonly AnonymousType declaringType;
public AnonymousTypeProperty(IUnresolvedProperty unresolved, ITypeResolveContext parentContext, AnonymousType declaringType)
: base(unresolved, parentContext)
{
this.declaringType = declaringType;
}
public override IType DeclaringType {
get { return declaringType; }
}
public override bool Equals(object obj)
{
AnonymousTypeProperty p = obj as AnonymousTypeProperty;
return p != null && this.Name == p.Name && declaringType.Equals(p.declaringType);
}
public override int GetHashCode()
{
return declaringType.GetHashCode() ^ unchecked(27 * this.Name.GetHashCode());
}
protected override IMethod CreateResolvedAccessor(IUnresolvedMethod unresolvedAccessor)
{
return new AnonymousTypeAccessor(unresolvedAccessor, context, this);
}
}
sealed class AnonymousTypeAccessor : DefaultResolvedMethod
{
readonly AnonymousTypeProperty owner;
public AnonymousTypeAccessor(IUnresolvedMethod unresolved, ITypeResolveContext parentContext, AnonymousTypeProperty owner)
: base(unresolved, parentContext, isExtensionMethod: false)
{
this.owner = owner;
}
public override IMember AccessorOwner {
get { return owner; }
}
public override IType DeclaringType {
get { return owner.DeclaringType; }
}
public override bool Equals(object obj)
{
AnonymousTypeAccessor p = obj as AnonymousTypeAccessor;
return p != null && this.Name == p.Name && owner.DeclaringType.Equals(p.owner.DeclaringType);
}
public override int GetHashCode()
{
return owner.DeclaringType.GetHashCode() ^ unchecked(27 * this.Name.GetHashCode());
}
}
*/
public override string Name {
get { return "Anonymous Type"; }
}
public override TypeKind Kind {
get { return TypeKind.Anonymous; }
}
public override IEnumerable<IType> DirectBaseTypes {
get {
yield return compilation.FindType(KnownTypeCode.Object);
}
}
public override bool? IsReferenceType {
get { return true; }
}
/*
public IReadOnlyList<IProperty> Properties {
get { return resolvedProperties; }
}
public override IEnumerable<IMethod> GetMethods(Predicate<IMethod> filter = null, GetMemberOptions options = GetMemberOptions.None)
{
if ((options & GetMemberOptions.IgnoreInheritedMembers) == GetMemberOptions.IgnoreInheritedMembers)
return EmptyList<IMethod>.Instance;
else
return compilation.FindType(KnownTypeCode.Object).GetMethods(filter, options);
}
public override IEnumerable<IMethod> GetMethods(IReadOnlyList<IType> typeArguments, Predicate<IMethod> filter = null, GetMemberOptions options = GetMemberOptions.None)
{
if ((options & GetMemberOptions.IgnoreInheritedMembers) == GetMemberOptions.IgnoreInheritedMembers)
return EmptyList<IMethod>.Instance;
else
return compilation.FindType(KnownTypeCode.Object).GetMethods(typeArguments, filter, options);
}
public override IEnumerable<IProperty> GetProperties(Predicate<IProperty> filter = null, GetMemberOptions options = GetMemberOptions.None)
{
for (int i = 0; i < unresolvedProperties.Length; i++) {
if (filter == null || filter(resolvedProperties[i]))
yield return resolvedProperties[i];
}
}
public override IEnumerable<IMethod> GetAccessors(Predicate<IMethod> filter, GetMemberOptions options)
{
for (int i = 0; i < unresolvedProperties.Length; i++) {
if (unresolvedProperties[i].CanGet) {
if (filter == null || filter(resolvedProperties[i].Getter))
yield return resolvedProperties[i].Getter;
}
if (unresolvedProperties[i].CanSet) {
if (filter == null || filter(resolvedProperties[i].Setter))
yield return resolvedProperties[i].Setter;
}
}
}
public override int GetHashCode()
{
unchecked {
int hashCode = resolvedProperties.Count;
foreach (var p in resolvedProperties) {
hashCode *= 31;
hashCode += p.Name.GetHashCode() ^ p.ReturnType.GetHashCode();
}
return hashCode;
}
}
public override bool Equals(IType other)
{
AnonymousType o = other as AnonymousType;
if (o == null || resolvedProperties.Count != o.resolvedProperties.Count)
return false;
for (int i = 0; i < resolvedProperties.Count; i++) {
IProperty p1 = resolvedProperties[i];
IProperty p2 = o.resolvedProperties[i];
if (p1.Name != p2.Name || !p1.ReturnType.Equals(p2.ReturnType))
return false;
}
return true;
}*/
}
}

60
ICSharpCode.Decompiler/TypeSystem/ApplyAttributeTypeVisitor.cs

@ -39,16 +39,18 @@ namespace ICSharpCode.Decompiler.TypeSystem @@ -39,16 +39,18 @@ namespace ICSharpCode.Decompiler.TypeSystem
TypeSystemOptions options,
bool typeChildrenOnly = false)
{
bool useDynamicType = (options & TypeSystemOptions.Dynamic) != 0;
bool useTupleTypes = (options & TypeSystemOptions.Tuple) != 0;
bool hasDynamicAttribute = false;
bool[] dynamicAttributeData = null;
string[] tupleElementNames = null;
if (attributes != null && (useDynamicType || useTupleTypes)) {
bool hasNullableAttribute = false;
Nullability nullability = Nullability.Oblivious;
Nullability[] nullableAttributeData = null;
const TypeSystemOptions relevantOptions = TypeSystemOptions.Dynamic | TypeSystemOptions.Tuple | TypeSystemOptions.NullabilityAnnotations;
if (attributes != null && (options & relevantOptions) != 0) {
foreach (var attrHandle in attributes.Value) {
var attr = metadata.GetCustomAttribute(attrHandle);
var attrType = attr.GetAttributeType(metadata);
if (useDynamicType && attrType.IsKnownType(metadata, KnownAttribute.Dynamic)) {
if ((options & TypeSystemOptions.Dynamic) != 0 && attrType.IsKnownType(metadata, KnownAttribute.Dynamic)) {
hasDynamicAttribute = true;
var ctor = attr.DecodeValue(Metadata.MetadataExtensions.minimalCorlibTypeProvider);
if (ctor.FixedArguments.Length == 1) {
@ -58,7 +60,7 @@ namespace ICSharpCode.Decompiler.TypeSystem @@ -58,7 +60,7 @@ namespace ICSharpCode.Decompiler.TypeSystem
dynamicAttributeData = values.SelectArray(v => (bool)v.Value);
}
}
} else if (useTupleTypes && attrType.IsKnownType(metadata, KnownAttribute.TupleElementNames)) {
} else if ((options & TypeSystemOptions.Tuple) != 0 && attrType.IsKnownType(metadata, KnownAttribute.TupleElementNames)) {
var ctor = attr.DecodeValue(Metadata.MetadataExtensions.minimalCorlibTypeProvider);
if (ctor.FixedArguments.Length == 1) {
var arg = ctor.FixedArguments[0];
@ -67,12 +69,26 @@ namespace ICSharpCode.Decompiler.TypeSystem @@ -67,12 +69,26 @@ namespace ICSharpCode.Decompiler.TypeSystem
tupleElementNames = values.SelectArray(v => (string)v.Value);
}
}
} else if ((options & TypeSystemOptions.NullabilityAnnotations) != 0 && attrType.IsKnownType(metadata, KnownAttribute.Nullable)) {
hasNullableAttribute = true;
var ctor = attr.DecodeValue(Metadata.MetadataExtensions.minimalCorlibTypeProvider);
if (ctor.FixedArguments.Length == 1) {
var arg = ctor.FixedArguments[0];
if (arg.Value is ImmutableArray<SRM.CustomAttributeTypedArgument<IType>> values
&& values.All(v => v.Value is byte b && b <= 2)) {
nullableAttributeData = values.SelectArray(v => (Nullability)(byte)v.Value);
} else if (arg.Value is byte b && b <= 2) {
nullability = (Nullability)(byte)arg.Value;
}
}
}
}
}
if (hasDynamicAttribute || (options & (TypeSystemOptions.Tuple | TypeSystemOptions.KeepModifiers)) != TypeSystemOptions.KeepModifiers) {
if (hasDynamicAttribute || hasNullableAttribute || (options & (TypeSystemOptions.Tuple | TypeSystemOptions.KeepModifiers)) != TypeSystemOptions.KeepModifiers) {
var visitor = new ApplyAttributeTypeVisitor(
compilation, hasDynamicAttribute, dynamicAttributeData, options, tupleElementNames
compilation, hasDynamicAttribute, dynamicAttributeData,
options, tupleElementNames,
nullability, nullableAttributeData
);
if (typeChildrenOnly) {
return inputType.VisitChildren(visitor);
@ -89,16 +105,22 @@ namespace ICSharpCode.Decompiler.TypeSystem @@ -89,16 +105,22 @@ namespace ICSharpCode.Decompiler.TypeSystem
readonly bool[] dynamicAttributeData;
readonly TypeSystemOptions options;
readonly string[] tupleElementNames;
readonly Nullability defaultNullability;
readonly Nullability[] nullableAttributeData;
int dynamicTypeIndex = 0;
int tupleTypeIndex = 0;
int nullabilityTypeIndex = 0;
private ApplyAttributeTypeVisitor(ICompilation compilation, bool hasDynamicAttribute, bool[] dynamicAttributeData, TypeSystemOptions options, string[] tupleElementNames)
private ApplyAttributeTypeVisitor(ICompilation compilation, bool hasDynamicAttribute, bool[] dynamicAttributeData, TypeSystemOptions options, string[] tupleElementNames,
Nullability defaultNullability, Nullability[] nullableAttributeData)
{
this.compilation = compilation ?? throw new ArgumentNullException(nameof(compilation));
this.hasDynamicAttribute = hasDynamicAttribute;
this.dynamicAttributeData = dynamicAttributeData;
this.options = options;
this.tupleElementNames = tupleElementNames;
this.defaultNullability = defaultNullability;
this.nullableAttributeData = nullableAttributeData;
}
public override IType VisitModOpt(ModifiedType type)
@ -123,10 +145,20 @@ namespace ICSharpCode.Decompiler.TypeSystem @@ -123,10 +145,20 @@ namespace ICSharpCode.Decompiler.TypeSystem
return base.VisitPointerType(type);
}
Nullability GetNullability()
{
if (nullabilityTypeIndex < nullableAttributeData?.Length)
return nullableAttributeData[nullabilityTypeIndex];
else
return defaultNullability;
}
public override IType VisitArrayType(ArrayType type)
{
var nullability = GetNullability();
dynamicTypeIndex++;
return base.VisitArrayType(type);
nullabilityTypeIndex++;
return base.VisitArrayType(type).ChangeNullability(nullability);
}
public override IType VisitByReferenceType(ByReferenceType type)
@ -188,6 +220,7 @@ namespace ICSharpCode.Decompiler.TypeSystem @@ -188,6 +220,7 @@ namespace ICSharpCode.Decompiler.TypeSystem
var arguments = new IType[type.TypeArguments.Count];
for (int i = 0; i < type.TypeArguments.Count; i++) {
dynamicTypeIndex++;
nullabilityTypeIndex++;
arguments[i] = type.TypeArguments[i].AcceptVisitor(this);
changed = changed || arguments[i] != type.TypeArguments[i];
}
@ -198,14 +231,15 @@ namespace ICSharpCode.Decompiler.TypeSystem @@ -198,14 +231,15 @@ namespace ICSharpCode.Decompiler.TypeSystem
public override IType VisitTypeDefinition(ITypeDefinition type)
{
IType newType = type;
if (type.KnownTypeCode == KnownTypeCode.Object && hasDynamicAttribute) {
if (dynamicAttributeData == null || dynamicTypeIndex >= dynamicAttributeData.Length)
return SpecialType.Dynamic;
newType = SpecialType.Dynamic;
if (dynamicAttributeData[dynamicTypeIndex])
return SpecialType.Dynamic;
return type;
newType = SpecialType.Dynamic;
}
return type;
Nullability nullability = GetNullability();
return newType.ChangeNullability(nullability);
}
}
}

10
ICSharpCode.Decompiler/TypeSystem/ArrayType.cs

@ -56,7 +56,15 @@ namespace ICSharpCode.Decompiler.TypeSystem @@ -56,7 +56,15 @@ namespace ICSharpCode.Decompiler.TypeSystem
public int Dimensions {
get { return dimensions; }
}
public override IType ChangeNullability(Nullability nullability)
{
if (nullability == Nullability.Oblivious)
return this;
else
return new NullabilityAnnotatedType(this, nullability);
}
public override string NameSuffix {
get {
return "[" + new string(',', dimensions-1) + "]";

7
ICSharpCode.Decompiler/TypeSystem/DecompilerTypeSystem.cs

@ -94,6 +94,11 @@ namespace ICSharpCode.Decompiler.TypeSystem @@ -94,6 +94,11 @@ namespace ICSharpCode.Decompiler.TypeSystem
/// </summary>
RefStructs = 0x100,
/// <summary>
/// If this option is active, [NullableAttribute] is removed and reference types with
/// nullability annotations are used instead.
/// </summary>
NullabilityAnnotations = 0x200,
/// <summary>
/// Default settings: typical options for the decompiler, with all C# languages features enabled.
/// </summary>
Default = Dynamic | Tuple | ExtensionMethods | DecimalConstants | ReadOnlyStructsAndParameters | RefStructs
@ -122,6 +127,8 @@ namespace ICSharpCode.Decompiler.TypeSystem @@ -122,6 +127,8 @@ namespace ICSharpCode.Decompiler.TypeSystem
typeSystemOptions |= TypeSystemOptions.RefStructs;
if (settings.IntroduceReadonlyAndInModifiers)
typeSystemOptions |= TypeSystemOptions.ReadOnlyStructsAndParameters;
if (settings.NullableReferenceTypes)
typeSystemOptions |= TypeSystemOptions.NullabilityAnnotations;
return typeSystemOptions;
}

10
ICSharpCode.Decompiler/TypeSystem/IType.cs

@ -69,6 +69,16 @@ namespace ICSharpCode.Decompiler.TypeSystem @@ -69,6 +69,16 @@ namespace ICSharpCode.Decompiler.TypeSystem
/// </summary>
bool IsByRefLike { get; }
/// <summary>
/// Gets the nullability annotation on this type.
/// </summary>
Nullability Nullability { get; }
/// <summary>
/// Creates a new type that is a copy of this type, with the changed nullability annotation.
/// </summary>
IType ChangeNullability(Nullability newNullability);
/// <summary>
/// Gets the underlying type definition.
/// Can return null for types which do not have a type definition (for example arrays, pointers, type parameters).

9
ICSharpCode.Decompiler/TypeSystem/Implementation/AbstractType.cs

@ -18,6 +18,7 @@ @@ -18,6 +18,7 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using ICSharpCode.Decompiler.Util;
@ -55,6 +56,14 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation @@ -55,6 +56,14 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation
public virtual bool IsByRefLike => false;
public virtual Nullability Nullability => Nullability.Oblivious;
public virtual IType ChangeNullability(Nullability nullability)
{
Debug.Assert(nullability == Nullability.Oblivious);
return this;
}
public abstract TypeKind Kind { get; }
public virtual int TypeParameterCount {

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

@ -192,7 +192,16 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation @@ -192,7 +192,16 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation
}
bool IType.IsByRefLike => false;
public Nullability Nullability => Nullability.Oblivious;
public IType ChangeNullability(Nullability nullability)
{
if (nullability == Nullability.Oblivious)
return this;
else
return new NullabilityAnnotatedType(this, nullability);
}
IType IType.DeclaringType {
get { return null; }
}

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

@ -204,6 +204,8 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation @@ -204,6 +204,8 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation
return (options & TypeSystemOptions.ReadOnlyStructsAndParameters) != 0;
case "IsByRefLikeAttribute":
return (options & TypeSystemOptions.RefStructs) != 0;
case "NullableAttribute":
return (options & TypeSystemOptions.NullabilityAnnotations) != 0;
default:
return false;
}

109
ICSharpCode.Decompiler/TypeSystem/Implementation/DecoratedType.cs

@ -0,0 +1,109 @@ @@ -0,0 +1,109 @@
using System;
using System.Collections.Generic;
using System.Text;
namespace ICSharpCode.Decompiler.TypeSystem.Implementation
{
public abstract class DecoratedType : IType
{
protected readonly IType baseType;
protected DecoratedType(IType baseType)
{
this.baseType = baseType;
}
TypeKind IType.Kind => baseType.Kind;
bool? IType.IsReferenceType => baseType.IsReferenceType;
bool IType.IsByRefLike => baseType.IsByRefLike;
Nullability IType.Nullability => baseType.Nullability;
public abstract IType ChangeNullability(Nullability nullability);
IType IType.DeclaringType => baseType.DeclaringType;
int IType.TypeParameterCount => baseType.TypeParameterCount;
IReadOnlyList<ITypeParameter> IType.TypeParameters => baseType.TypeParameters;
IReadOnlyList<IType> IType.TypeArguments => baseType.TypeArguments;
IEnumerable<IType> IType.DirectBaseTypes => baseType.DirectBaseTypes;
string INamedElement.FullName => baseType.FullName;
string INamedElement.Name => baseType.Name;
string INamedElement.ReflectionName => baseType.ReflectionName;
string INamedElement.Namespace => baseType.Namespace;
public abstract IType AcceptVisitor(TypeVisitor visitor);
public abstract bool Equals(IType other);
IEnumerable<IMethod> IType.GetAccessors(Predicate<IMethod> filter, GetMemberOptions options)
{
return baseType.GetAccessors(filter, options);
}
IEnumerable<IMethod> IType.GetConstructors(Predicate<IMethod> filter, GetMemberOptions options)
{
return baseType.GetConstructors(filter, options);
}
ITypeDefinition IType.GetDefinition()
{
return baseType.GetDefinition();
}
IEnumerable<IEvent> IType.GetEvents(Predicate<IEvent> filter, GetMemberOptions options)
{
return baseType.GetEvents(filter, options);
}
IEnumerable<IField> IType.GetFields(Predicate<IField> filter, GetMemberOptions options)
{
return baseType.GetFields(filter, options);
}
IEnumerable<IMember> IType.GetMembers(Predicate<IMember> filter, GetMemberOptions options)
{
return baseType.GetMembers(filter, options);
}
IEnumerable<IMethod> IType.GetMethods(Predicate<IMethod> filter, GetMemberOptions options)
{
return baseType.GetMethods(filter, options);
}
IEnumerable<IMethod> IType.GetMethods(IReadOnlyList<IType> typeArguments, Predicate<IMethod> filter, GetMemberOptions options)
{
return baseType.GetMethods(typeArguments, filter, options);
}
IEnumerable<IType> IType.GetNestedTypes(Predicate<ITypeDefinition> filter, GetMemberOptions options)
{
return baseType.GetNestedTypes(filter, options);
}
IEnumerable<IType> IType.GetNestedTypes(IReadOnlyList<IType> typeArguments, Predicate<ITypeDefinition> filter, GetMemberOptions options)
{
return baseType.GetNestedTypes(typeArguments, filter, options);
}
IEnumerable<IProperty> IType.GetProperties(Predicate<IProperty> filter, GetMemberOptions options)
{
return baseType.GetProperties(filter, options);
}
TypeParameterSubstitution IType.GetSubstitution()
{
return baseType.GetSubstitution();
}
public abstract IType VisitChildren(TypeVisitor visitor);
}
}

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

@ -39,6 +39,7 @@ namespace ICSharpCode.Decompiler.TypeSystem @@ -39,6 +39,7 @@ namespace ICSharpCode.Decompiler.TypeSystem
Extension,
Dynamic,
TupleElementNames,
Nullable,
Conditional,
Obsolete,
IsReadOnly,
@ -102,6 +103,7 @@ namespace ICSharpCode.Decompiler.TypeSystem @@ -102,6 +103,7 @@ namespace ICSharpCode.Decompiler.TypeSystem
new TopLevelTypeName("System.Runtime.CompilerServices", nameof(ExtensionAttribute)),
new TopLevelTypeName("System.Runtime.CompilerServices", nameof(DynamicAttribute)),
new TopLevelTypeName("System.Runtime.CompilerServices", nameof(TupleElementNamesAttribute)),
new TopLevelTypeName("System.Runtime.CompilerServices", "NullableAttribute"),
new TopLevelTypeName("System.Diagnostics", nameof(ConditionalAttribute)),
new TopLevelTypeName("System", nameof(ObsoleteAttribute)),
new TopLevelTypeName("System.Runtime.CompilerServices", "IsReadOnlyAttribute"),

10
ICSharpCode.Decompiler/TypeSystem/Implementation/MetadataTypeDefinition.cs

@ -263,6 +263,16 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation @@ -263,6 +263,16 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation
IReadOnlyList<IType> IType.TypeArguments => TypeParameters;
Nullability IType.Nullability => Nullability.Oblivious;
public IType ChangeNullability(Nullability nullability)
{
if (nullability == Nullability.Oblivious)
return this;
else
return new NullabilityAnnotatedType(this, nullability);
}
public IEnumerable<IType> DirectBaseTypes {
get {
var baseTypes = LazyInit.VolatileRead(ref this.directBaseTypes);

9
ICSharpCode.Decompiler/TypeSystem/Implementation/MinimalCorlib.cs

@ -176,6 +176,15 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation @@ -176,6 +176,15 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation
}
bool IType.IsByRefLike => false;
Nullability IType.Nullability => Nullability.Oblivious;
IType IType.ChangeNullability(Nullability nullability)
{
if (nullability == Nullability.Oblivious)
return this;
else
return new NullabilityAnnotatedType(this, nullability);
}
int IType.TypeParameterCount => KnownTypeReference.Get(typeCode).TypeParameterCount;

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

@ -0,0 +1,55 @@ @@ -0,0 +1,55 @@
using System.Diagnostics;
namespace ICSharpCode.Decompiler.TypeSystem.Implementation
{
public sealed class NullabilityAnnotatedType : DecoratedType, IType
{
readonly Nullability nullability;
internal NullabilityAnnotatedType(IType type, Nullability nullability)
: base(type)
{
Debug.Assert(nullability != Nullability.Oblivious);
Debug.Assert(!(type is ParameterizedType || type is NullabilityAnnotatedType));
this.nullability = nullability;
}
public Nullability Nullability => nullability;
public IType ElementType => baseType;
public override IType AcceptVisitor(TypeVisitor visitor)
{
return visitor.VisitNullabilityAnnotatedType(this);
}
public override bool Equals(IType other)
{
return other is NullabilityAnnotatedType nat
&& nat.nullability == nullability
&& nat.baseType.Equals(baseType);
}
public override IType ChangeNullability(Nullability nullability)
{
if (nullability == this.nullability)
return this;
else
return baseType.ChangeNullability(nullability);
}
public override IType VisitChildren(TypeVisitor visitor)
{
IType newBase = baseType.AcceptVisitor(visitor);
if (newBase != baseType)
return new NullabilityAnnotatedType(newBase, nullability);
else
return this;
}
public override string ToString()
{
return baseType.ToString() + (nullability == Nullability.Nullable ? "?" : "!");
}
}
}

2
ICSharpCode.Decompiler/TypeSystem/MetadataModule.cs

@ -301,7 +301,7 @@ namespace ICSharpCode.Decompiler.TypeSystem @@ -301,7 +301,7 @@ namespace ICSharpCode.Decompiler.TypeSystem
{
// resolve without substituting dynamic/tuple types
var ty = ResolveType(declaringTypeReference, context,
options & ~(TypeSystemOptions.Dynamic | TypeSystemOptions.Tuple));
options & ~(TypeSystemOptions.Dynamic | TypeSystemOptions.Tuple | TypeSystemOptions.NullabilityAnnotations));
// but substitute tuple types in type arguments:
ty = ApplyAttributeTypeVisitor.ApplyAttributesToType(ty, Compilation, null, metadata, options, typeChildrenOnly: true);
return ty;

10
ICSharpCode.Decompiler/TypeSystem/ModifiedType.cs

@ -42,6 +42,16 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation @@ -42,6 +42,16 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation
public override bool? IsReferenceType => elementType.IsReferenceType;
public override bool IsByRefLike => elementType.IsByRefLike;
public override Nullability Nullability => elementType.Nullability;
public override IType ChangeNullability(Nullability nullability)
{
IType newElementType = elementType.ChangeNullability(nullability);
if (newElementType == elementType)
return this;
else
return new ModifiedType(modifier, newElementType, kind == TypeKind.ModReq);
}
public override ITypeDefinition GetDefinition()
{

15
ICSharpCode.Decompiler/TypeSystem/NormalizeTypeVisitor.cs

@ -18,6 +18,7 @@ namespace ICSharpCode.Decompiler.TypeSystem @@ -18,6 +18,7 @@ namespace ICSharpCode.Decompiler.TypeSystem
TupleToUnderlyingType = true,
RemoveModOpt = true,
RemoveModReq = true,
RemoveNullability = true,
};
public bool EquivalentTypes(IType a, IType b)
@ -33,6 +34,7 @@ namespace ICSharpCode.Decompiler.TypeSystem @@ -33,6 +34,7 @@ namespace ICSharpCode.Decompiler.TypeSystem
public bool ReplaceMethodTypeParametersWithDummy = true;
public bool DynamicAndObject = true;
public bool TupleToUnderlyingType = true;
public bool RemoveNullability = true;
public override IType VisitTypeParameter(ITypeParameter type)
{
@ -50,7 +52,10 @@ namespace ICSharpCode.Decompiler.TypeSystem @@ -50,7 +52,10 @@ namespace ICSharpCode.Decompiler.TypeSystem
if (DynamicAndObject && type.KnownTypeCode == KnownTypeCode.Object) {
// Instead of normalizing dynamic->object,
// we do this the opposite direction, so that we don't need a compilation to find the object type.
return SpecialType.Dynamic;
if (RemoveNullability)
return SpecialType.Dynamic;
else
return SpecialType.Dynamic.ChangeNullability(type.Nullability);
}
return base.VisitTypeDefinition(type);
}
@ -64,6 +69,14 @@ namespace ICSharpCode.Decompiler.TypeSystem @@ -64,6 +69,14 @@ namespace ICSharpCode.Decompiler.TypeSystem
}
}
public override IType VisitNullabilityAnnotatedType(NullabilityAnnotatedType type)
{
if (RemoveNullability)
return type.ElementType.AcceptVisitor(this);
else
return base.VisitNullabilityAnnotatedType(type);
}
public override IType VisitModOpt(ModifiedType type)
{
if (RemoveModOpt) {

13
ICSharpCode.Decompiler/TypeSystem/Nullability.cs

@ -0,0 +1,13 @@ @@ -0,0 +1,13 @@
using System;
using System.Collections.Generic;
using System.Text;
namespace ICSharpCode.Decompiler.TypeSystem
{
public enum Nullability : byte
{
Oblivious = 0,
NotNullable = 1,
Nullable = 2
}
}

14
ICSharpCode.Decompiler/TypeSystem/ParameterizedType.cs

@ -84,7 +84,17 @@ namespace ICSharpCode.Decompiler.TypeSystem @@ -84,7 +84,17 @@ namespace ICSharpCode.Decompiler.TypeSystem
public bool? IsReferenceType => genericType.IsReferenceType;
public bool IsByRefLike => genericType.IsByRefLike;
public Nullability Nullability => genericType.Nullability;
public IType ChangeNullability(Nullability nullability)
{
IType newGenericType = genericType.ChangeNullability(nullability);
if (newGenericType == genericType)
return this;
else
return new ParameterizedType(newGenericType, typeArguments);
}
public IType DeclaringType {
get {
IType declaringType = genericType.DeclaringType;
@ -154,7 +164,7 @@ namespace ICSharpCode.Decompiler.TypeSystem @@ -154,7 +164,7 @@ namespace ICSharpCode.Decompiler.TypeSystem
/// </summary>
public ITypeDefinition GetDefinition()
{
return genericType as ITypeDefinition;
return genericType.GetDefinition();
}
/// <summary>

5
ICSharpCode.Decompiler/TypeSystem/TypeVisitor.cs

@ -75,5 +75,10 @@ namespace ICSharpCode.Decompiler.TypeSystem @@ -75,5 +75,10 @@ namespace ICSharpCode.Decompiler.TypeSystem
{
return type.VisitChildren(this);
}
public virtual IType VisitNullabilityAnnotatedType(NullabilityAnnotatedType type)
{
return type.VisitChildren(this);
}
}
}

1
ILSpy/Languages/CSharpLanguage.cs

@ -101,6 +101,7 @@ namespace ICSharpCode.ILSpy @@ -101,6 +101,7 @@ namespace ICSharpCode.ILSpy
new LanguageVersion(Decompiler.CSharp.LanguageVersion.CSharp7_1.ToString(), "C# 7.1 / VS 2017.3"),
new LanguageVersion(Decompiler.CSharp.LanguageVersion.CSharp7_2.ToString(), "C# 7.2 / VS 2017.4"),
new LanguageVersion(Decompiler.CSharp.LanguageVersion.CSharp7_3.ToString(), "C# 7.3 / VS 2017.7"),
new LanguageVersion(Decompiler.CSharp.LanguageVersion.CSharp8_0.ToString(), "C# 8.0 / VS 2019"),
};
}
return versions;

Loading…
Cancel
Save