mirror of
https://github.com/esiur/esiur-dotnet.git
synced 2025-05-07 12:02:59 +00:00
Nullables
This commit is contained in:
parent
537ead3887
commit
4603f66499
64
Esiur/Data/NullabilityInfo.cs
Normal file
64
Esiur/Data/NullabilityInfo.cs
Normal file
@ -0,0 +1,64 @@
|
|||||||
|
// Licensed to the .NET Foundation under one or more agreements.
|
||||||
|
// The .NET Foundation licenses this file to you under the MIT license.
|
||||||
|
|
||||||
|
using System;
|
||||||
|
|
||||||
|
namespace Esiur.Data
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// A class that represents nullability info
|
||||||
|
/// </summary>
|
||||||
|
public sealed class NullabilityInfo
|
||||||
|
{
|
||||||
|
internal NullabilityInfo(Type type, NullabilityState readState, NullabilityState writeState,
|
||||||
|
NullabilityInfo? elementType, NullabilityInfo[] typeArguments)
|
||||||
|
{
|
||||||
|
Type = type;
|
||||||
|
ReadState = readState;
|
||||||
|
WriteState = writeState;
|
||||||
|
ElementType = elementType;
|
||||||
|
GenericTypeArguments = typeArguments;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The <see cref="System.Type" /> of the member or generic parameter
|
||||||
|
/// to which this NullabilityInfo belongs
|
||||||
|
/// </summary>
|
||||||
|
public Type Type { get; }
|
||||||
|
/// <summary>
|
||||||
|
/// The nullability read state of the member
|
||||||
|
/// </summary>
|
||||||
|
public NullabilityState ReadState { get; internal set; }
|
||||||
|
/// <summary>
|
||||||
|
/// The nullability write state of the member
|
||||||
|
/// </summary>
|
||||||
|
public NullabilityState WriteState { get; internal set; }
|
||||||
|
/// <summary>
|
||||||
|
/// If the member type is an array, gives the <see cref="NullabilityInfo" /> of the elements of the array, null otherwise
|
||||||
|
/// </summary>
|
||||||
|
public NullabilityInfo? ElementType { get; }
|
||||||
|
/// <summary>
|
||||||
|
/// If the member type is a generic type, gives the array of <see cref="NullabilityInfo" /> for each type parameter
|
||||||
|
/// </summary>
|
||||||
|
public NullabilityInfo[] GenericTypeArguments { get; }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// An enum that represents nullability state
|
||||||
|
/// </summary>
|
||||||
|
public enum NullabilityState
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Nullability context not enabled (oblivious)
|
||||||
|
/// </summary>
|
||||||
|
Unknown,
|
||||||
|
/// <summary>
|
||||||
|
/// Non nullable value or reference type
|
||||||
|
/// </summary>
|
||||||
|
NotNull,
|
||||||
|
/// <summary>
|
||||||
|
/// Nullable value or reference type
|
||||||
|
/// </summary>
|
||||||
|
Nullable
|
||||||
|
}
|
||||||
|
}
|
714
Esiur/Data/NullabilityInfoContext.cs
Normal file
714
Esiur/Data/NullabilityInfoContext.cs
Normal file
@ -0,0 +1,714 @@
|
|||||||
|
// Licensed to the .NET Foundation under one or more agreements.
|
||||||
|
// The .NET Foundation licenses this file to you under the MIT license.
|
||||||
|
|
||||||
|
using Esiur.Data;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Collections.ObjectModel;
|
||||||
|
using System.Diagnostics;
|
||||||
|
using System.IO;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Reflection;
|
||||||
|
using System.Reflection.Metadata;
|
||||||
|
|
||||||
|
namespace Esiur.Data
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Provides APIs for populating nullability information/context from reflection members:
|
||||||
|
/// <see cref="ParameterInfo"/>, <see cref="FieldInfo"/>, <see cref="PropertyInfo"/> and <see cref="EventInfo"/>.
|
||||||
|
/// </summary>
|
||||||
|
public sealed class NullabilityInfoContext
|
||||||
|
{
|
||||||
|
private const string CompilerServicesNameSpace = "System.Runtime.CompilerServices";
|
||||||
|
private readonly Dictionary<Module, NotAnnotatedStatus> _publicOnlyModules = new();
|
||||||
|
private readonly Dictionary<MemberInfo, NullabilityState> _context = new();
|
||||||
|
|
||||||
|
internal static bool IsSupported { get; } =
|
||||||
|
AppContext.TryGetSwitch("System.Reflection.NullabilityInfoContext.IsSupported", out bool isSupported) ? isSupported : true;
|
||||||
|
|
||||||
|
[Flags]
|
||||||
|
private enum NotAnnotatedStatus
|
||||||
|
{
|
||||||
|
None = 0x0, // no restriction, all members annotated
|
||||||
|
Private = 0x1, // private members not annotated
|
||||||
|
Internal = 0x2 // internal members not annotated
|
||||||
|
}
|
||||||
|
|
||||||
|
private NullabilityState? GetNullableContext(MemberInfo? memberInfo)
|
||||||
|
{
|
||||||
|
while (memberInfo != null)
|
||||||
|
{
|
||||||
|
if (_context.TryGetValue(memberInfo, out NullabilityState state))
|
||||||
|
{
|
||||||
|
return state;
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (CustomAttributeData attribute in memberInfo.GetCustomAttributesData())
|
||||||
|
{
|
||||||
|
if (attribute.AttributeType.Name == "NullableContextAttribute" &&
|
||||||
|
attribute.AttributeType.Namespace == CompilerServicesNameSpace &&
|
||||||
|
attribute.ConstructorArguments.Count == 1)
|
||||||
|
{
|
||||||
|
state = TranslateByte(attribute.ConstructorArguments[0].Value);
|
||||||
|
_context.Add(memberInfo, state);
|
||||||
|
return state;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
memberInfo = memberInfo.DeclaringType;
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Populates <see cref="NullabilityInfo" /> for the given <see cref="ParameterInfo" />.
|
||||||
|
/// If the nullablePublicOnly feature is set for an assembly, like it does in .NET SDK, the private and/or internal member's
|
||||||
|
/// nullability attributes are omitted, in this case the API will return NullabilityState.Unknown state.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="parameterInfo">The parameter which nullability info gets populated</param>
|
||||||
|
/// <exception cref="ArgumentNullException">If the parameterInfo parameter is null</exception>
|
||||||
|
/// <returns><see cref="NullabilityInfo" /></returns>
|
||||||
|
public NullabilityInfo Create(ParameterInfo parameterInfo)
|
||||||
|
{
|
||||||
|
if (parameterInfo == null)
|
||||||
|
throw new ArgumentNullException();
|
||||||
|
|
||||||
|
EnsureIsSupported();
|
||||||
|
|
||||||
|
IList<CustomAttributeData> attributes = parameterInfo.GetCustomAttributesData();
|
||||||
|
NullableAttributeStateParser parser = parameterInfo.Member is MethodBase method && IsPrivateOrInternalMethodAndAnnotationDisabled(method)
|
||||||
|
? NullableAttributeStateParser.Unknown
|
||||||
|
: CreateParser(attributes);
|
||||||
|
NullabilityInfo nullability = GetNullabilityInfo(parameterInfo.Member, parameterInfo.ParameterType, parser);
|
||||||
|
|
||||||
|
if (nullability.ReadState != NullabilityState.Unknown)
|
||||||
|
{
|
||||||
|
CheckParameterMetadataType(parameterInfo, nullability);
|
||||||
|
}
|
||||||
|
|
||||||
|
CheckNullabilityAttributes(nullability, attributes);
|
||||||
|
return nullability;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void CheckParameterMetadataType(ParameterInfo parameter, NullabilityInfo nullability)
|
||||||
|
{
|
||||||
|
if (parameter.Member is MethodInfo method)
|
||||||
|
{
|
||||||
|
MethodInfo metaMethod = GetMethodMetadataDefinition(method);
|
||||||
|
ParameterInfo? metaParameter = null;
|
||||||
|
if (string.IsNullOrEmpty(parameter.Name))
|
||||||
|
{
|
||||||
|
metaParameter = metaMethod.ReturnParameter;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ParameterInfo[] parameters = metaMethod.GetParameters();
|
||||||
|
for (int i = 0; i < parameters.Length; i++)
|
||||||
|
{
|
||||||
|
if (parameter.Position == i &&
|
||||||
|
parameter.Name == parameters[i].Name)
|
||||||
|
{
|
||||||
|
metaParameter = parameters[i];
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (metaParameter != null)
|
||||||
|
{
|
||||||
|
CheckGenericParameters(nullability, metaMethod, metaParameter.ParameterType, parameter.Member.ReflectedType);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static MethodInfo GetMethodMetadataDefinition(MethodInfo method)
|
||||||
|
{
|
||||||
|
if (method.IsGenericMethod && !method.IsGenericMethodDefinition)
|
||||||
|
{
|
||||||
|
method = method.GetGenericMethodDefinition();
|
||||||
|
}
|
||||||
|
|
||||||
|
return (MethodInfo)GetMemberMetadataDefinition(method);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void CheckNullabilityAttributes(NullabilityInfo nullability, IList<CustomAttributeData> attributes)
|
||||||
|
{
|
||||||
|
var codeAnalysisReadState = NullabilityState.Unknown;
|
||||||
|
var codeAnalysisWriteState = NullabilityState.Unknown;
|
||||||
|
|
||||||
|
foreach (CustomAttributeData attribute in attributes)
|
||||||
|
{
|
||||||
|
if (attribute.AttributeType.Namespace == "System.Diagnostics.CodeAnalysis")
|
||||||
|
{
|
||||||
|
if (attribute.AttributeType.Name == "NotNullAttribute")
|
||||||
|
{
|
||||||
|
codeAnalysisReadState = NullabilityState.NotNull;
|
||||||
|
}
|
||||||
|
else if ((attribute.AttributeType.Name == "MaybeNullAttribute" ||
|
||||||
|
attribute.AttributeType.Name == "MaybeNullWhenAttribute") &&
|
||||||
|
codeAnalysisReadState == NullabilityState.Unknown &&
|
||||||
|
!IsValueTypeOrValueTypeByRef(nullability.Type))
|
||||||
|
{
|
||||||
|
codeAnalysisReadState = NullabilityState.Nullable;
|
||||||
|
}
|
||||||
|
else if (attribute.AttributeType.Name == "DisallowNullAttribute")
|
||||||
|
{
|
||||||
|
codeAnalysisWriteState = NullabilityState.NotNull;
|
||||||
|
}
|
||||||
|
else if (attribute.AttributeType.Name == "AllowNullAttribute" &&
|
||||||
|
codeAnalysisWriteState == NullabilityState.Unknown &&
|
||||||
|
!IsValueTypeOrValueTypeByRef(nullability.Type))
|
||||||
|
{
|
||||||
|
codeAnalysisWriteState = NullabilityState.Nullable;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (codeAnalysisReadState != NullabilityState.Unknown)
|
||||||
|
{
|
||||||
|
nullability.ReadState = codeAnalysisReadState;
|
||||||
|
}
|
||||||
|
if (codeAnalysisWriteState != NullabilityState.Unknown)
|
||||||
|
{
|
||||||
|
nullability.WriteState = codeAnalysisWriteState;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Populates <see cref="NullabilityInfo" /> for the given <see cref="PropertyInfo" />.
|
||||||
|
/// If the nullablePublicOnly feature is set for an assembly, like it does in .NET SDK, the private and/or internal member's
|
||||||
|
/// nullability attributes are omitted, in this case the API will return NullabilityState.Unknown state.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="propertyInfo">The parameter which nullability info gets populated</param>
|
||||||
|
/// <exception cref="ArgumentNullException">If the propertyInfo parameter is null</exception>
|
||||||
|
/// <returns><see cref="NullabilityInfo" /></returns>
|
||||||
|
public NullabilityInfo Create(PropertyInfo propertyInfo)
|
||||||
|
{
|
||||||
|
if (propertyInfo == null)
|
||||||
|
throw new ArgumentNullException();
|
||||||
|
|
||||||
|
EnsureIsSupported();
|
||||||
|
|
||||||
|
MethodInfo? getter = propertyInfo.GetGetMethod(true);
|
||||||
|
MethodInfo? setter = propertyInfo.GetSetMethod(true);
|
||||||
|
bool annotationsDisabled = (getter == null || IsPrivateOrInternalMethodAndAnnotationDisabled(getter))
|
||||||
|
&& (setter == null || IsPrivateOrInternalMethodAndAnnotationDisabled(setter));
|
||||||
|
NullableAttributeStateParser parser = annotationsDisabled ? NullableAttributeStateParser.Unknown : CreateParser(propertyInfo.GetCustomAttributesData());
|
||||||
|
NullabilityInfo nullability = GetNullabilityInfo(propertyInfo, propertyInfo.PropertyType, parser);
|
||||||
|
|
||||||
|
if (getter != null)
|
||||||
|
{
|
||||||
|
CheckNullabilityAttributes(nullability, getter.ReturnParameter.GetCustomAttributesData());
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
nullability.ReadState = NullabilityState.Unknown;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (setter != null)
|
||||||
|
{
|
||||||
|
CheckNullabilityAttributes(nullability, setter.GetParameters().Last().GetCustomAttributesData());
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
nullability.WriteState = NullabilityState.Unknown;
|
||||||
|
}
|
||||||
|
|
||||||
|
return nullability;
|
||||||
|
}
|
||||||
|
|
||||||
|
///// <summary>
|
||||||
|
///// Populates <see cref="NullabilityInfo" /> for the given <see cref="MethodInfo" />.
|
||||||
|
///// If the nullablePublicOnly feature is set for an assembly, like it does in .NET SDK, the private and/or internal member's
|
||||||
|
///// nullability attributes are omitted, in this case the API will return NullabilityState.Unknown state.
|
||||||
|
///// </summary>
|
||||||
|
///// <param name="propertyInfo">The parameter which nullability info gets populated</param>
|
||||||
|
///// <exception cref="ArgumentNullException">If the propertyInfo parameter is null</exception>
|
||||||
|
///// <returns><see cref="NullabilityInfo" /></returns>
|
||||||
|
//public NullabilityInfo Create(MethodInfo memberInfo)
|
||||||
|
//{
|
||||||
|
// if (memberInfo == null)
|
||||||
|
// throw new ArgumentNullException();
|
||||||
|
|
||||||
|
// EnsureIsSupported();
|
||||||
|
|
||||||
|
|
||||||
|
// bool annotationsDisabled = IsPrivateOrInternalMethodAndAnnotationDisabled(memberInfo);
|
||||||
|
|
||||||
|
// NullableAttributeStateParser parser = annotationsDisabled ? NullableAttributeStateParser.Unknown : CreateParser(memberInfo.GetCustomAttributesData());
|
||||||
|
// NullabilityInfo nullability = GetNullabilityInfo(memberInfo, memberInfo.ReturnType, parser);
|
||||||
|
|
||||||
|
|
||||||
|
// CheckNullabilityAttributes(nullability, memberInfo.ReturnParameter.GetCustomAttributesData());
|
||||||
|
|
||||||
|
// return nullability;
|
||||||
|
//}
|
||||||
|
|
||||||
|
private bool IsPrivateOrInternalMethodAndAnnotationDisabled(MethodBase method)
|
||||||
|
{
|
||||||
|
if ((method.IsPrivate || method.IsFamilyAndAssembly || method.IsAssembly) &&
|
||||||
|
IsPublicOnly(method.IsPrivate, method.IsFamilyAndAssembly, method.IsAssembly, method.Module))
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Populates <see cref="NullabilityInfo" /> for the given <see cref="EventInfo" />.
|
||||||
|
/// If the nullablePublicOnly feature is set for an assembly, like it does in .NET SDK, the private and/or internal member's
|
||||||
|
/// nullability attributes are omitted, in this case the API will return NullabilityState.Unknown state.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="eventInfo">The parameter which nullability info gets populated</param>
|
||||||
|
/// <exception cref="ArgumentNullException">If the eventInfo parameter is null</exception>
|
||||||
|
/// <returns><see cref="NullabilityInfo" /></returns>
|
||||||
|
public NullabilityInfo Create(EventInfo eventInfo)
|
||||||
|
{
|
||||||
|
if (eventInfo == null)
|
||||||
|
throw new ArgumentNullException();
|
||||||
|
|
||||||
|
EnsureIsSupported();
|
||||||
|
|
||||||
|
return GetNullabilityInfo(eventInfo, eventInfo.EventHandlerType!, CreateParser(eventInfo.GetCustomAttributesData()));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Populates <see cref="NullabilityInfo" /> for the given <see cref="FieldInfo" />
|
||||||
|
/// If the nullablePublicOnly feature is set for an assembly, like it does in .NET SDK, the private and/or internal member's
|
||||||
|
/// nullability attributes are omitted, in this case the API will return NullabilityState.Unknown state.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="fieldInfo">The parameter which nullability info gets populated</param>
|
||||||
|
/// <exception cref="ArgumentNullException">If the fieldInfo parameter is null</exception>
|
||||||
|
/// <returns><see cref="NullabilityInfo" /></returns>
|
||||||
|
public NullabilityInfo Create(FieldInfo fieldInfo)
|
||||||
|
{
|
||||||
|
if (fieldInfo == null)
|
||||||
|
throw new ArgumentNullException();
|
||||||
|
|
||||||
|
EnsureIsSupported();
|
||||||
|
|
||||||
|
IList<CustomAttributeData> attributes = fieldInfo.GetCustomAttributesData();
|
||||||
|
NullableAttributeStateParser parser = IsPrivateOrInternalFieldAndAnnotationDisabled(fieldInfo) ? NullableAttributeStateParser.Unknown : CreateParser(attributes);
|
||||||
|
NullabilityInfo nullability = GetNullabilityInfo(fieldInfo, fieldInfo.FieldType, parser);
|
||||||
|
CheckNullabilityAttributes(nullability, attributes);
|
||||||
|
return nullability;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void EnsureIsSupported()
|
||||||
|
{
|
||||||
|
if (!IsSupported)
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool IsPrivateOrInternalFieldAndAnnotationDisabled(FieldInfo fieldInfo)
|
||||||
|
{
|
||||||
|
if ((fieldInfo.IsPrivate || fieldInfo.IsFamilyAndAssembly || fieldInfo.IsAssembly) &&
|
||||||
|
IsPublicOnly(fieldInfo.IsPrivate, fieldInfo.IsFamilyAndAssembly, fieldInfo.IsAssembly, fieldInfo.Module))
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool IsPublicOnly(bool isPrivate, bool isFamilyAndAssembly, bool isAssembly, Module module)
|
||||||
|
{
|
||||||
|
if (!_publicOnlyModules.TryGetValue(module, out NotAnnotatedStatus value))
|
||||||
|
{
|
||||||
|
value = PopulateAnnotationInfo(module.GetCustomAttributesData());
|
||||||
|
_publicOnlyModules.Add(module, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (value == NotAnnotatedStatus.None)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((isPrivate || isFamilyAndAssembly) && value.HasFlag(NotAnnotatedStatus.Private) ||
|
||||||
|
isAssembly && value.HasFlag(NotAnnotatedStatus.Internal))
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static NotAnnotatedStatus PopulateAnnotationInfo(IList<CustomAttributeData> customAttributes)
|
||||||
|
{
|
||||||
|
foreach (CustomAttributeData attribute in customAttributes)
|
||||||
|
{
|
||||||
|
if (attribute.AttributeType.Name == "NullablePublicOnlyAttribute" &&
|
||||||
|
attribute.AttributeType.Namespace == CompilerServicesNameSpace &&
|
||||||
|
attribute.ConstructorArguments.Count == 1)
|
||||||
|
{
|
||||||
|
if (attribute.ConstructorArguments[0].Value is bool boolValue && boolValue)
|
||||||
|
{
|
||||||
|
return NotAnnotatedStatus.Internal | NotAnnotatedStatus.Private;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return NotAnnotatedStatus.Private;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return NotAnnotatedStatus.None;
|
||||||
|
}
|
||||||
|
|
||||||
|
private NullabilityInfo GetNullabilityInfo(MemberInfo memberInfo, Type type, NullableAttributeStateParser parser)
|
||||||
|
{
|
||||||
|
int index = 0;
|
||||||
|
NullabilityInfo nullability = GetNullabilityInfo(memberInfo, type, parser, ref index);
|
||||||
|
|
||||||
|
if (nullability.ReadState != NullabilityState.Unknown)
|
||||||
|
{
|
||||||
|
TryLoadGenericMetaTypeNullability(memberInfo, nullability);
|
||||||
|
}
|
||||||
|
|
||||||
|
return nullability;
|
||||||
|
}
|
||||||
|
|
||||||
|
private NullabilityInfo GetNullabilityInfo(MemberInfo memberInfo, Type type, NullableAttributeStateParser parser, ref int index)
|
||||||
|
{
|
||||||
|
NullabilityState state = NullabilityState.Unknown;
|
||||||
|
NullabilityInfo? elementState = null;
|
||||||
|
NullabilityInfo[] genericArgumentsState = Array.Empty<NullabilityInfo>();
|
||||||
|
Type underlyingType = type;
|
||||||
|
|
||||||
|
if (underlyingType.IsByRef || underlyingType.IsPointer)
|
||||||
|
{
|
||||||
|
underlyingType = underlyingType.GetElementType()!;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (underlyingType.IsValueType)
|
||||||
|
{
|
||||||
|
if (Nullable.GetUnderlyingType(underlyingType) is { } nullableUnderlyingType)
|
||||||
|
{
|
||||||
|
underlyingType = nullableUnderlyingType;
|
||||||
|
state = NullabilityState.Nullable;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
state = NullabilityState.NotNull;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (underlyingType.IsGenericType)
|
||||||
|
{
|
||||||
|
++index;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (!parser.ParseNullableState(index++, ref state)
|
||||||
|
&& GetNullableContext(memberInfo) is { } contextState)
|
||||||
|
{
|
||||||
|
state = contextState;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (underlyingType.IsArray)
|
||||||
|
{
|
||||||
|
elementState = GetNullabilityInfo(memberInfo, underlyingType.GetElementType()!, parser, ref index);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (underlyingType.IsGenericType)
|
||||||
|
{
|
||||||
|
Type[] genericArguments = underlyingType.GetGenericArguments();
|
||||||
|
genericArgumentsState = new NullabilityInfo[genericArguments.Length];
|
||||||
|
|
||||||
|
for (int i = 0; i < genericArguments.Length; i++)
|
||||||
|
{
|
||||||
|
genericArgumentsState[i] = GetNullabilityInfo(memberInfo, genericArguments[i], parser, ref index);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return new NullabilityInfo(type, state, state, elementState, genericArgumentsState);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static NullableAttributeStateParser CreateParser(IList<CustomAttributeData> customAttributes)
|
||||||
|
{
|
||||||
|
foreach (CustomAttributeData attribute in customAttributes)
|
||||||
|
{
|
||||||
|
if (attribute.AttributeType.Name == "NullableAttribute" &&
|
||||||
|
attribute.AttributeType.Namespace == CompilerServicesNameSpace &&
|
||||||
|
attribute.ConstructorArguments.Count == 1)
|
||||||
|
{
|
||||||
|
return new NullableAttributeStateParser(attribute.ConstructorArguments[0].Value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return new NullableAttributeStateParser(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void TryLoadGenericMetaTypeNullability(MemberInfo memberInfo, NullabilityInfo nullability)
|
||||||
|
{
|
||||||
|
MemberInfo? metaMember = GetMemberMetadataDefinition(memberInfo);
|
||||||
|
Type? metaType = null;
|
||||||
|
if (metaMember is FieldInfo field)
|
||||||
|
{
|
||||||
|
metaType = field.FieldType;
|
||||||
|
}
|
||||||
|
else if (metaMember is PropertyInfo property)
|
||||||
|
{
|
||||||
|
metaType = GetPropertyMetaType(property);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (metaType != null)
|
||||||
|
{
|
||||||
|
CheckGenericParameters(nullability, metaMember!, metaType, memberInfo.ReflectedType);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static MemberInfo GetMemberMetadataDefinition(MemberInfo member)
|
||||||
|
{
|
||||||
|
Type? type = member.DeclaringType;
|
||||||
|
if ((type != null) && type.IsGenericType && !type.IsGenericTypeDefinition)
|
||||||
|
{
|
||||||
|
return GetMemberWithSameMetadataDefinitionAs(type.GetGenericTypeDefinition(), member);
|
||||||
|
}
|
||||||
|
|
||||||
|
return member;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool HasSameMetadataDefinitionAs(MemberInfo mi, MemberInfo other) { throw new NotImplementedException(); }
|
||||||
|
|
||||||
|
static MemberInfo GetMemberWithSameMetadataDefinitionAs(Type type, MemberInfo member)
|
||||||
|
{
|
||||||
|
if (member == null)
|
||||||
|
throw new ArgumentNullException();
|
||||||
|
|
||||||
|
const BindingFlags all = BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static | BindingFlags.Instance;
|
||||||
|
foreach (MemberInfo myMemberInfo in type.GetMembers(all))
|
||||||
|
{
|
||||||
|
if (HasSameMetadataDefinitionAs(myMemberInfo, member))
|
||||||
|
{
|
||||||
|
return myMemberInfo;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new Exception();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Type GetPropertyMetaType(PropertyInfo property)
|
||||||
|
{
|
||||||
|
if (property.GetGetMethod(true) is MethodInfo method)
|
||||||
|
{
|
||||||
|
return method.ReturnType;
|
||||||
|
}
|
||||||
|
|
||||||
|
return property.GetSetMethod(true)!.GetParameters()[0].ParameterType;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void CheckGenericParameters(NullabilityInfo nullability, MemberInfo metaMember, Type metaType, Type? reflectedType)
|
||||||
|
{
|
||||||
|
if (metaType.IsGenericParameter)
|
||||||
|
{
|
||||||
|
if (nullability.ReadState == NullabilityState.NotNull)
|
||||||
|
{
|
||||||
|
TryUpdateGenericParameterNullability(nullability, metaType, reflectedType);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (metaType.ContainsGenericParameters)
|
||||||
|
{
|
||||||
|
if (nullability.GenericTypeArguments.Length > 0)
|
||||||
|
{
|
||||||
|
Type[] genericArguments = metaType.GetGenericArguments();
|
||||||
|
|
||||||
|
for (int i = 0; i < genericArguments.Length; i++)
|
||||||
|
{
|
||||||
|
CheckGenericParameters(nullability.GenericTypeArguments[i], metaMember, genericArguments[i], reflectedType);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (nullability.ElementType is { } elementNullability && metaType.IsArray)
|
||||||
|
{
|
||||||
|
CheckGenericParameters(elementNullability, metaMember, metaType.GetElementType()!, reflectedType);
|
||||||
|
}
|
||||||
|
// We could also follow this branch for metaType.IsPointer, but since pointers must be unmanaged this
|
||||||
|
// will be a no-op regardless
|
||||||
|
else if (metaType.IsByRef)
|
||||||
|
{
|
||||||
|
CheckGenericParameters(nullability, metaMember, metaType.GetElementType()!, reflectedType);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool TryUpdateGenericParameterNullability(NullabilityInfo nullability, Type genericParameter, Type? reflectedType)
|
||||||
|
{
|
||||||
|
Debug.Assert(genericParameter.IsGenericParameter);
|
||||||
|
|
||||||
|
if (reflectedType is not null
|
||||||
|
&& !IsGenericMethodParameter(genericParameter)
|
||||||
|
&& TryUpdateGenericTypeParameterNullabilityFromReflectedType(nullability, genericParameter, reflectedType, reflectedType))
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (IsValueTypeOrValueTypeByRef(nullability.Type))
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
var state = NullabilityState.Unknown;
|
||||||
|
if (CreateParser(genericParameter.GetCustomAttributesData()).ParseNullableState(0, ref state))
|
||||||
|
{
|
||||||
|
nullability.ReadState = state;
|
||||||
|
nullability.WriteState = state;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (GetNullableContext(genericParameter) is { } contextState)
|
||||||
|
{
|
||||||
|
nullability.ReadState = contextState;
|
||||||
|
nullability.WriteState = contextState;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool IsGenericMethodParameter(Type genericParameter) => genericParameter.IsGenericParameter && genericParameter.DeclaringMethod != null;
|
||||||
|
|
||||||
|
private bool TryUpdateGenericTypeParameterNullabilityFromReflectedType(NullabilityInfo nullability, Type genericParameter, Type context, Type reflectedType)
|
||||||
|
{
|
||||||
|
Debug.Assert(genericParameter.IsGenericParameter && !IsGenericMethodParameter(genericParameter));
|
||||||
|
|
||||||
|
Type contextTypeDefinition = context.IsGenericType && !context.IsGenericTypeDefinition ? context.GetGenericTypeDefinition() : context;
|
||||||
|
if (genericParameter.DeclaringType == contextTypeDefinition)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
Type? baseType = contextTypeDefinition.BaseType;
|
||||||
|
if (baseType is null)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!baseType.IsGenericType
|
||||||
|
|| (baseType.IsGenericTypeDefinition ? baseType : baseType.GetGenericTypeDefinition()) != genericParameter.DeclaringType)
|
||||||
|
{
|
||||||
|
return TryUpdateGenericTypeParameterNullabilityFromReflectedType(nullability, genericParameter, baseType, reflectedType);
|
||||||
|
}
|
||||||
|
|
||||||
|
Type[] genericArguments = baseType.GetGenericArguments();
|
||||||
|
Type genericArgument = genericArguments[genericParameter.GenericParameterPosition];
|
||||||
|
if (genericArgument.IsGenericParameter)
|
||||||
|
{
|
||||||
|
return TryUpdateGenericParameterNullability(nullability, genericArgument, reflectedType);
|
||||||
|
}
|
||||||
|
|
||||||
|
NullableAttributeStateParser parser = CreateParser(contextTypeDefinition.GetCustomAttributesData());
|
||||||
|
int nullabilityStateIndex = 1; // start at 1 since index 0 is the type itself
|
||||||
|
for (int i = 0; i < genericParameter.GenericParameterPosition; i++)
|
||||||
|
{
|
||||||
|
nullabilityStateIndex += CountNullabilityStates(genericArguments[i]);
|
||||||
|
}
|
||||||
|
return TryPopulateNullabilityInfo(nullability, parser, ref nullabilityStateIndex);
|
||||||
|
|
||||||
|
static int CountNullabilityStates(Type type)
|
||||||
|
{
|
||||||
|
Type underlyingType = Nullable.GetUnderlyingType(type) ?? type;
|
||||||
|
if (underlyingType.IsGenericType)
|
||||||
|
{
|
||||||
|
int count = 1;
|
||||||
|
foreach (Type genericArgument in underlyingType.GetGenericArguments())
|
||||||
|
{
|
||||||
|
count += CountNullabilityStates(genericArgument);
|
||||||
|
}
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (underlyingType.HasElementType)
|
||||||
|
{
|
||||||
|
return (underlyingType.IsArray ? 1 : 0) + CountNullabilityStates(underlyingType.GetElementType()!);
|
||||||
|
}
|
||||||
|
|
||||||
|
return type.IsValueType ? 0 : 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static bool TryPopulateNullabilityInfo(NullabilityInfo nullability, NullableAttributeStateParser parser, ref int index)
|
||||||
|
{
|
||||||
|
bool isValueType = IsValueTypeOrValueTypeByRef(nullability.Type);
|
||||||
|
if (!isValueType)
|
||||||
|
{
|
||||||
|
var state = NullabilityState.Unknown;
|
||||||
|
if (!parser.ParseNullableState(index, ref state))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
nullability.ReadState = state;
|
||||||
|
nullability.WriteState = state;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!isValueType || (Nullable.GetUnderlyingType(nullability.Type) ?? nullability.Type).IsGenericType)
|
||||||
|
{
|
||||||
|
index++;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (nullability.GenericTypeArguments.Length > 0)
|
||||||
|
{
|
||||||
|
foreach (NullabilityInfo genericTypeArgumentNullability in nullability.GenericTypeArguments)
|
||||||
|
{
|
||||||
|
TryPopulateNullabilityInfo(genericTypeArgumentNullability, parser, ref index);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (nullability.ElementType is { } elementTypeNullability)
|
||||||
|
{
|
||||||
|
TryPopulateNullabilityInfo(elementTypeNullability, parser, ref index);
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static NullabilityState TranslateByte(object? value)
|
||||||
|
{
|
||||||
|
return value is byte b ? TranslateByte(b) : NullabilityState.Unknown;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static NullabilityState TranslateByte(byte b) =>
|
||||||
|
b switch
|
||||||
|
{
|
||||||
|
1 => NullabilityState.NotNull,
|
||||||
|
2 => NullabilityState.Nullable,
|
||||||
|
_ => NullabilityState.Unknown
|
||||||
|
};
|
||||||
|
|
||||||
|
private static bool IsValueTypeOrValueTypeByRef(Type type) =>
|
||||||
|
type.IsValueType || ((type.IsByRef || type.IsPointer) && type.GetElementType()!.IsValueType);
|
||||||
|
|
||||||
|
private readonly struct NullableAttributeStateParser
|
||||||
|
{
|
||||||
|
private static readonly object UnknownByte = (byte)0;
|
||||||
|
|
||||||
|
private readonly object? _nullableAttributeArgument;
|
||||||
|
|
||||||
|
public NullableAttributeStateParser(object? nullableAttributeArgument)
|
||||||
|
{
|
||||||
|
this._nullableAttributeArgument = nullableAttributeArgument;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static NullableAttributeStateParser Unknown => new(UnknownByte);
|
||||||
|
|
||||||
|
public bool ParseNullableState(int index, ref NullabilityState state)
|
||||||
|
{
|
||||||
|
switch (this._nullableAttributeArgument)
|
||||||
|
{
|
||||||
|
case byte b:
|
||||||
|
state = TranslateByte(b);
|
||||||
|
return true;
|
||||||
|
case ReadOnlyCollection<CustomAttributeTypedArgument> args
|
||||||
|
when index < args.Count && args[index].Value is byte elementB:
|
||||||
|
state = TranslateByte(elementB);
|
||||||
|
return true;
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -56,8 +56,16 @@
|
|||||||
|
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
<Compile Remove="Data\NullableAttribute.cs" />
|
||||||
|
<Compile Remove="Data\NullableContextAttribute.cs" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<None Include="Data\NullableAttribute.cs" />
|
||||||
|
<None Include="Data\NullableContextAttribute.cs" />
|
||||||
<None Include="LICENSE" Pack="true" PackagePath=""></None>
|
<None Include="LICENSE" Pack="true" PackagePath=""></None>
|
||||||
<None Include="README.md" Pack="true" PackagePath=""/>
|
<None Include="README.md" Pack="true" PackagePath="" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
|
|
||||||
|
@ -72,7 +72,6 @@ public class ConstantTemplate : MemberTemplate
|
|||||||
public static ConstantTemplate MakeConstantTemplate(Type type, FieldInfo ci, byte index = 0, string customName = null, TypeTemplate typeTemplate = null)
|
public static ConstantTemplate MakeConstantTemplate(Type type, FieldInfo ci, byte index = 0, string customName = null, TypeTemplate typeTemplate = null)
|
||||||
{
|
{
|
||||||
var annotationAttr = ci.GetCustomAttribute<AnnotationAttribute>(true);
|
var annotationAttr = ci.GetCustomAttribute<AnnotationAttribute>(true);
|
||||||
var nullableAttr = ci.GetCustomAttribute<NullableAttribute>(true);
|
|
||||||
|
|
||||||
var valueType = RepresentationType.FromType(ci.FieldType);
|
var valueType = RepresentationType.FromType(ci.FieldType);
|
||||||
|
|
||||||
|
@ -82,29 +82,32 @@ public class EventTemplate : MemberTemplate
|
|||||||
|
|
||||||
var annotationAttr = ei.GetCustomAttribute<AnnotationAttribute>(true);
|
var annotationAttr = ei.GetCustomAttribute<AnnotationAttribute>(true);
|
||||||
var listenableAttr = ei.GetCustomAttribute<ListenableAttribute>(true);
|
var listenableAttr = ei.GetCustomAttribute<ListenableAttribute>(true);
|
||||||
var nullableAttr = ei.GetCustomAttribute<NullableAttribute>(true);
|
|
||||||
var nullableContextAttr = ei.GetCustomAttribute<NullableContextAttribute>(true);
|
|
||||||
|
|
||||||
var flags = nullableAttr?.Flags?.ToList() ?? new List<byte>();
|
evtType.Nullable = new NullabilityInfoContext().Create(ei).ReadState is NullabilityState.Nullable;
|
||||||
|
|
||||||
// skip the eventHandler class
|
//var nullableAttr = ei.GetCustomAttribute<NullableAttribute>(true);
|
||||||
if (flags.Count > 1)
|
//var nullableContextAttr = ei.GetCustomAttribute<NullableContextAttribute>(true);
|
||||||
flags = flags.Skip(1).ToList();
|
|
||||||
|
|
||||||
if (nullableContextAttr?.Flag == 2)
|
//var flags = nullableAttr?.Flags?.ToList() ?? new List<byte>();
|
||||||
{
|
|
||||||
if (flags.Count == 1)
|
//// skip the eventHandler class
|
||||||
evtType.SetNotNull(flags.FirstOrDefault());
|
//if (flags.Count > 1)
|
||||||
else
|
// flags = flags.Skip(1).ToList();
|
||||||
evtType.SetNotNull(flags);
|
|
||||||
}
|
//if (nullableContextAttr?.Flag == 2)
|
||||||
else
|
//{
|
||||||
{
|
// if (flags.Count == 1)
|
||||||
if (flags.Count == 1)
|
// evtType.SetNotNull(flags.FirstOrDefault());
|
||||||
evtType.SetNull(flags.FirstOrDefault());
|
// else
|
||||||
else
|
// evtType.SetNotNull(flags);
|
||||||
evtType.SetNull(flags);
|
//}
|
||||||
}
|
//else
|
||||||
|
//{
|
||||||
|
// if (flags.Count == 1)
|
||||||
|
// evtType.SetNull(flags.FirstOrDefault());
|
||||||
|
// else
|
||||||
|
// evtType.SetNull(flags);
|
||||||
|
//}
|
||||||
|
|
||||||
var et = new EventTemplate(typeTemplate, index, customName ?? ei.Name, ei.DeclaringType != type, evtType);
|
var et = new EventTemplate(typeTemplate, index, customName ?? ei.Name, ei.DeclaringType != type, evtType);
|
||||||
et.EventInfo = ei;
|
et.EventInfo = ei;
|
||||||
|
@ -90,36 +90,42 @@ public class FunctionTemplate : MemberTemplate
|
|||||||
throw new Exception($"Unsupported type `{mi.ReturnType}` in method `{type.Name}.{mi.Name}` return");
|
throw new Exception($"Unsupported type `{mi.ReturnType}` in method `{type.Name}.{mi.Name}` return");
|
||||||
|
|
||||||
var annotationAttr = mi.GetCustomAttribute<AnnotationAttribute>(true);
|
var annotationAttr = mi.GetCustomAttribute<AnnotationAttribute>(true);
|
||||||
var nullableAttr = mi.GetCustomAttribute<NullableAttribute>(true);
|
|
||||||
var nullableContextAttr = mi.GetCustomAttribute<NullableContextAttribute>(true);
|
|
||||||
|
|
||||||
var flags = nullableAttr?.Flags?.ToList() ?? new List<byte>();
|
var nullabilityInfoContext = new NullabilityInfoContext();
|
||||||
|
|
||||||
var rtNullableAttr = mi.ReturnTypeCustomAttributes.GetCustomAttributes(typeof(NullableAttribute), true).FirstOrDefault() as NullableAttribute;
|
rtType.Nullable = nullabilityInfoContext.Create(mi.ReturnParameter).WriteState is NullabilityState.Nullable;
|
||||||
var rtNullableContextAttr = mi.ReturnTypeCustomAttributes
|
|
||||||
.GetCustomAttributes(typeof(NullableContextAttribute), true)
|
|
||||||
.FirstOrDefault() as NullableContextAttribute
|
|
||||||
?? nullableContextAttr;
|
|
||||||
|
|
||||||
var rtFlags = rtNullableAttr?.Flags?.ToList() ?? new List<byte>();
|
|
||||||
|
|
||||||
if (rtFlags.Count > 0 && genericRtType == typeof(AsyncReply<>))
|
//var nullableAttr = mi.GetCustomAttribute<NullableAttribute>(true);
|
||||||
rtFlags.RemoveAt(0);
|
//var nullableContextAttr = mi.GetCustomAttribute<NullableContextAttribute>(true);
|
||||||
|
|
||||||
if (rtNullableContextAttr?.Flag == 2)
|
//var flags = nullableAttr?.Flags?.ToList() ?? new List<byte>();
|
||||||
{
|
|
||||||
if (rtFlags.Count == 1)
|
//var rtNullableAttr = mi.ReturnTypeCustomAttributes.GetCustomAttributes(typeof(NullableAttribute), true).FirstOrDefault() as NullableAttribute;
|
||||||
rtType.SetNotNull(rtFlags.FirstOrDefault());
|
//var rtNullableContextAttr = mi.ReturnTypeCustomAttributes
|
||||||
else
|
// .GetCustomAttributes(typeof(NullableContextAttribute), true)
|
||||||
rtType.SetNotNull(rtFlags);
|
// .FirstOrDefault() as NullableContextAttribute
|
||||||
}
|
// ?? nullableContextAttr;
|
||||||
else
|
|
||||||
{
|
//var rtFlags = rtNullableAttr?.Flags?.ToList() ?? new List<byte>();
|
||||||
if (rtFlags.Count == 1)
|
|
||||||
rtType.SetNull(rtFlags.FirstOrDefault());
|
//if (rtFlags.Count > 0 && genericRtType == typeof(AsyncReply<>))
|
||||||
else
|
// rtFlags.RemoveAt(0);
|
||||||
rtType.SetNull(rtFlags);
|
|
||||||
}
|
//if (rtNullableContextAttr?.Flag == 2)
|
||||||
|
//{
|
||||||
|
// if (rtFlags.Count == 1)
|
||||||
|
// rtType.SetNotNull(rtFlags.FirstOrDefault());
|
||||||
|
// else
|
||||||
|
// rtType.SetNotNull(rtFlags);
|
||||||
|
//}
|
||||||
|
//else
|
||||||
|
//{
|
||||||
|
// if (rtFlags.Count == 1)
|
||||||
|
// rtType.SetNull(rtFlags.FirstOrDefault());
|
||||||
|
// else
|
||||||
|
// rtType.SetNull(rtFlags);
|
||||||
|
//}
|
||||||
|
|
||||||
var args = mi.GetParameters();
|
var args = mi.GetParameters();
|
||||||
|
|
||||||
@ -136,27 +142,28 @@ public class FunctionTemplate : MemberTemplate
|
|||||||
if (argType == null)
|
if (argType == null)
|
||||||
throw new Exception($"Unsupported type `{x.ParameterType}` in method `{type.Name}.{mi.Name}` parameter `{x.Name}`");
|
throw new Exception($"Unsupported type `{x.ParameterType}` in method `{type.Name}.{mi.Name}` parameter `{x.Name}`");
|
||||||
|
|
||||||
|
argType.Nullable = nullabilityInfoContext.Create(x).WriteState is NullabilityState.Nullable;
|
||||||
|
|
||||||
var argNullableAttr = x.GetCustomAttribute<NullableAttribute>(true);
|
//var argNullableAttr = x.GetCustomAttribute<NullableAttribute>(true);
|
||||||
var argNullableContextAttr = x.GetCustomAttribute<NullableContextAttribute>(true) ?? nullableContextAttr;
|
//var argNullableContextAttr = x.GetCustomAttribute<NullableContextAttribute>(true) ?? nullableContextAttr;
|
||||||
|
|
||||||
var argFlags = argNullableAttr?.Flags?.ToList() ?? new List<byte>();
|
//var argFlags = argNullableAttr?.Flags?.ToList() ?? new List<byte>();
|
||||||
|
|
||||||
|
|
||||||
if (argNullableContextAttr?.Flag == 2)
|
//if (argNullableContextAttr?.Flag == 2)
|
||||||
{
|
//{
|
||||||
if (argFlags.Count == 1)
|
// if (argFlags.Count == 1)
|
||||||
argType.SetNotNull(argFlags.FirstOrDefault());
|
// argType.SetNotNull(argFlags.FirstOrDefault());
|
||||||
else
|
// else
|
||||||
argType.SetNotNull(argFlags);
|
// argType.SetNotNull(argFlags);
|
||||||
}
|
//}
|
||||||
else
|
//else
|
||||||
{
|
//{
|
||||||
if (rtFlags.Count == 1)
|
// if (rtFlags.Count == 1)
|
||||||
argType.SetNull(argFlags.FirstOrDefault());
|
// argType.SetNull(argFlags.FirstOrDefault());
|
||||||
else
|
// else
|
||||||
argType.SetNull(argFlags);
|
// argType.SetNull(argFlags);
|
||||||
}
|
//}
|
||||||
|
|
||||||
return new ArgumentTemplate()
|
return new ArgumentTemplate()
|
||||||
{
|
{
|
||||||
|
@ -139,6 +139,9 @@ public class PropertyTemplate : MemberTemplate
|
|||||||
RepresentationType valueType, string readAnnotation = null, string writeAnnotation = null, bool recordable = false)
|
RepresentationType valueType, string readAnnotation = null, string writeAnnotation = null, bool recordable = false)
|
||||||
: base(template, index, name, inherited)
|
: base(template, index, name, inherited)
|
||||||
{
|
{
|
||||||
|
if (name == "RecordNullable")
|
||||||
|
Console.Beep();
|
||||||
|
|
||||||
this.Recordable = recordable;
|
this.Recordable = recordable;
|
||||||
//this.Storage = storage;
|
//this.Storage = storage;
|
||||||
if (readAnnotation != null)
|
if (readAnnotation != null)
|
||||||
@ -163,28 +166,32 @@ public class PropertyTemplate : MemberTemplate
|
|||||||
var annotationAttr = pi.GetCustomAttribute<AnnotationAttribute>(true);
|
var annotationAttr = pi.GetCustomAttribute<AnnotationAttribute>(true);
|
||||||
var storageAttr = pi.GetCustomAttribute<StorageAttribute>(true);
|
var storageAttr = pi.GetCustomAttribute<StorageAttribute>(true);
|
||||||
|
|
||||||
var nullableContextAttr = pi.GetCustomAttribute<NullableContextAttribute>(true);
|
var nullabilityContext = new NullabilityInfoContext();
|
||||||
var nullableAttr = pi.GetCustomAttribute<NullableAttribute>(true);
|
propType.Nullable = nullabilityContext.Create(pi).ReadState is NullabilityState.Nullable;
|
||||||
|
|
||||||
var flags = nullableAttr?.Flags?.ToList() ?? new List<byte>();
|
|
||||||
|
|
||||||
if (flags.Count > 0 && genericPropType == typeof(DistributedPropertyContext<>))
|
// var nullableContextAttr = pi.GetCustomAttribute<NullableContextAttribute>(true);
|
||||||
flags.RemoveAt(0);
|
// var nullableAttr = pi.GetCustomAttribute<NullableAttribute>(true);
|
||||||
|
|
||||||
if (nullableContextAttr?.Flag == 2)
|
// var flags = nullableAttr?.Flags?.ToList() ?? new List<byte>();
|
||||||
{
|
|
||||||
if (flags.Count == 1)
|
// if (flags.Count > 0 && genericPropType == typeof(DistributedPropertyContext<>))
|
||||||
propType.SetNotNull(flags.FirstOrDefault());
|
// flags.RemoveAt(0);
|
||||||
else
|
|
||||||
propType.SetNotNull(flags);
|
// if (nullableContextAttr?.Flag == 2)
|
||||||
}
|
// {
|
||||||
else
|
// if (flags.Count == 1)
|
||||||
{
|
// propType.SetNotNull(flags.FirstOrDefault());
|
||||||
if (flags.Count == 1)
|
// else
|
||||||
propType.SetNull(flags.FirstOrDefault());
|
// propType.SetNotNull(flags);
|
||||||
else
|
// }
|
||||||
propType.SetNull(flags);
|
// else
|
||||||
}
|
// {
|
||||||
|
// if (flags.Count == 1)
|
||||||
|
// propType.SetNull(flags.FirstOrDefault());
|
||||||
|
// else
|
||||||
|
// propType.SetNull(flags);
|
||||||
|
// }
|
||||||
|
|
||||||
var pt = new PropertyTemplate(typeTemplate, index, customName ?? pi.Name, pi.DeclaringType != type, propType);
|
var pt = new PropertyTemplate(typeTemplate, index, customName ?? pi.Name, pi.DeclaringType != type, propType);
|
||||||
|
|
||||||
|
@ -376,7 +376,6 @@ public class TypeTemplate
|
|||||||
public static ConstantTemplate MakeConstantTemplate(Type type, FieldInfo ci, ExportAttribute exportAttr, byte index = 0, TypeTemplate typeTemplate = null)
|
public static ConstantTemplate MakeConstantTemplate(Type type, FieldInfo ci, ExportAttribute exportAttr, byte index = 0, TypeTemplate typeTemplate = null)
|
||||||
{
|
{
|
||||||
var annotationAttr = ci.GetCustomAttribute<AnnotationAttribute>(true);
|
var annotationAttr = ci.GetCustomAttribute<AnnotationAttribute>(true);
|
||||||
var nullableAttr = ci.GetCustomAttribute<NullableAttribute>(true);
|
|
||||||
|
|
||||||
var valueType = RepresentationType.FromType(ci.FieldType);
|
var valueType = RepresentationType.FromType(ci.FieldType);
|
||||||
|
|
||||||
|
@ -120,6 +120,8 @@ public partial class MyService
|
|||||||
|
|
||||||
[Export] public MyRecord Record => new MyRecord() { Id = 33, Name = "Test", Score = 99.33 };
|
[Export] public MyRecord Record => new MyRecord() { Id = 33, Name = "Test", Score = 99.33 };
|
||||||
|
|
||||||
|
[Export] public MyRecord? RecordNullable => new MyRecord() { Id = 33, Name = "Test Nullable", Score = 99.33 };
|
||||||
|
|
||||||
[Export] public List<int> IntList => new List<int>() { 1, 2, 3, 4, 5 };
|
[Export] public List<int> IntList => new List<int>() { 1, 2, 3, 4, 5 };
|
||||||
|
|
||||||
[Export] public IRecord[] RecordsArray => new IRecord[] { new MyRecord() { Id = 22, Name = "Test", Score = 22.1 } };
|
[Export] public IRecord[] RecordsArray => new IRecord[] { new MyRecord() { Id = 22, Name = "Test", Score = 22.1 } };
|
||||||
|
Loading…
x
Reference in New Issue
Block a user