From 3e5d8a330994d8ae5d6db7c2bd1058d3c5b22103 Mon Sep 17 00:00:00 2001 From: Jason Ginchereau Date: Fri, 6 Oct 2023 17:17:45 -1000 Subject: [PATCH] Fix nullability context for typedefs generator --- src/NodeApi.Generator/Program.cs | 1 + .../TypeDefinitionsGenerator.cs | 37 +++++++++++++++++-- 2 files changed, 35 insertions(+), 3 deletions(-) diff --git a/src/NodeApi.Generator/Program.cs b/src/NodeApi.Generator/Program.cs index f10fb55d..e892942b 100644 --- a/src/NodeApi.Generator/Program.cs +++ b/src/NodeApi.Generator/Program.cs @@ -224,6 +224,7 @@ private static void ResolveSystemAssemblies(string? targetFramework) } string dotnetRootDirectory = Path.GetDirectoryName(Path.GetDirectoryName( Path.GetDirectoryName(runtimeDirectory)!)!)!; + string refAssemblyDirectory = Path.Combine( dotnetRootDirectory, "packs", diff --git a/src/NodeApi.Generator/TypeDefinitionsGenerator.cs b/src/NodeApi.Generator/TypeDefinitionsGenerator.cs index 90aec5fb..d9566f05 100644 --- a/src/NodeApi.Generator/TypeDefinitionsGenerator.cs +++ b/src/NodeApi.Generator/TypeDefinitionsGenerator.cs @@ -933,7 +933,9 @@ private string GetTSType(PropertyInfo property) propertyType = propertyType.GetElementType()!; } - string tsType = GetTSType(propertyType, _nullabilityContext.Create(property)); + string tsType = GetTSType( + propertyType, + FixNullability(_nullabilityContext.Create(property))); if (tsType == "unknown" || tsType.Contains("unknown")) { @@ -1001,7 +1003,8 @@ private string GetTSType(ParameterInfo parameter) } tsType = GetTSType( - parameter.ParameterType, _nullabilityContext.Create(parameter)); + parameter.ParameterType, + FixNullability(_nullabilityContext.Create(parameter))); return $"{{ {resultName}: {tsType}, {outProperties} }}"; } } @@ -1013,7 +1016,7 @@ private string GetTSType(ParameterInfo parameter) parameterType = parameterType.GetElementType()!; } - tsType = GetTSType(parameterType, _nullabilityContext.Create(parameter)); + tsType = GetTSType(parameterType, FixNullability(_nullabilityContext.Create(parameter))); if (tsType == "unknown" || tsType.Contains("unknown")) { string className = parameter.Member.DeclaringType!.Name; @@ -1043,6 +1046,34 @@ private string GetTSType(ParameterInfo parameter) return tsType; } + /// + /// The generator loads all referenced types in a separate MetadataLoadContext, + /// which causes a problem with NullabilityInfoContext when it tries to detect + /// whether a type is a value type, because the referenced ValueType type is not + /// the same as the system ValueType type. This method overrides the nullability + /// state for value types, which can never be nullable. (Note Nullable is itself + /// a non-nullable value type; it is handled by the generator as a special case.) + /// + private static NullabilityInfo FixNullability(NullabilityInfo nullability) + { + if (nullability.Type.BaseType?.FullName == typeof(ValueType).FullName) + { + // Use reflection to override these properties which have internal setters. + // There is no public constructor and no other way to set these properties. + typeof(NullabilityInfo).GetProperty(nameof(NullabilityInfo.ReadState))! + .SetValue(nullability, NullabilityState.NotNull); + typeof(NullabilityInfo).GetProperty(nameof(NullabilityInfo.WriteState))! + .SetValue(nullability, NullabilityState.NotNull); + } + + for (int i = 0; i < nullability.GenericTypeArguments.Length; i++) + { + FixNullability(nullability.GenericTypeArguments[i]); + } + + return nullability; + } + private string GetTSType(Type type, NullabilityInfo? nullability) { string tsType = "unknown";