diff --git a/NuGet.config b/NuGet.config
index 963019c74b9b4..f34253e5b0e2f 100644
--- a/NuGet.config
+++ b/NuGet.config
@@ -10,7 +10,11 @@
+
+
+
+
+
+
+
diff --git a/eng/Version.Details.xml b/eng/Version.Details.xml
index 9cfaf1bccf913..c8a893de7d463 100644
--- a/eng/Version.Details.xml
+++ b/eng/Version.Details.xml
@@ -99,14 +99,14 @@
201f4dae9d1a1e105d8ba86d7ece61eed1f665e0
-
+
https://github.com/dotnet/source-build-reference-packages
- fa4c0e8f53ef2541a23e519af4dfb86cb88e1bae
+ 95f83e27806330fec09edd96e06bba3acabe3f35
-
+
https://github.com/dotnet/source-build-externals
- 3dc05150cf234f76f6936dcb2853d31a0da1f60e
+ e844aa02a05b90d8cbe499676ec6ee0f19ec4980
@@ -354,9 +354,9 @@
https://dev.azure.com/dnceng/internal/_git/dotnet-optimization
67613417f5e1af250e6ddfba79f8f2885d8e90fb
-
+
https://github.com/dotnet/hotreload-utils
- 12d02e5c2310be6460f896bc3aeb0ddf1e8926bc
+ 5524f726f92ef862b415793758cebbd2a1950b70
https://github.com/dotnet/runtime-assets
diff --git a/eng/Versions.props b/eng/Versions.props
index 799ab19e9a8af..da037c2c7ca92 100644
--- a/eng/Versions.props
+++ b/eng/Versions.props
@@ -186,7 +186,7 @@
8.0.0-prerelease.23407.2
8.0.0-prerelease.23407.2
8.0.0-prerelease.23407.2
- 8.0.0-alpha.0.23563.1
+ 8.0.0-alpha.0.23570.2
2.4.2
1.0.0
2.4.5
diff --git a/eng/packaging.targets b/eng/packaging.targets
index a1fbe07ed2a57..67405cb51aeff 100644
--- a/eng/packaging.targets
+++ b/eng/packaging.targets
@@ -21,13 +21,20 @@
true
$(MSBuildThisFileDirectory)useSharedDesignerContext.txt
PACKAGE.md
+
+ false
true
- true
+
// Information stored in the DAC table of interest to the DAC implementation
// Note that this information is shared between all instantiations of ClrDataAccess, so initialize
// it just once in code:ClrDataAccess.GetDacGlobals (rather than use fields in ClrDataAccess);
@@ -1493,10 +1495,10 @@ class __Str16Ptr : public __DPtr
}
void EnumMem(void) const
{
- char* str = DacInstantiateStringW(m_addr, maxChars, false);
+ WCHAR* str = DacInstantiateStringW(m_addr, maxChars, false);
if (str)
{
- DacEnumMemoryRegion(m_addr, strlen(str) + 1);
+ DacEnumMemoryRegion(m_addr, u16_strlen(str) + 1);
}
}
};
diff --git a/src/coreclr/inc/dacvars.h b/src/coreclr/inc/dacvars.h
index 8fcc1cced7d43..030c18f0b9db2 100644
--- a/src/coreclr/inc/dacvars.h
+++ b/src/coreclr/inc/dacvars.h
@@ -231,5 +231,7 @@ DEFINE_DACVAR(SIZE_T, dac__g_clrNotificationArguments, ::g_clrNotificationArgume
DEFINE_DACVAR(bool, dac__g_metadataUpdatesApplied, ::g_metadataUpdatesApplied)
#endif
+DEFINE_DACVAR(PTR_WSTR, dac__g_EntryAssemblyPath, ::g_EntryAssemblyPath)
+
#undef DEFINE_DACVAR
#undef DEFINE_DACVAR_NO_DUMP
diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/ObjectWriter.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/ObjectWriter.cs
index bef41e3d7ba35..d85252cca6168 100644
--- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/ObjectWriter.cs
+++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/ObjectWriter.cs
@@ -439,15 +439,11 @@ public void EmitDebugEHClauseInfo(ObjectNode node)
private static extern void EmitDebugFunctionInfo(IntPtr objWriter, byte[] methodName, int methodSize, uint methodTypeIndex);
public void EmitDebugFunctionInfo(ObjectNode node, int methodSize)
{
- uint methodTypeIndex = 0;
-
- var methodNode = node as IMethodNode;
- if (methodNode != null)
+ if (node is IMethodNode methodNode)
{
- methodTypeIndex = _userDefinedTypeDescriptor.GetMethodFunctionIdTypeIndex(methodNode.Method);
+ uint methodTypeIndex = _userDefinedTypeDescriptor.GetMethodFunctionIdTypeIndex(methodNode.Method);
+ EmitDebugFunctionInfo(_nativeObjectWriter, _currentNodeZeroTerminatedName.UnderlyingArray, methodSize, methodTypeIndex);
}
-
- EmitDebugFunctionInfo(_nativeObjectWriter, _currentNodeZeroTerminatedName.UnderlyingArray, methodSize, methodTypeIndex);
}
[DllImport(NativeObjectWriterFileName)]
diff --git a/src/coreclr/vm/corhost.cpp b/src/coreclr/vm/corhost.cpp
index bb32e93cd0961..b85331e00ce8d 100644
--- a/src/coreclr/vm/corhost.cpp
+++ b/src/coreclr/vm/corhost.cpp
@@ -310,6 +310,15 @@ HRESULT CorHost2::ExecuteAssembly(DWORD dwAppDomainId,
_ASSERTE (!pThread->PreemptiveGCDisabled());
+ if (g_EntryAssemblyPath == NULL)
+ {
+ // Store the entry assembly path for diagnostic purposes (for example, dumps)
+ size_t len = u16_strlen(pwzAssemblyPath) + 1;
+ NewArrayHolder path { new WCHAR[len] };
+ wcscpy_s(path, len, pwzAssemblyPath);
+ g_EntryAssemblyPath = path.Extract();
+ }
+
Assembly *pAssembly = AssemblySpec::LoadAssembly(pwzAssemblyPath);
#if defined(FEATURE_MULTICOREJIT)
diff --git a/src/coreclr/vm/vars.cpp b/src/coreclr/vm/vars.cpp
index 85737057d2f9c..00840f9195651 100644
--- a/src/coreclr/vm/vars.cpp
+++ b/src/coreclr/vm/vars.cpp
@@ -106,6 +106,8 @@ GVAL_IMPL_INIT(DWORD, g_debuggerWordTLSIndex, TLS_OUT_OF_INDEXES);
#endif
GVAL_IMPL_INIT(DWORD, g_TlsIndex, TLS_OUT_OF_INDEXES);
+GVAL_IMPL_INIT(PTR_WSTR, g_EntryAssemblyPath, NULL);
+
#ifndef DACCESS_COMPILE
// @TODO - PROMOTE.
diff --git a/src/coreclr/vm/vars.hpp b/src/coreclr/vm/vars.hpp
index dd92ee7b12fde..03762a24e695d 100644
--- a/src/coreclr/vm/vars.hpp
+++ b/src/coreclr/vm/vars.hpp
@@ -389,6 +389,9 @@ GVAL_DECL(DWORD, g_debuggerWordTLSIndex);
#endif
GVAL_DECL(DWORD, g_TlsIndex);
+// Full path to the managed entry assembly - stored for ease of identifying the entry asssembly for diagnostics
+GVAL_DECL(PTR_WSTR, g_EntryAssemblyPath);
+
// Global System Information
extern SYSTEM_INFO g_SystemInfo;
diff --git a/src/coreclr/vm/weakreferencenative.cpp b/src/coreclr/vm/weakreferencenative.cpp
index f9a7b9a6bfc01..bb0ac10906ef0 100644
--- a/src/coreclr/vm/weakreferencenative.cpp
+++ b/src/coreclr/vm/weakreferencenative.cpp
@@ -172,8 +172,7 @@ FCIMPL1(FC_BOOL_RET, ComAwareWeakReferenceNative::HasInteropInfo, Object* pObjec
_ASSERTE(pObject != nullptr);
SyncBlock* pSyncBlock = pObject->PassiveGetSyncBlock();
- _ASSERTE(pSyncBlock != nullptr);
- return pSyncBlock->GetInteropInfoNoCreate() != nullptr;
+ return pSyncBlock != nullptr && pSyncBlock->GetInteropInfoNoCreate() != nullptr;
}
FCIMPLEND
diff --git a/src/libraries/Directory.Build.targets b/src/libraries/Directory.Build.targets
index cb644757a2d95..348a71ba8da89 100644
--- a/src/libraries/Directory.Build.targets
+++ b/src/libraries/Directory.Build.targets
@@ -7,6 +7,18 @@
$(TestStrongNameKeyId)
+
+
+
+ true
+ true
+ true
+
+
@@ -47,14 +59,6 @@
-->
$(WarningsAsErrors.Replace('NU1605', ''))
-
- true
- true
- true
false
diff --git a/src/libraries/Microsoft.Bcl.TimeProvider/ref/Microsoft.Bcl.TimeProvider.csproj b/src/libraries/Microsoft.Bcl.TimeProvider/ref/Microsoft.Bcl.TimeProvider.csproj
index 11d55537a3a3b..45eebd41ee4e3 100644
--- a/src/libraries/Microsoft.Bcl.TimeProvider/ref/Microsoft.Bcl.TimeProvider.csproj
+++ b/src/libraries/Microsoft.Bcl.TimeProvider/ref/Microsoft.Bcl.TimeProvider.csproj
@@ -13,6 +13,6 @@
-
+
diff --git a/src/libraries/Microsoft.Bcl.TimeProvider/src/Microsoft.Bcl.TimeProvider.csproj b/src/libraries/Microsoft.Bcl.TimeProvider/src/Microsoft.Bcl.TimeProvider.csproj
index 95e508e6f0d2b..fbc46e4552c67 100644
--- a/src/libraries/Microsoft.Bcl.TimeProvider/src/Microsoft.Bcl.TimeProvider.csproj
+++ b/src/libraries/Microsoft.Bcl.TimeProvider/src/Microsoft.Bcl.TimeProvider.csproj
@@ -14,6 +14,8 @@ System.TimeProvider
System.ITimer
false
+ true
+ 1
@@ -31,7 +33,7 @@ System.ITimer
-
+
diff --git a/src/libraries/Microsoft.Extensions.Options/gen/Emitter.cs b/src/libraries/Microsoft.Extensions.Options/gen/Emitter.cs
index 41609ad4b2010..1edc429378528 100644
--- a/src/libraries/Microsoft.Extensions.Options/gen/Emitter.cs
+++ b/src/libraries/Microsoft.Extensions.Options/gen/Emitter.cs
@@ -374,12 +374,83 @@ public void EmitCompareAttribute(string modifier, string prefix, string classNam
""");
}
- public void EmitRangeAttribute(string modifier, string prefix, string className, string suffix)
+ public void EmitRangeAttribute(string modifier, string prefix, string className, string suffix, bool emitTimeSpanSupport)
{
OutGeneratedCodeAttribute();
string qualifiedClassName = $"{prefix}{suffix}_{className}";
+ string initializationString = emitTimeSpanSupport ?
+ """
+ if (OperandType == typeof(global::System.TimeSpan))
+ {
+ if (!global::System.TimeSpan.TryParse((string)Minimum, culture, out global::System.TimeSpan timeSpanMinimum) ||
+ !global::System.TimeSpan.TryParse((string)Maximum, culture, out global::System.TimeSpan timeSpanMaximum))
+ {
+ throw new global::System.InvalidOperationException(c_minMaxError);
+ }
+ Minimum = timeSpanMinimum;
+ Maximum = timeSpanMaximum;
+ }
+ else
+ {
+ Minimum = ConvertValue(Minimum, culture) ?? throw new global::System.InvalidOperationException(c_minMaxError);
+ Maximum = ConvertValue(Maximum, culture) ?? throw new global::System.InvalidOperationException(c_minMaxError);
+ }
+ """
+ :
+ """
+ Minimum = ConvertValue(Minimum, culture) ?? throw new global::System.InvalidOperationException(c_minMaxError);
+ Maximum = ConvertValue(Maximum, culture) ?? throw new global::System.InvalidOperationException(c_minMaxError);
+ """;
+
+ string convertValue = emitTimeSpanSupport ?
+ """
+ if (OperandType == typeof(global::System.TimeSpan))
+ {
+ if (value is global::System.TimeSpan)
+ {
+ convertedValue = value;
+ }
+ else if (value is string)
+ {
+ if (!global::System.TimeSpan.TryParse((string)value, formatProvider, out global::System.TimeSpan timeSpanValue))
+ {
+ return false;
+ }
+ convertedValue = timeSpanValue;
+ }
+ else
+ {
+ throw new global::System.InvalidOperationException($"A value type {value.GetType()} that is not a TimeSpan or a string has been given. This might indicate a problem with the source generator.");
+ }
+ }
+ else
+ {
+ try
+ {
+ convertedValue = ConvertValue(value, formatProvider);
+ }
+ catch (global::System.Exception e) when (e is global::System.FormatException or global::System.InvalidCastException or global::System.NotSupportedException)
+ {
+ return false;
+ }
+ }
+ """
+ :
+ """
+ try
+ {
+ convertedValue = ConvertValue(value, formatProvider);
+ }
+ catch (global::System.Exception e) when (e is global::System.FormatException or global::System.InvalidCastException or global::System.NotSupportedException)
+ {
+ return false;
+ }
+ """;
+
+
+
OutLn($$"""
[global::System.AttributeUsage(global::System.AttributeTargets.Property | global::System.AttributeTargets.Field | global::System.AttributeTargets.Parameter, AllowMultiple = false)]
{{modifier}} class {{qualifiedClassName}} : {{StaticValidationAttributeType}}
@@ -414,19 +485,20 @@ public override string FormatErrorMessage(string name) =>
string.Format(global::System.Globalization.CultureInfo.CurrentCulture, GetValidationErrorMessage(), name, Minimum, Maximum);
private bool NeedToConvertMinMax { get; }
private bool Initialized { get; set; }
+ private const string c_minMaxError = "The minimum and maximum values must be set to valid values.";
+
public override bool IsValid(object? value)
{
if (!Initialized)
{
if (Minimum is null || Maximum is null)
{
- throw new global::System.InvalidOperationException("The minimum and maximum values must be set to valid values.");
+ throw new global::System.InvalidOperationException(c_minMaxError);
}
if (NeedToConvertMinMax)
{
System.Globalization.CultureInfo culture = ParseLimitsInInvariantCulture ? global::System.Globalization.CultureInfo.InvariantCulture : global::System.Globalization.CultureInfo.CurrentCulture;
- Minimum = ConvertValue(Minimum, culture) ?? throw new global::System.InvalidOperationException("The minimum and maximum values must be set to valid values.");
- Maximum = ConvertValue(Maximum, culture) ?? throw new global::System.InvalidOperationException("The minimum and maximum values must be set to valid values.");
+{{initializationString}}
}
int cmp = ((global::System.IComparable)Minimum).CompareTo((global::System.IComparable)Maximum);
if (cmp > 0)
@@ -448,14 +520,7 @@ public override bool IsValid(object? value)
System.Globalization.CultureInfo formatProvider = ConvertValueInInvariantCulture ? global::System.Globalization.CultureInfo.InvariantCulture : global::System.Globalization.CultureInfo.CurrentCulture;
object? convertedValue;
- try
- {
- convertedValue = ConvertValue(value, formatProvider);
- }
- catch (global::System.Exception e) when (e is global::System.FormatException or global::System.InvalidCastException or global::System.NotSupportedException)
- {
- return false;
- }
+{{convertValue}}
var min = (global::System.IComparable)Minimum;
var max = (global::System.IComparable)Maximum;
@@ -574,7 +639,7 @@ private void GenValidationAttributesClasses()
}
else if (attributeData.Key == _symbolHolder.RangeAttributeSymbol.Name)
{
- EmitRangeAttribute(_optionsSourceGenContext.ClassModifier, Emitter.StaticAttributeClassNamePrefix, attributeData.Key, _optionsSourceGenContext.Suffix);
+ EmitRangeAttribute(_optionsSourceGenContext.ClassModifier, Emitter.StaticAttributeClassNamePrefix, attributeData.Key, _optionsSourceGenContext.Suffix, attributeData.Value is not null);
}
}
diff --git a/src/libraries/Microsoft.Extensions.Options/gen/Parser.cs b/src/libraries/Microsoft.Extensions.Options/gen/Parser.cs
index ab2c19de81923..8a88ec6f2f4e6 100644
--- a/src/libraries/Microsoft.Extensions.Options/gen/Parser.cs
+++ b/src/libraries/Microsoft.Extensions.Options/gen/Parser.cs
@@ -624,10 +624,21 @@ private void TrackCompareAttributeForSubstitution(AttributeData attribute, IType
private void TrackRangeAttributeForSubstitution(AttributeData attribute, ITypeSymbol memberType, ref string attributeFullQualifiedName)
{
ImmutableArray constructorParameters = attribute.AttributeConstructor?.Parameters ?? ImmutableArray.Empty;
- SpecialType argumentSpecialType = SpecialType.None;
+ ITypeSymbol? argumentType = null;
+ bool hasTimeSpanType = false;
+
+ ITypeSymbol typeSymbol = memberType;
+ if (typeSymbol.OriginalDefinition.SpecialType == SpecialType.System_Nullable_T)
+ {
+ typeSymbol = ((INamedTypeSymbol)typeSymbol).TypeArguments[0];
+ }
+
if (constructorParameters.Length == 2)
{
- argumentSpecialType = constructorParameters[0].Type.SpecialType;
+ if (OptionsSourceGenContext.IsConvertibleBasicType(typeSymbol))
+ {
+ argumentType = constructorParameters[0].Type;
+ }
}
else if (constructorParameters.Length == 3)
{
@@ -641,23 +652,25 @@ private void TrackRangeAttributeForSubstitution(AttributeData attribute, ITypeSy
}
}
- if (argumentValue is INamedTypeSymbol namedTypeSymbol && OptionsSourceGenContext.IsConvertibleBasicType(namedTypeSymbol))
+ if (argumentValue is INamedTypeSymbol namedTypeSymbol)
{
- argumentSpecialType = namedTypeSymbol.SpecialType;
+ // When type is provided as a parameter, it has to match the property type.
+ if (OptionsSourceGenContext.IsConvertibleBasicType(namedTypeSymbol) && typeSymbol.SpecialType == namedTypeSymbol.SpecialType)
+ {
+ argumentType = namedTypeSymbol;
+ }
+ else if (SymbolEqualityComparer.Default.Equals(namedTypeSymbol, _symbolHolder.TimeSpanSymbol) &&
+ (SymbolEqualityComparer.Default.Equals(typeSymbol, _symbolHolder.TimeSpanSymbol) || typeSymbol.SpecialType == SpecialType.System_String))
+ {
+ hasTimeSpanType = true;
+ argumentType = _symbolHolder.TimeSpanSymbol;
+ }
}
}
- ITypeSymbol typeSymbol = memberType;
- if (typeSymbol.OriginalDefinition.SpecialType == SpecialType.System_Nullable_T)
- {
- typeSymbol = ((INamedTypeSymbol)typeSymbol).TypeArguments[0];
- }
-
- if (argumentSpecialType != SpecialType.None &&
- OptionsSourceGenContext.IsConvertibleBasicType(typeSymbol) &&
- (constructorParameters.Length != 3 || typeSymbol.SpecialType == argumentSpecialType)) // When type is provided as a parameter, it has to match the property type.
+ if (argumentType is not null)
{
- _optionsSourceGenContext.EnsureTrackingAttribute(attribute.AttributeClass!.Name, createValue: false, out _);
+ _optionsSourceGenContext.EnsureTrackingAttribute(attribute.AttributeClass!.Name, createValue: hasTimeSpanType, out _);
attributeFullQualifiedName = $"{Emitter.StaticGeneratedValidationAttributesClassesNamespace}.{Emitter.StaticAttributeClassNamePrefix}{_optionsSourceGenContext.Suffix}_{attribute.AttributeClass!.Name}";
}
}
diff --git a/src/libraries/Microsoft.Extensions.Options/gen/SymbolHolder.cs b/src/libraries/Microsoft.Extensions.Options/gen/SymbolHolder.cs
index 3447a07d39830..8e7073f8ec218 100644
--- a/src/libraries/Microsoft.Extensions.Options/gen/SymbolHolder.cs
+++ b/src/libraries/Microsoft.Extensions.Options/gen/SymbolHolder.cs
@@ -23,6 +23,7 @@ internal sealed record class SymbolHolder(
INamedTypeSymbol IValidatableObjectSymbol,
INamedTypeSymbol GenericIEnumerableSymbol,
INamedTypeSymbol TypeSymbol,
+ INamedTypeSymbol TimeSpanSymbol,
INamedTypeSymbol ValidateObjectMembersAttributeSymbol,
INamedTypeSymbol ValidateEnumeratedItemsAttributeSymbol);
}
diff --git a/src/libraries/Microsoft.Extensions.Options/gen/SymbolLoader.cs b/src/libraries/Microsoft.Extensions.Options/gen/SymbolLoader.cs
index ea55622892975..5be4932d8d6c4 100644
--- a/src/libraries/Microsoft.Extensions.Options/gen/SymbolLoader.cs
+++ b/src/libraries/Microsoft.Extensions.Options/gen/SymbolLoader.cs
@@ -19,6 +19,7 @@ internal static class SymbolLoader
internal const string IValidatableObjectType = "System.ComponentModel.DataAnnotations.IValidatableObject";
internal const string IValidateOptionsType = "Microsoft.Extensions.Options.IValidateOptions`1";
internal const string TypeOfType = "System.Type";
+ internal const string TimeSpanType = "System.TimeSpan";
internal const string ValidateObjectMembersAttribute = "Microsoft.Extensions.Options.ValidateObjectMembersAttribute";
internal const string ValidateEnumeratedItemsAttribute = "Microsoft.Extensions.Options.ValidateEnumeratedItemsAttribute";
internal const string GenericIEnumerableType = "System.Collections.Generic.IEnumerable`1";
@@ -42,6 +43,7 @@ public static bool TryLoad(Compilation compilation, out SymbolHolder? symbolHold
var validateOptionsSymbol = GetSymbol(IValidateOptionsType);
var genericIEnumerableSymbol = GetSymbol(GenericIEnumerableType);
var typeSymbol = GetSymbol(TypeOfType);
+ var timeSpanSymbol = GetSymbol(TimeSpanType);
var validateObjectMembersAttribute = GetSymbol(ValidateObjectMembersAttribute);
var validateEnumeratedItemsAttribute = GetSymbol(ValidateEnumeratedItemsAttribute);
var unconditionalSuppressMessageAttributeSymbol = GetSymbol(UnconditionalSuppressMessageAttributeType);
@@ -70,6 +72,7 @@ public static bool TryLoad(Compilation compilation, out SymbolHolder? symbolHold
validateOptionsSymbol == null ||
genericIEnumerableSymbol == null ||
typeSymbol == null ||
+ timeSpanSymbol == null ||
validateObjectMembersAttribute == null ||
validateEnumeratedItemsAttribute == null)
{
@@ -93,6 +96,7 @@ public static bool TryLoad(Compilation compilation, out SymbolHolder? symbolHold
ivalidatableObjectSymbol,
genericIEnumerableSymbol,
typeSymbol,
+ timeSpanSymbol,
validateObjectMembersAttribute,
validateEnumeratedItemsAttribute);
diff --git a/src/libraries/Microsoft.Extensions.Options/src/Microsoft.Extensions.Options.csproj b/src/libraries/Microsoft.Extensions.Options/src/Microsoft.Extensions.Options.csproj
index abbc5d2329c5c..e35606898e391 100644
--- a/src/libraries/Microsoft.Extensions.Options/src/Microsoft.Extensions.Options.csproj
+++ b/src/libraries/Microsoft.Extensions.Options/src/Microsoft.Extensions.Options.csproj
@@ -5,7 +5,7 @@
true
true
true
- 1
+ 2
Provides a strongly typed way of specifying and accessing settings using dependency injection.
diff --git a/src/libraries/Microsoft.Extensions.Options/tests/SourceGeneration.Unit.Tests/Baselines/EmitterWithCustomValidator.netcore.g.cs b/src/libraries/Microsoft.Extensions.Options/tests/SourceGeneration.Unit.Tests/Baselines/EmitterWithCustomValidator.netcore.g.cs
index 2c5af12c5b5f2..38bacf966df05 100644
--- a/src/libraries/Microsoft.Extensions.Options/tests/SourceGeneration.Unit.Tests/Baselines/EmitterWithCustomValidator.netcore.g.cs
+++ b/src/libraries/Microsoft.Extensions.Options/tests/SourceGeneration.Unit.Tests/Baselines/EmitterWithCustomValidator.netcore.g.cs
@@ -99,19 +99,21 @@ public override string FormatErrorMessage(string name) =>
string.Format(global::System.Globalization.CultureInfo.CurrentCulture, GetValidationErrorMessage(), name, Minimum, Maximum);
private bool NeedToConvertMinMax { get; }
private bool Initialized { get; set; }
+ private const string c_minMaxError = "The minimum and maximum values must be set to valid values.";
+
public override bool IsValid(object? value)
{
if (!Initialized)
{
if (Minimum is null || Maximum is null)
{
- throw new global::System.InvalidOperationException("The minimum and maximum values must be set to valid values.");
+ throw new global::System.InvalidOperationException(c_minMaxError);
}
if (NeedToConvertMinMax)
{
System.Globalization.CultureInfo culture = ParseLimitsInInvariantCulture ? global::System.Globalization.CultureInfo.InvariantCulture : global::System.Globalization.CultureInfo.CurrentCulture;
- Minimum = ConvertValue(Minimum, culture) ?? throw new global::System.InvalidOperationException("The minimum and maximum values must be set to valid values.");
- Maximum = ConvertValue(Maximum, culture) ?? throw new global::System.InvalidOperationException("The minimum and maximum values must be set to valid values.");
+ Minimum = ConvertValue(Minimum, culture) ?? throw new global::System.InvalidOperationException(c_minMaxError);
+ Maximum = ConvertValue(Maximum, culture) ?? throw new global::System.InvalidOperationException(c_minMaxError);
}
int cmp = ((global::System.IComparable)Minimum).CompareTo((global::System.IComparable)Maximum);
if (cmp > 0)
diff --git a/src/libraries/Microsoft.Extensions.Options/tests/SourceGeneration.Unit.Tests/Baselines/EmitterWithCustomValidator.netfx.g.cs b/src/libraries/Microsoft.Extensions.Options/tests/SourceGeneration.Unit.Tests/Baselines/EmitterWithCustomValidator.netfx.g.cs
index 9dc3ded5bd462..fe77e3e6bd924 100644
--- a/src/libraries/Microsoft.Extensions.Options/tests/SourceGeneration.Unit.Tests/Baselines/EmitterWithCustomValidator.netfx.g.cs
+++ b/src/libraries/Microsoft.Extensions.Options/tests/SourceGeneration.Unit.Tests/Baselines/EmitterWithCustomValidator.netfx.g.cs
@@ -97,19 +97,21 @@ public override string FormatErrorMessage(string name) =>
string.Format(global::System.Globalization.CultureInfo.CurrentCulture, GetValidationErrorMessage(), name, Minimum, Maximum);
private bool NeedToConvertMinMax { get; }
private bool Initialized { get; set; }
+ private const string c_minMaxError = "The minimum and maximum values must be set to valid values.";
+
public override bool IsValid(object? value)
{
if (!Initialized)
{
if (Minimum is null || Maximum is null)
{
- throw new global::System.InvalidOperationException("The minimum and maximum values must be set to valid values.");
+ throw new global::System.InvalidOperationException(c_minMaxError);
}
if (NeedToConvertMinMax)
{
System.Globalization.CultureInfo culture = ParseLimitsInInvariantCulture ? global::System.Globalization.CultureInfo.InvariantCulture : global::System.Globalization.CultureInfo.CurrentCulture;
- Minimum = ConvertValue(Minimum, culture) ?? throw new global::System.InvalidOperationException("The minimum and maximum values must be set to valid values.");
- Maximum = ConvertValue(Maximum, culture) ?? throw new global::System.InvalidOperationException("The minimum and maximum values must be set to valid values.");
+ Minimum = ConvertValue(Minimum, culture) ?? throw new global::System.InvalidOperationException(c_minMaxError);
+ Maximum = ConvertValue(Maximum, culture) ?? throw new global::System.InvalidOperationException(c_minMaxError);
}
int cmp = ((global::System.IComparable)Minimum).CompareTo((global::System.IComparable)Maximum);
if (cmp > 0)
diff --git a/src/libraries/Microsoft.Extensions.Options/tests/SourceGeneration.Unit.Tests/Baselines/GeneratedAttributesTest.netcore.lang10.g.cs b/src/libraries/Microsoft.Extensions.Options/tests/SourceGeneration.Unit.Tests/Baselines/GeneratedAttributesTest.netcore.lang10.g.cs
index cc9864a2619c4..7cf1fe61e1a94 100644
--- a/src/libraries/Microsoft.Extensions.Options/tests/SourceGeneration.Unit.Tests/Baselines/GeneratedAttributesTest.netcore.lang10.g.cs
+++ b/src/libraries/Microsoft.Extensions.Options/tests/SourceGeneration.Unit.Tests/Baselines/GeneratedAttributesTest.netcore.lang10.g.cs
@@ -150,6 +150,26 @@ partial class OptionsUsingGeneratedAttributesValidator
(builder ??= new()).AddResults(validationResults);
}
+ context.MemberName = "P13";
+ context.DisplayName = string.IsNullOrEmpty(name) ? "OptionsUsingGeneratedAttributes.P13" : $"{name}.P13";
+ validationResults.Clear();
+ validationAttributes.Clear();
+ validationAttributes.Add(global::__OptionValidationStaticInstances.__Attributes_2C497155.A6);
+ if (!global::System.ComponentModel.DataAnnotations.Validator.TryValidateValue(options.P13, context, validationResults, validationAttributes))
+ {
+ (builder ??= new()).AddResults(validationResults);
+ }
+
+ context.MemberName = "P14";
+ context.DisplayName = string.IsNullOrEmpty(name) ? "OptionsUsingGeneratedAttributes.P14" : $"{name}.P14";
+ validationResults.Clear();
+ validationAttributes.Clear();
+ validationAttributes.Add(global::__OptionValidationStaticInstances.__Attributes_2C497155.A7);
+ if (!global::System.ComponentModel.DataAnnotations.Validator.TryValidateValue(options.P14, context, validationResults, validationAttributes))
+ {
+ (builder ??= new()).AddResults(validationResults);
+ }
+
return builder is null ? global::Microsoft.Extensions.Options.ValidateOptionsResult.Success : builder.Build();
}
}
@@ -175,6 +195,16 @@ internal static class __Attributes_2C497155
internal static readonly __OptionValidationGeneratedAttributes.__SourceGen__2C497155_CompareAttribute A5 = new __OptionValidationGeneratedAttributes.__SourceGen__2C497155_CompareAttribute(
"P5");
+
+ internal static readonly __OptionValidationGeneratedAttributes.__SourceGen__2C497155_RangeAttribute A6 = new __OptionValidationGeneratedAttributes.__SourceGen__2C497155_RangeAttribute(
+ typeof(global::System.TimeSpan),
+ "00:00:00",
+ "23:59:59");
+
+ internal static readonly __OptionValidationGeneratedAttributes.__SourceGen__2C497155_RangeAttribute A7 = new __OptionValidationGeneratedAttributes.__SourceGen__2C497155_RangeAttribute(
+ typeof(global::System.TimeSpan),
+ "01:00:00",
+ "23:59:59");
}
}
namespace __OptionValidationStaticInstances
@@ -395,19 +425,34 @@ public override string FormatErrorMessage(string name) =>
string.Format(global::System.Globalization.CultureInfo.CurrentCulture, GetValidationErrorMessage(), name, Minimum, Maximum);
private bool NeedToConvertMinMax { get; }
private bool Initialized { get; set; }
+ private const string c_minMaxError = "The minimum and maximum values must be set to valid values.";
+
public override bool IsValid(object? value)
{
if (!Initialized)
{
if (Minimum is null || Maximum is null)
{
- throw new global::System.InvalidOperationException("The minimum and maximum values must be set to valid values.");
+ throw new global::System.InvalidOperationException(c_minMaxError);
}
if (NeedToConvertMinMax)
{
System.Globalization.CultureInfo culture = ParseLimitsInInvariantCulture ? global::System.Globalization.CultureInfo.InvariantCulture : global::System.Globalization.CultureInfo.CurrentCulture;
- Minimum = ConvertValue(Minimum, culture) ?? throw new global::System.InvalidOperationException("The minimum and maximum values must be set to valid values.");
- Maximum = ConvertValue(Maximum, culture) ?? throw new global::System.InvalidOperationException("The minimum and maximum values must be set to valid values.");
+ if (OperandType == typeof(global::System.TimeSpan))
+ {
+ if (!global::System.TimeSpan.TryParse((string)Minimum, culture, out global::System.TimeSpan timeSpanMinimum) ||
+ !global::System.TimeSpan.TryParse((string)Maximum, culture, out global::System.TimeSpan timeSpanMaximum))
+ {
+ throw new global::System.InvalidOperationException(c_minMaxError);
+ }
+ Minimum = timeSpanMinimum;
+ Maximum = timeSpanMaximum;
+ }
+ else
+ {
+ Minimum = ConvertValue(Minimum, culture) ?? throw new global::System.InvalidOperationException(c_minMaxError);
+ Maximum = ConvertValue(Maximum, culture) ?? throw new global::System.InvalidOperationException(c_minMaxError);
+ }
}
int cmp = ((global::System.IComparable)Minimum).CompareTo((global::System.IComparable)Maximum);
if (cmp > 0)
@@ -429,13 +474,35 @@ public override bool IsValid(object? value)
System.Globalization.CultureInfo formatProvider = ConvertValueInInvariantCulture ? global::System.Globalization.CultureInfo.InvariantCulture : global::System.Globalization.CultureInfo.CurrentCulture;
object? convertedValue;
- try
+ if (OperandType == typeof(global::System.TimeSpan))
{
- convertedValue = ConvertValue(value, formatProvider);
+ if (value is global::System.TimeSpan)
+ {
+ convertedValue = value;
+ }
+ else if (value is string)
+ {
+ if (!global::System.TimeSpan.TryParse((string)value, formatProvider, out global::System.TimeSpan timeSpanValue))
+ {
+ return false;
+ }
+ convertedValue = timeSpanValue;
+ }
+ else
+ {
+ throw new global::System.InvalidOperationException($"A value type {value.GetType()} that is not a TimeSpan or a string has been given. This might indicate a problem with the source generator.");
+ }
}
- catch (global::System.Exception e) when (e is global::System.FormatException or global::System.InvalidCastException or global::System.NotSupportedException)
+ else
{
- return false;
+ try
+ {
+ convertedValue = ConvertValue(value, formatProvider);
+ }
+ catch (global::System.Exception e) when (e is global::System.FormatException or global::System.InvalidCastException or global::System.NotSupportedException)
+ {
+ return false;
+ }
}
var min = (global::System.IComparable)Minimum;
diff --git a/src/libraries/Microsoft.Extensions.Options/tests/SourceGeneration.Unit.Tests/Baselines/GeneratedAttributesTest.netcore.lang11.g.cs b/src/libraries/Microsoft.Extensions.Options/tests/SourceGeneration.Unit.Tests/Baselines/GeneratedAttributesTest.netcore.lang11.g.cs
index 2a33e51b0b617..f7bba04603342 100644
--- a/src/libraries/Microsoft.Extensions.Options/tests/SourceGeneration.Unit.Tests/Baselines/GeneratedAttributesTest.netcore.lang11.g.cs
+++ b/src/libraries/Microsoft.Extensions.Options/tests/SourceGeneration.Unit.Tests/Baselines/GeneratedAttributesTest.netcore.lang11.g.cs
@@ -150,6 +150,26 @@ partial class OptionsUsingGeneratedAttributesValidator
(builder ??= new()).AddResults(validationResults);
}
+ context.MemberName = "P13";
+ context.DisplayName = string.IsNullOrEmpty(name) ? "OptionsUsingGeneratedAttributes.P13" : $"{name}.P13";
+ validationResults.Clear();
+ validationAttributes.Clear();
+ validationAttributes.Add(global::__OptionValidationStaticInstances.__Attributes.A6);
+ if (!global::System.ComponentModel.DataAnnotations.Validator.TryValidateValue(options.P13, context, validationResults, validationAttributes))
+ {
+ (builder ??= new()).AddResults(validationResults);
+ }
+
+ context.MemberName = "P14";
+ context.DisplayName = string.IsNullOrEmpty(name) ? "OptionsUsingGeneratedAttributes.P14" : $"{name}.P14";
+ validationResults.Clear();
+ validationAttributes.Clear();
+ validationAttributes.Add(global::__OptionValidationStaticInstances.__Attributes.A7);
+ if (!global::System.ComponentModel.DataAnnotations.Validator.TryValidateValue(options.P14, context, validationResults, validationAttributes))
+ {
+ (builder ??= new()).AddResults(validationResults);
+ }
+
return builder is null ? global::Microsoft.Extensions.Options.ValidateOptionsResult.Success : builder.Build();
}
}
@@ -175,6 +195,16 @@ file static class __Attributes
internal static readonly __OptionValidationGeneratedAttributes.__SourceGen__CompareAttribute A5 = new __OptionValidationGeneratedAttributes.__SourceGen__CompareAttribute(
"P5");
+
+ internal static readonly __OptionValidationGeneratedAttributes.__SourceGen__RangeAttribute A6 = new __OptionValidationGeneratedAttributes.__SourceGen__RangeAttribute(
+ typeof(global::System.TimeSpan),
+ "00:00:00",
+ "23:59:59");
+
+ internal static readonly __OptionValidationGeneratedAttributes.__SourceGen__RangeAttribute A7 = new __OptionValidationGeneratedAttributes.__SourceGen__RangeAttribute(
+ typeof(global::System.TimeSpan),
+ "01:00:00",
+ "23:59:59");
}
}
namespace __OptionValidationStaticInstances
@@ -395,19 +425,34 @@ public override string FormatErrorMessage(string name) =>
string.Format(global::System.Globalization.CultureInfo.CurrentCulture, GetValidationErrorMessage(), name, Minimum, Maximum);
private bool NeedToConvertMinMax { get; }
private bool Initialized { get; set; }
+ private const string c_minMaxError = "The minimum and maximum values must be set to valid values.";
+
public override bool IsValid(object? value)
{
if (!Initialized)
{
if (Minimum is null || Maximum is null)
{
- throw new global::System.InvalidOperationException("The minimum and maximum values must be set to valid values.");
+ throw new global::System.InvalidOperationException(c_minMaxError);
}
if (NeedToConvertMinMax)
{
System.Globalization.CultureInfo culture = ParseLimitsInInvariantCulture ? global::System.Globalization.CultureInfo.InvariantCulture : global::System.Globalization.CultureInfo.CurrentCulture;
- Minimum = ConvertValue(Minimum, culture) ?? throw new global::System.InvalidOperationException("The minimum and maximum values must be set to valid values.");
- Maximum = ConvertValue(Maximum, culture) ?? throw new global::System.InvalidOperationException("The minimum and maximum values must be set to valid values.");
+ if (OperandType == typeof(global::System.TimeSpan))
+ {
+ if (!global::System.TimeSpan.TryParse((string)Minimum, culture, out global::System.TimeSpan timeSpanMinimum) ||
+ !global::System.TimeSpan.TryParse((string)Maximum, culture, out global::System.TimeSpan timeSpanMaximum))
+ {
+ throw new global::System.InvalidOperationException(c_minMaxError);
+ }
+ Minimum = timeSpanMinimum;
+ Maximum = timeSpanMaximum;
+ }
+ else
+ {
+ Minimum = ConvertValue(Minimum, culture) ?? throw new global::System.InvalidOperationException(c_minMaxError);
+ Maximum = ConvertValue(Maximum, culture) ?? throw new global::System.InvalidOperationException(c_minMaxError);
+ }
}
int cmp = ((global::System.IComparable)Minimum).CompareTo((global::System.IComparable)Maximum);
if (cmp > 0)
@@ -429,13 +474,35 @@ public override bool IsValid(object? value)
System.Globalization.CultureInfo formatProvider = ConvertValueInInvariantCulture ? global::System.Globalization.CultureInfo.InvariantCulture : global::System.Globalization.CultureInfo.CurrentCulture;
object? convertedValue;
- try
+ if (OperandType == typeof(global::System.TimeSpan))
{
- convertedValue = ConvertValue(value, formatProvider);
+ if (value is global::System.TimeSpan)
+ {
+ convertedValue = value;
+ }
+ else if (value is string)
+ {
+ if (!global::System.TimeSpan.TryParse((string)value, formatProvider, out global::System.TimeSpan timeSpanValue))
+ {
+ return false;
+ }
+ convertedValue = timeSpanValue;
+ }
+ else
+ {
+ throw new global::System.InvalidOperationException($"A value type {value.GetType()} that is not a TimeSpan or a string has been given. This might indicate a problem with the source generator.");
+ }
}
- catch (global::System.Exception e) when (e is global::System.FormatException or global::System.InvalidCastException or global::System.NotSupportedException)
+ else
{
- return false;
+ try
+ {
+ convertedValue = ConvertValue(value, formatProvider);
+ }
+ catch (global::System.Exception e) when (e is global::System.FormatException or global::System.InvalidCastException or global::System.NotSupportedException)
+ {
+ return false;
+ }
}
var min = (global::System.IComparable)Minimum;
diff --git a/src/libraries/Microsoft.Extensions.Options/tests/SourceGeneration.Unit.Tests/Baselines/GeneratedAttributesTest.netfx.lang10.g.cs b/src/libraries/Microsoft.Extensions.Options/tests/SourceGeneration.Unit.Tests/Baselines/GeneratedAttributesTest.netfx.lang10.g.cs
index 7f5eb90a20281..4b28eb159d147 100644
--- a/src/libraries/Microsoft.Extensions.Options/tests/SourceGeneration.Unit.Tests/Baselines/GeneratedAttributesTest.netfx.lang10.g.cs
+++ b/src/libraries/Microsoft.Extensions.Options/tests/SourceGeneration.Unit.Tests/Baselines/GeneratedAttributesTest.netfx.lang10.g.cs
@@ -118,6 +118,26 @@ partial class OptionsUsingGeneratedAttributesValidator
(builder ??= new()).AddResults(validationResults);
}
+ context.MemberName = "P13";
+ context.DisplayName = string.IsNullOrEmpty(name) ? "OptionsUsingGeneratedAttributes.P13" : $"{name}.P13";
+ validationResults.Clear();
+ validationAttributes.Clear();
+ validationAttributes.Add(global::__OptionValidationStaticInstances.__Attributes_2C497155.A5);
+ if (!global::System.ComponentModel.DataAnnotations.Validator.TryValidateValue(options.P13, context, validationResults, validationAttributes))
+ {
+ (builder ??= new()).AddResults(validationResults);
+ }
+
+ context.MemberName = "P14";
+ context.DisplayName = string.IsNullOrEmpty(name) ? "OptionsUsingGeneratedAttributes.P14" : $"{name}.P14";
+ validationResults.Clear();
+ validationAttributes.Clear();
+ validationAttributes.Add(global::__OptionValidationStaticInstances.__Attributes_2C497155.A6);
+ if (!global::System.ComponentModel.DataAnnotations.Validator.TryValidateValue(options.P14, context, validationResults, validationAttributes))
+ {
+ (builder ??= new()).AddResults(validationResults);
+ }
+
return builder is null ? global::Microsoft.Extensions.Options.ValidateOptionsResult.Success : builder.Build();
}
}
@@ -139,6 +159,16 @@ internal static class __Attributes_2C497155
internal static readonly __OptionValidationGeneratedAttributes.__SourceGen__2C497155_CompareAttribute A4 = new __OptionValidationGeneratedAttributes.__SourceGen__2C497155_CompareAttribute(
"P5");
+
+ internal static readonly __OptionValidationGeneratedAttributes.__SourceGen__2C497155_RangeAttribute A5 = new __OptionValidationGeneratedAttributes.__SourceGen__2C497155_RangeAttribute(
+ typeof(global::System.TimeSpan),
+ "00:00:00",
+ "23:59:59");
+
+ internal static readonly __OptionValidationGeneratedAttributes.__SourceGen__2C497155_RangeAttribute A6 = new __OptionValidationGeneratedAttributes.__SourceGen__2C497155_RangeAttribute(
+ typeof(global::System.TimeSpan),
+ "01:00:00",
+ "23:59:59");
}
}
namespace __OptionValidationStaticInstances
@@ -310,19 +340,34 @@ public override string FormatErrorMessage(string name) =>
string.Format(global::System.Globalization.CultureInfo.CurrentCulture, GetValidationErrorMessage(), name, Minimum, Maximum);
private bool NeedToConvertMinMax { get; }
private bool Initialized { get; set; }
+ private const string c_minMaxError = "The minimum and maximum values must be set to valid values.";
+
public override bool IsValid(object? value)
{
if (!Initialized)
{
if (Minimum is null || Maximum is null)
{
- throw new global::System.InvalidOperationException("The minimum and maximum values must be set to valid values.");
+ throw new global::System.InvalidOperationException(c_minMaxError);
}
if (NeedToConvertMinMax)
{
System.Globalization.CultureInfo culture = ParseLimitsInInvariantCulture ? global::System.Globalization.CultureInfo.InvariantCulture : global::System.Globalization.CultureInfo.CurrentCulture;
- Minimum = ConvertValue(Minimum, culture) ?? throw new global::System.InvalidOperationException("The minimum and maximum values must be set to valid values.");
- Maximum = ConvertValue(Maximum, culture) ?? throw new global::System.InvalidOperationException("The minimum and maximum values must be set to valid values.");
+ if (OperandType == typeof(global::System.TimeSpan))
+ {
+ if (!global::System.TimeSpan.TryParse((string)Minimum, culture, out global::System.TimeSpan timeSpanMinimum) ||
+ !global::System.TimeSpan.TryParse((string)Maximum, culture, out global::System.TimeSpan timeSpanMaximum))
+ {
+ throw new global::System.InvalidOperationException(c_minMaxError);
+ }
+ Minimum = timeSpanMinimum;
+ Maximum = timeSpanMaximum;
+ }
+ else
+ {
+ Minimum = ConvertValue(Minimum, culture) ?? throw new global::System.InvalidOperationException(c_minMaxError);
+ Maximum = ConvertValue(Maximum, culture) ?? throw new global::System.InvalidOperationException(c_minMaxError);
+ }
}
int cmp = ((global::System.IComparable)Minimum).CompareTo((global::System.IComparable)Maximum);
if (cmp > 0)
@@ -344,13 +389,35 @@ public override bool IsValid(object? value)
System.Globalization.CultureInfo formatProvider = ConvertValueInInvariantCulture ? global::System.Globalization.CultureInfo.InvariantCulture : global::System.Globalization.CultureInfo.CurrentCulture;
object? convertedValue;
- try
+ if (OperandType == typeof(global::System.TimeSpan))
{
- convertedValue = ConvertValue(value, formatProvider);
+ if (value is global::System.TimeSpan)
+ {
+ convertedValue = value;
+ }
+ else if (value is string)
+ {
+ if (!global::System.TimeSpan.TryParse((string)value, formatProvider, out global::System.TimeSpan timeSpanValue))
+ {
+ return false;
+ }
+ convertedValue = timeSpanValue;
+ }
+ else
+ {
+ throw new global::System.InvalidOperationException($"A value type {value.GetType()} that is not a TimeSpan or a string has been given. This might indicate a problem with the source generator.");
+ }
}
- catch (global::System.Exception e) when (e is global::System.FormatException or global::System.InvalidCastException or global::System.NotSupportedException)
+ else
{
- return false;
+ try
+ {
+ convertedValue = ConvertValue(value, formatProvider);
+ }
+ catch (global::System.Exception e) when (e is global::System.FormatException or global::System.InvalidCastException or global::System.NotSupportedException)
+ {
+ return false;
+ }
}
var min = (global::System.IComparable)Minimum;
diff --git a/src/libraries/Microsoft.Extensions.Options/tests/SourceGeneration.Unit.Tests/Baselines/GeneratedAttributesTest.netfx.lang11.g.cs b/src/libraries/Microsoft.Extensions.Options/tests/SourceGeneration.Unit.Tests/Baselines/GeneratedAttributesTest.netfx.lang11.g.cs
index 3ab56e21320a0..4c300abc6d05b 100644
--- a/src/libraries/Microsoft.Extensions.Options/tests/SourceGeneration.Unit.Tests/Baselines/GeneratedAttributesTest.netfx.lang11.g.cs
+++ b/src/libraries/Microsoft.Extensions.Options/tests/SourceGeneration.Unit.Tests/Baselines/GeneratedAttributesTest.netfx.lang11.g.cs
@@ -118,6 +118,26 @@ partial class OptionsUsingGeneratedAttributesValidator
(builder ??= new()).AddResults(validationResults);
}
+ context.MemberName = "P13";
+ context.DisplayName = string.IsNullOrEmpty(name) ? "OptionsUsingGeneratedAttributes.P13" : $"{name}.P13";
+ validationResults.Clear();
+ validationAttributes.Clear();
+ validationAttributes.Add(global::__OptionValidationStaticInstances.__Attributes.A5);
+ if (!global::System.ComponentModel.DataAnnotations.Validator.TryValidateValue(options.P13, context, validationResults, validationAttributes))
+ {
+ (builder ??= new()).AddResults(validationResults);
+ }
+
+ context.MemberName = "P14";
+ context.DisplayName = string.IsNullOrEmpty(name) ? "OptionsUsingGeneratedAttributes.P14" : $"{name}.P14";
+ validationResults.Clear();
+ validationAttributes.Clear();
+ validationAttributes.Add(global::__OptionValidationStaticInstances.__Attributes.A6);
+ if (!global::System.ComponentModel.DataAnnotations.Validator.TryValidateValue(options.P14, context, validationResults, validationAttributes))
+ {
+ (builder ??= new()).AddResults(validationResults);
+ }
+
return builder is null ? global::Microsoft.Extensions.Options.ValidateOptionsResult.Success : builder.Build();
}
}
@@ -139,6 +159,16 @@ file static class __Attributes
internal static readonly __OptionValidationGeneratedAttributes.__SourceGen__CompareAttribute A4 = new __OptionValidationGeneratedAttributes.__SourceGen__CompareAttribute(
"P5");
+
+ internal static readonly __OptionValidationGeneratedAttributes.__SourceGen__RangeAttribute A5 = new __OptionValidationGeneratedAttributes.__SourceGen__RangeAttribute(
+ typeof(global::System.TimeSpan),
+ "00:00:00",
+ "23:59:59");
+
+ internal static readonly __OptionValidationGeneratedAttributes.__SourceGen__RangeAttribute A6 = new __OptionValidationGeneratedAttributes.__SourceGen__RangeAttribute(
+ typeof(global::System.TimeSpan),
+ "01:00:00",
+ "23:59:59");
}
}
namespace __OptionValidationStaticInstances
@@ -310,19 +340,34 @@ public override string FormatErrorMessage(string name) =>
string.Format(global::System.Globalization.CultureInfo.CurrentCulture, GetValidationErrorMessage(), name, Minimum, Maximum);
private bool NeedToConvertMinMax { get; }
private bool Initialized { get; set; }
+ private const string c_minMaxError = "The minimum and maximum values must be set to valid values.";
+
public override bool IsValid(object? value)
{
if (!Initialized)
{
if (Minimum is null || Maximum is null)
{
- throw new global::System.InvalidOperationException("The minimum and maximum values must be set to valid values.");
+ throw new global::System.InvalidOperationException(c_minMaxError);
}
if (NeedToConvertMinMax)
{
System.Globalization.CultureInfo culture = ParseLimitsInInvariantCulture ? global::System.Globalization.CultureInfo.InvariantCulture : global::System.Globalization.CultureInfo.CurrentCulture;
- Minimum = ConvertValue(Minimum, culture) ?? throw new global::System.InvalidOperationException("The minimum and maximum values must be set to valid values.");
- Maximum = ConvertValue(Maximum, culture) ?? throw new global::System.InvalidOperationException("The minimum and maximum values must be set to valid values.");
+ if (OperandType == typeof(global::System.TimeSpan))
+ {
+ if (!global::System.TimeSpan.TryParse((string)Minimum, culture, out global::System.TimeSpan timeSpanMinimum) ||
+ !global::System.TimeSpan.TryParse((string)Maximum, culture, out global::System.TimeSpan timeSpanMaximum))
+ {
+ throw new global::System.InvalidOperationException(c_minMaxError);
+ }
+ Minimum = timeSpanMinimum;
+ Maximum = timeSpanMaximum;
+ }
+ else
+ {
+ Minimum = ConvertValue(Minimum, culture) ?? throw new global::System.InvalidOperationException(c_minMaxError);
+ Maximum = ConvertValue(Maximum, culture) ?? throw new global::System.InvalidOperationException(c_minMaxError);
+ }
}
int cmp = ((global::System.IComparable)Minimum).CompareTo((global::System.IComparable)Maximum);
if (cmp > 0)
@@ -344,13 +389,35 @@ public override bool IsValid(object? value)
System.Globalization.CultureInfo formatProvider = ConvertValueInInvariantCulture ? global::System.Globalization.CultureInfo.InvariantCulture : global::System.Globalization.CultureInfo.CurrentCulture;
object? convertedValue;
- try
+ if (OperandType == typeof(global::System.TimeSpan))
{
- convertedValue = ConvertValue(value, formatProvider);
+ if (value is global::System.TimeSpan)
+ {
+ convertedValue = value;
+ }
+ else if (value is string)
+ {
+ if (!global::System.TimeSpan.TryParse((string)value, formatProvider, out global::System.TimeSpan timeSpanValue))
+ {
+ return false;
+ }
+ convertedValue = timeSpanValue;
+ }
+ else
+ {
+ throw new global::System.InvalidOperationException($"A value type {value.GetType()} that is not a TimeSpan or a string has been given. This might indicate a problem with the source generator.");
+ }
}
- catch (global::System.Exception e) when (e is global::System.FormatException or global::System.InvalidCastException or global::System.NotSupportedException)
+ else
{
- return false;
+ try
+ {
+ convertedValue = ConvertValue(value, formatProvider);
+ }
+ catch (global::System.Exception e) when (e is global::System.FormatException or global::System.InvalidCastException or global::System.NotSupportedException)
+ {
+ return false;
+ }
}
var min = (global::System.IComparable)Minimum;
diff --git a/src/libraries/Microsoft.Extensions.Options/tests/SourceGeneration.Unit.Tests/Main.cs b/src/libraries/Microsoft.Extensions.Options/tests/SourceGeneration.Unit.Tests/Main.cs
index 623251707f87b..c72be0d72c2c0 100644
--- a/src/libraries/Microsoft.Extensions.Options/tests/SourceGeneration.Unit.Tests/Main.cs
+++ b/src/libraries/Microsoft.Extensions.Options/tests/SourceGeneration.Unit.Tests/Main.cs
@@ -1731,6 +1731,7 @@ public async Task GeneratedAttributesTest(LanguageVersion languageVersion)
#endif //NETCOREAPP
string source = $$"""
+ using System;
using System.Collections.Generic;
using Microsoft.Extensions.Options;
using System.ComponentModel.DataAnnotations;
@@ -1782,6 +1783,12 @@ public class OptionsUsingGeneratedAttributes
[MaxLengthAttribute(5)]
public List? P12 { get; set; }
+
+ [RangeAttribute(typeof(TimeSpan), "00:00:00", "23:59:59")]
+ public string? P13 { get; set; }
+
+ [RangeAttribute(typeof(TimeSpan), "01:00:00", "23:59:59")]
+ public TimeSpan P14 { get; set; }
}
[OptionsValidator]
diff --git a/src/libraries/Microsoft.Extensions.Options/tests/SourceGenerationTests/Baselines/NetCoreApp/Validators.g.cs b/src/libraries/Microsoft.Extensions.Options/tests/SourceGenerationTests/Baselines/NetCoreApp/Validators.g.cs
index 956cae26e90f6..93c101431004c 100644
--- a/src/libraries/Microsoft.Extensions.Options/tests/SourceGenerationTests/Baselines/NetCoreApp/Validators.g.cs
+++ b/src/libraries/Microsoft.Extensions.Options/tests/SourceGenerationTests/Baselines/NetCoreApp/Validators.g.cs
@@ -1721,6 +1721,68 @@ partial class MultipleAttributeModelValidator
}
}
namespace TestClasses.OptionsValidation
+{
+ partial class OptionsUsingRangeWithTimeSpanValidator
+ {
+ ///
+ /// Validates a specific named options instance (or all when is ).
+ ///
+ /// The name of the options instance being validated.
+ /// The options instance.
+ /// Validation result.
+ [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Options.SourceGeneration", "42.42.42.42")]
+ [System.Diagnostics.CodeAnalysis.UnconditionalSuppressMessage("Trimming", "IL2026:RequiresUnreferencedCode",
+ Justification = "The created ValidationContext object is used in a way that never call reflection")]
+ public global::Microsoft.Extensions.Options.ValidateOptionsResult Validate(string? name, global::TestClasses.OptionsValidation.OptionsUsingRangeWithTimeSpan options)
+ {
+ global::Microsoft.Extensions.Options.ValidateOptionsResultBuilder? builder = null;
+ var context = new global::System.ComponentModel.DataAnnotations.ValidationContext(options);
+ var validationResults = new global::System.Collections.Generic.List();
+ var validationAttributes = new global::System.Collections.Generic.List(1);
+
+ context.MemberName = "P1";
+ context.DisplayName = string.IsNullOrEmpty(name) ? "OptionsUsingRangeWithTimeSpan.P1" : $"{name}.P1";
+ validationAttributes.Add(global::__OptionValidationStaticInstances.__Attributes.A19);
+ if (!global::System.ComponentModel.DataAnnotations.Validator.TryValidateValue(options.P1, context, validationResults, validationAttributes))
+ {
+ (builder ??= new()).AddResults(validationResults);
+ }
+
+ context.MemberName = "P2";
+ context.DisplayName = string.IsNullOrEmpty(name) ? "OptionsUsingRangeWithTimeSpan.P2" : $"{name}.P2";
+ validationResults.Clear();
+ validationAttributes.Clear();
+ validationAttributes.Add(global::__OptionValidationStaticInstances.__Attributes.A19);
+ if (!global::System.ComponentModel.DataAnnotations.Validator.TryValidateValue(options.P2, context, validationResults, validationAttributes))
+ {
+ (builder ??= new()).AddResults(validationResults);
+ }
+
+ context.MemberName = "P3";
+ context.DisplayName = string.IsNullOrEmpty(name) ? "OptionsUsingRangeWithTimeSpan.P3" : $"{name}.P3";
+ validationResults.Clear();
+ validationAttributes.Clear();
+ validationAttributes.Add(global::__OptionValidationStaticInstances.__Attributes.A19);
+ if (!global::System.ComponentModel.DataAnnotations.Validator.TryValidateValue(options.P3, context, validationResults, validationAttributes))
+ {
+ (builder ??= new()).AddResults(validationResults);
+ }
+
+ context.MemberName = "P4";
+ context.DisplayName = string.IsNullOrEmpty(name) ? "OptionsUsingRangeWithTimeSpan.P4" : $"{name}.P4";
+ validationResults.Clear();
+ validationAttributes.Clear();
+ validationAttributes.Add(global::__OptionValidationStaticInstances.__Attributes.A19);
+ if (!global::System.ComponentModel.DataAnnotations.Validator.TryValidateValue(options.P4, context, validationResults, validationAttributes))
+ {
+ (builder ??= new()).AddResults(validationResults);
+ }
+
+ return builder is null ? global::Microsoft.Extensions.Options.ValidateOptionsResult.Success : builder.Build();
+ }
+ }
+}
+namespace TestClasses.OptionsValidation
{
partial class RangeAttributeModelDateValidator
{
@@ -1742,7 +1804,7 @@ partial class RangeAttributeModelDateValidator
context.MemberName = "Val";
context.DisplayName = string.IsNullOrEmpty(name) ? "RangeAttributeModelDate.Val" : $"{name}.Val";
- validationAttributes.Add(global::__OptionValidationStaticInstances.__Attributes.A19);
+ validationAttributes.Add(global::__OptionValidationStaticInstances.__Attributes.A20);
if (!global::System.ComponentModel.DataAnnotations.Validator.TryValidateValue(options.Val, context, validationResults, validationAttributes))
{
(builder ??= new()).AddResults(validationResults);
@@ -1838,7 +1900,7 @@ partial class RegularExpressionAttributeModelValidator
context.MemberName = "Val";
context.DisplayName = string.IsNullOrEmpty(name) ? "RegularExpressionAttributeModel.Val" : $"{name}.Val";
- validationAttributes.Add(global::__OptionValidationStaticInstances.__Attributes.A20);
+ validationAttributes.Add(global::__OptionValidationStaticInstances.__Attributes.A21);
if (!global::System.ComponentModel.DataAnnotations.Validator.TryValidateValue(options.Val, context, validationResults, validationAttributes))
{
(builder ??= new()).AddResults(validationResults);
@@ -2039,6 +2101,11 @@ file static class __Attributes
(int)9);
internal static readonly __OptionValidationGeneratedAttributes.__SourceGen__RangeAttribute A19 = new __OptionValidationGeneratedAttributes.__SourceGen__RangeAttribute(
+ typeof(global::System.TimeSpan),
+ "00:00:00",
+ "00:00:10");
+
+ internal static readonly __OptionValidationGeneratedAttributes.__SourceGen__RangeAttribute A20 = new __OptionValidationGeneratedAttributes.__SourceGen__RangeAttribute(
typeof(global::System.DateTime),
"1/2/2004",
"3/4/2004")
@@ -2046,7 +2113,7 @@ file static class __Attributes
ParseLimitsInInvariantCulture = true
};
- internal static readonly global::System.ComponentModel.DataAnnotations.RegularExpressionAttribute A20 = new global::System.ComponentModel.DataAnnotations.RegularExpressionAttribute(
+ internal static readonly global::System.ComponentModel.DataAnnotations.RegularExpressionAttribute A21 = new global::System.ComponentModel.DataAnnotations.RegularExpressionAttribute(
"\\s");
}
}
@@ -2143,19 +2210,34 @@ public override string FormatErrorMessage(string name) =>
string.Format(global::System.Globalization.CultureInfo.CurrentCulture, GetValidationErrorMessage(), name, Minimum, Maximum);
private bool NeedToConvertMinMax { get; }
private bool Initialized { get; set; }
+ private const string c_minMaxError = "The minimum and maximum values must be set to valid values.";
+
public override bool IsValid(object? value)
{
if (!Initialized)
{
if (Minimum is null || Maximum is null)
{
- throw new global::System.InvalidOperationException("The minimum and maximum values must be set to valid values.");
+ throw new global::System.InvalidOperationException(c_minMaxError);
}
if (NeedToConvertMinMax)
{
System.Globalization.CultureInfo culture = ParseLimitsInInvariantCulture ? global::System.Globalization.CultureInfo.InvariantCulture : global::System.Globalization.CultureInfo.CurrentCulture;
- Minimum = ConvertValue(Minimum, culture) ?? throw new global::System.InvalidOperationException("The minimum and maximum values must be set to valid values.");
- Maximum = ConvertValue(Maximum, culture) ?? throw new global::System.InvalidOperationException("The minimum and maximum values must be set to valid values.");
+ if (OperandType == typeof(global::System.TimeSpan))
+ {
+ if (!global::System.TimeSpan.TryParse((string)Minimum, culture, out global::System.TimeSpan timeSpanMinimum) ||
+ !global::System.TimeSpan.TryParse((string)Maximum, culture, out global::System.TimeSpan timeSpanMaximum))
+ {
+ throw new global::System.InvalidOperationException(c_minMaxError);
+ }
+ Minimum = timeSpanMinimum;
+ Maximum = timeSpanMaximum;
+ }
+ else
+ {
+ Minimum = ConvertValue(Minimum, culture) ?? throw new global::System.InvalidOperationException(c_minMaxError);
+ Maximum = ConvertValue(Maximum, culture) ?? throw new global::System.InvalidOperationException(c_minMaxError);
+ }
}
int cmp = ((global::System.IComparable)Minimum).CompareTo((global::System.IComparable)Maximum);
if (cmp > 0)
@@ -2177,13 +2259,35 @@ public override bool IsValid(object? value)
System.Globalization.CultureInfo formatProvider = ConvertValueInInvariantCulture ? global::System.Globalization.CultureInfo.InvariantCulture : global::System.Globalization.CultureInfo.CurrentCulture;
object? convertedValue;
- try
+ if (OperandType == typeof(global::System.TimeSpan))
{
- convertedValue = ConvertValue(value, formatProvider);
+ if (value is global::System.TimeSpan)
+ {
+ convertedValue = value;
+ }
+ else if (value is string)
+ {
+ if (!global::System.TimeSpan.TryParse((string)value, formatProvider, out global::System.TimeSpan timeSpanValue))
+ {
+ return false;
+ }
+ convertedValue = timeSpanValue;
+ }
+ else
+ {
+ throw new global::System.InvalidOperationException($"A value type {value.GetType()} that is not a TimeSpan or a string has been given. This might indicate a problem with the source generator.");
+ }
}
- catch (global::System.Exception e) when (e is global::System.FormatException or global::System.InvalidCastException or global::System.NotSupportedException)
+ else
{
- return false;
+ try
+ {
+ convertedValue = ConvertValue(value, formatProvider);
+ }
+ catch (global::System.Exception e) when (e is global::System.FormatException or global::System.InvalidCastException or global::System.NotSupportedException)
+ {
+ return false;
+ }
}
var min = (global::System.IComparable)Minimum;
diff --git a/src/libraries/Microsoft.Extensions.Options/tests/SourceGenerationTests/Baselines/NetFX/Validators.g.cs b/src/libraries/Microsoft.Extensions.Options/tests/SourceGenerationTests/Baselines/NetFX/Validators.g.cs
index faae7d62d9c41..3c9f86fd84f8a 100644
--- a/src/libraries/Microsoft.Extensions.Options/tests/SourceGenerationTests/Baselines/NetFX/Validators.g.cs
+++ b/src/libraries/Microsoft.Extensions.Options/tests/SourceGenerationTests/Baselines/NetFX/Validators.g.cs
@@ -1637,6 +1637,66 @@ partial class MultipleAttributeModelValidator
}
}
namespace TestClasses.OptionsValidation
+{
+ partial class OptionsUsingRangeWithTimeSpanValidator
+ {
+ ///
+ /// Validates a specific named options instance (or all when is ).
+ ///
+ /// The name of the options instance being validated.
+ /// The options instance.
+ /// Validation result.
+ [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Options.SourceGeneration", "42.42.42.42")]
+ public global::Microsoft.Extensions.Options.ValidateOptionsResult Validate(string? name, global::TestClasses.OptionsValidation.OptionsUsingRangeWithTimeSpan options)
+ {
+ global::Microsoft.Extensions.Options.ValidateOptionsResultBuilder? builder = null;
+ var context = new global::System.ComponentModel.DataAnnotations.ValidationContext(options);
+ var validationResults = new global::System.Collections.Generic.List();
+ var validationAttributes = new global::System.Collections.Generic.List(1);
+
+ context.MemberName = "P1";
+ context.DisplayName = string.IsNullOrEmpty(name) ? "OptionsUsingRangeWithTimeSpan.P1" : $"{name}.P1";
+ validationAttributes.Add(global::__OptionValidationStaticInstances.__Attributes.A19);
+ if (!global::System.ComponentModel.DataAnnotations.Validator.TryValidateValue(options.P1, context, validationResults, validationAttributes))
+ {
+ (builder ??= new()).AddResults(validationResults);
+ }
+
+ context.MemberName = "P2";
+ context.DisplayName = string.IsNullOrEmpty(name) ? "OptionsUsingRangeWithTimeSpan.P2" : $"{name}.P2";
+ validationResults.Clear();
+ validationAttributes.Clear();
+ validationAttributes.Add(global::__OptionValidationStaticInstances.__Attributes.A19);
+ if (!global::System.ComponentModel.DataAnnotations.Validator.TryValidateValue(options.P2, context, validationResults, validationAttributes))
+ {
+ (builder ??= new()).AddResults(validationResults);
+ }
+
+ context.MemberName = "P3";
+ context.DisplayName = string.IsNullOrEmpty(name) ? "OptionsUsingRangeWithTimeSpan.P3" : $"{name}.P3";
+ validationResults.Clear();
+ validationAttributes.Clear();
+ validationAttributes.Add(global::__OptionValidationStaticInstances.__Attributes.A19);
+ if (!global::System.ComponentModel.DataAnnotations.Validator.TryValidateValue(options.P3, context, validationResults, validationAttributes))
+ {
+ (builder ??= new()).AddResults(validationResults);
+ }
+
+ context.MemberName = "P4";
+ context.DisplayName = string.IsNullOrEmpty(name) ? "OptionsUsingRangeWithTimeSpan.P4" : $"{name}.P4";
+ validationResults.Clear();
+ validationAttributes.Clear();
+ validationAttributes.Add(global::__OptionValidationStaticInstances.__Attributes.A19);
+ if (!global::System.ComponentModel.DataAnnotations.Validator.TryValidateValue(options.P4, context, validationResults, validationAttributes))
+ {
+ (builder ??= new()).AddResults(validationResults);
+ }
+
+ return builder is null ? global::Microsoft.Extensions.Options.ValidateOptionsResult.Success : builder.Build();
+ }
+ }
+}
+namespace TestClasses.OptionsValidation
{
partial class RangeAttributeModelDateValidator
{
@@ -1746,7 +1806,7 @@ partial class RegularExpressionAttributeModelValidator
context.MemberName = "Val";
context.DisplayName = string.IsNullOrEmpty(name) ? "RegularExpressionAttributeModel.Val" : $"{name}.Val";
- validationAttributes.Add(global::__OptionValidationStaticInstances.__Attributes.A19);
+ validationAttributes.Add(global::__OptionValidationStaticInstances.__Attributes.A20);
if (!global::System.ComponentModel.DataAnnotations.Validator.TryValidateValue(options.Val, context, validationResults, validationAttributes))
{
(builder ??= new()).AddResults(validationResults);
@@ -1940,7 +2000,12 @@ file static class __Attributes
(int)5,
(int)9);
- internal static readonly global::System.ComponentModel.DataAnnotations.RegularExpressionAttribute A19 = new global::System.ComponentModel.DataAnnotations.RegularExpressionAttribute(
+ internal static readonly __OptionValidationGeneratedAttributes.__SourceGen__RangeAttribute A19 = new __OptionValidationGeneratedAttributes.__SourceGen__RangeAttribute(
+ typeof(global::System.TimeSpan),
+ "00:00:00",
+ "00:00:10");
+
+ internal static readonly global::System.ComponentModel.DataAnnotations.RegularExpressionAttribute A20 = new global::System.ComponentModel.DataAnnotations.RegularExpressionAttribute(
"\\s");
}
}
@@ -2037,19 +2102,34 @@ public override string FormatErrorMessage(string name) =>
string.Format(global::System.Globalization.CultureInfo.CurrentCulture, GetValidationErrorMessage(), name, Minimum, Maximum);
private bool NeedToConvertMinMax { get; }
private bool Initialized { get; set; }
+ private const string c_minMaxError = "The minimum and maximum values must be set to valid values.";
+
public override bool IsValid(object? value)
{
if (!Initialized)
{
if (Minimum is null || Maximum is null)
{
- throw new global::System.InvalidOperationException("The minimum and maximum values must be set to valid values.");
+ throw new global::System.InvalidOperationException(c_minMaxError);
}
if (NeedToConvertMinMax)
{
System.Globalization.CultureInfo culture = ParseLimitsInInvariantCulture ? global::System.Globalization.CultureInfo.InvariantCulture : global::System.Globalization.CultureInfo.CurrentCulture;
- Minimum = ConvertValue(Minimum, culture) ?? throw new global::System.InvalidOperationException("The minimum and maximum values must be set to valid values.");
- Maximum = ConvertValue(Maximum, culture) ?? throw new global::System.InvalidOperationException("The minimum and maximum values must be set to valid values.");
+ if (OperandType == typeof(global::System.TimeSpan))
+ {
+ if (!global::System.TimeSpan.TryParse((string)Minimum, culture, out global::System.TimeSpan timeSpanMinimum) ||
+ !global::System.TimeSpan.TryParse((string)Maximum, culture, out global::System.TimeSpan timeSpanMaximum))
+ {
+ throw new global::System.InvalidOperationException(c_minMaxError);
+ }
+ Minimum = timeSpanMinimum;
+ Maximum = timeSpanMaximum;
+ }
+ else
+ {
+ Minimum = ConvertValue(Minimum, culture) ?? throw new global::System.InvalidOperationException(c_minMaxError);
+ Maximum = ConvertValue(Maximum, culture) ?? throw new global::System.InvalidOperationException(c_minMaxError);
+ }
}
int cmp = ((global::System.IComparable)Minimum).CompareTo((global::System.IComparable)Maximum);
if (cmp > 0)
@@ -2071,13 +2151,35 @@ public override bool IsValid(object? value)
System.Globalization.CultureInfo formatProvider = ConvertValueInInvariantCulture ? global::System.Globalization.CultureInfo.InvariantCulture : global::System.Globalization.CultureInfo.CurrentCulture;
object? convertedValue;
- try
+ if (OperandType == typeof(global::System.TimeSpan))
{
- convertedValue = ConvertValue(value, formatProvider);
+ if (value is global::System.TimeSpan)
+ {
+ convertedValue = value;
+ }
+ else if (value is string)
+ {
+ if (!global::System.TimeSpan.TryParse((string)value, formatProvider, out global::System.TimeSpan timeSpanValue))
+ {
+ return false;
+ }
+ convertedValue = timeSpanValue;
+ }
+ else
+ {
+ throw new global::System.InvalidOperationException($"A value type {value.GetType()} that is not a TimeSpan or a string has been given. This might indicate a problem with the source generator.");
+ }
}
- catch (global::System.Exception e) when (e is global::System.FormatException or global::System.InvalidCastException or global::System.NotSupportedException)
+ else
{
- return false;
+ try
+ {
+ convertedValue = ConvertValue(value, formatProvider);
+ }
+ catch (global::System.Exception e) when (e is global::System.FormatException or global::System.InvalidCastException or global::System.NotSupportedException)
+ {
+ return false;
+ }
}
var min = (global::System.IComparable)Minimum;
diff --git a/src/libraries/Microsoft.Extensions.Options/tests/SourceGenerationTests/Generated/OptionsValidationTests.cs b/src/libraries/Microsoft.Extensions.Options/tests/SourceGenerationTests/Generated/OptionsValidationTests.cs
index 49941010f1ace..4f9770ce7a17c 100644
--- a/src/libraries/Microsoft.Extensions.Options/tests/SourceGenerationTests/Generated/OptionsValidationTests.cs
+++ b/src/libraries/Microsoft.Extensions.Options/tests/SourceGenerationTests/Generated/OptionsValidationTests.cs
@@ -4,6 +4,7 @@
using System;
using System.ComponentModel.DataAnnotations;
using System.Globalization;
+using System.Linq;
using Microsoft.Extensions.Options;
using TestClasses.OptionsValidation;
using Xunit;
@@ -439,4 +440,41 @@ public void AttributePropertyModelTestOnErrorMessageResource()
var modelValidator = new AttributePropertyModelValidator();
Utils.VerifyValidateOptionsResult(modelValidator.Validate(nameof(validModel), validModel), 1);
}
+
+ [Fact]
+ public void OptionsUsingRangeWithTimeSpanValid()
+ {
+ var validModel = new OptionsUsingRangeWithTimeSpan
+ {
+ P1 = TimeSpan.FromSeconds(1),
+ P2 = TimeSpan.FromSeconds(2),
+ P3 = "00:00:03",
+ P4 = "00:00:04",
+ };
+
+ var modelValidator = new OptionsUsingRangeWithTimeSpanValidator();
+ ValidateOptionsResult result = modelValidator.Validate(nameof(validModel), validModel);
+ Assert.Equal(ValidateOptionsResult.Success, result);
+
+ var invalidModel = new OptionsUsingRangeWithTimeSpan
+ {
+ P1 = TimeSpan.FromSeconds(11),
+ P2 = TimeSpan.FromSeconds(-2),
+ P3 = "01:00:03",
+ P4 = "02:00:04",
+ };
+ result = modelValidator.Validate(nameof(invalidModel), invalidModel);
+ Assert.Equal(4, result.Failures.Count());
+
+ // null values pass the validation!
+ invalidModel = new OptionsUsingRangeWithTimeSpan
+ {
+ P1 = TimeSpan.FromSeconds(100),
+ P2 = null,
+ P3 = "00:01:00",
+ P4 = null,
+ };
+ result = modelValidator.Validate(nameof(invalidModel), invalidModel);
+ Assert.Equal(2, result.Failures.Count());
+ }
}
diff --git a/src/libraries/Microsoft.Extensions.Options/tests/SourceGenerationTests/TestClasses/Models.cs b/src/libraries/Microsoft.Extensions.Options/tests/SourceGenerationTests/TestClasses/Models.cs
index 240163023eb93..c245ebd783bdc 100644
--- a/src/libraries/Microsoft.Extensions.Options/tests/SourceGenerationTests/TestClasses/Models.cs
+++ b/src/libraries/Microsoft.Extensions.Options/tests/SourceGenerationTests/TestClasses/Models.cs
@@ -179,6 +179,21 @@ public class ComplexModel
public TypeWithoutOptionsValidator? ValWithoutOptionsValidator { get; set; }
}
+ public class OptionsUsingRangeWithTimeSpan
+ {
+ [Range(typeof(TimeSpan), "00:00:00", "00:00:10")]
+ public TimeSpan P1 { get; set; }
+
+ [Range(typeof(TimeSpan), "00:00:00", "00:00:10")]
+ public TimeSpan? P2 { get; set; }
+
+ [Range(typeof(TimeSpan), "00:00:00", "00:00:10")]
+ public string P3 { get; set; }
+
+ [Range(typeof(TimeSpan), "00:00:00", "00:00:10")]
+ public string? P4 { get; set; }
+ }
+
[OptionsValidator]
public partial class RequiredAttributeModelValidator : IValidateOptions
{
@@ -248,4 +263,9 @@ public partial class LeafModelValidator : IValidateOptions
internal sealed partial class ComplexModelValidator : IValidateOptions
{
}
+
+ [OptionsValidator]
+ internal sealed partial class OptionsUsingRangeWithTimeSpanValidator : IValidateOptions
+ {
+ }
}
diff --git a/src/libraries/Microsoft.Extensions.Options/tests/TrimmingTests/ConfigureTests.cs b/src/libraries/Microsoft.Extensions.Options/tests/TrimmingTests/ConfigureTests.cs
index 7a39d8a8810fd..90179563300b9 100644
--- a/src/libraries/Microsoft.Extensions.Options/tests/TrimmingTests/ConfigureTests.cs
+++ b/src/libraries/Microsoft.Extensions.Options/tests/TrimmingTests/ConfigureTests.cs
@@ -46,7 +46,8 @@ optionsC is null ||
P2 = new List { "1234", "12345" },
P3 = "123456",
P4 = "12345",
- P5 = 7
+ P5 = 7,
+ P6 = TimeSpan.FromSeconds(5),
};
ValidateOptionsResult result = localOptionsValidator.Validate("", optionsUsingValidationAttributes);
@@ -113,6 +114,10 @@ public class OptionsUsingValidationAttributes
[Range(1, 10, MinimumIsExclusive = true, MaximumIsExclusive = true)]
public int P5 { get; set; }
+
+ [Range(typeof(TimeSpan), "00:00:00", "00:00:10")]
+ public TimeSpan P6 { get; set; }
+
}
[OptionsValidator]
diff --git a/src/libraries/Microsoft.Internal.Runtime.AspNetCore.Transport/src/Microsoft.Internal.Runtime.AspNetCore.Transport.proj b/src/libraries/Microsoft.Internal.Runtime.AspNetCore.Transport/src/Microsoft.Internal.Runtime.AspNetCore.Transport.proj
index 732c102bab245..f628ef93cc78c 100644
--- a/src/libraries/Microsoft.Internal.Runtime.AspNetCore.Transport/src/Microsoft.Internal.Runtime.AspNetCore.Transport.proj
+++ b/src/libraries/Microsoft.Internal.Runtime.AspNetCore.Transport/src/Microsoft.Internal.Runtime.AspNetCore.Transport.proj
@@ -13,12 +13,6 @@
$(NoWarn);NU5131
-
-
- true
- $(PatchVersion)
-
-
$(NoWarn);NU5131;NU5128
-
-
- true
- $(PatchVersion)
-
-
diff --git a/src/libraries/System.Private.CoreLib/src/System/Runtime/Intrinsics/Vector512.cs b/src/libraries/System.Private.CoreLib/src/System/Runtime/Intrinsics/Vector512.cs
index f95c2d34c3c5f..824440a5bf2a7 100644
--- a/src/libraries/System.Private.CoreLib/src/System/Runtime/Intrinsics/Vector512.cs
+++ b/src/libraries/System.Private.CoreLib/src/System/Runtime/Intrinsics/Vector512.cs
@@ -441,8 +441,7 @@ public static void CopyTo(this Vector512 vector, T[] destination)
ThrowHelper.ThrowArgumentException_DestinationTooShort();
}
- ref byte address = ref Unsafe.As(ref MemoryMarshal.GetArrayDataReference(destination));
- Unsafe.WriteUnaligned(ref address, vector);
+ Unsafe.WriteUnaligned(ref Unsafe.As(ref destination[0]), vector);
}
/// Copies a to a given array starting at the specified index.
@@ -468,8 +467,7 @@ public static void CopyTo(this Vector512 vector, T[] destination, int star
ThrowHelper.ThrowArgumentException_DestinationTooShort();
}
- ref byte address = ref Unsafe.As(ref MemoryMarshal.GetArrayDataReference(destination));
- Unsafe.WriteUnaligned(ref Unsafe.Add(ref address, startIndex), vector);
+ Unsafe.WriteUnaligned(ref Unsafe.As(ref destination[startIndex]), vector);
}
/// Copies a to a given span.
diff --git a/src/libraries/System.Runtime.Intrinsics/tests/Vectors/Vector128Tests.cs b/src/libraries/System.Runtime.Intrinsics/tests/Vectors/Vector128Tests.cs
index 3894d835a60b0..cf1f6cec9be38 100644
--- a/src/libraries/System.Runtime.Intrinsics/tests/Vectors/Vector128Tests.cs
+++ b/src/libraries/System.Runtime.Intrinsics/tests/Vectors/Vector128Tests.cs
@@ -4517,6 +4517,22 @@ public void Vector128SingleEqualsNonCanonicalNaNTest()
}
}
+ [Fact]
+ public void Vector128SingleCopyToTest()
+ {
+ float[] array = new float[4];
+ Vector128.Create(2.0f).CopyTo(array);
+ Assert.True(array.AsSpan().SequenceEqual([2.0f, 2.0f, 2.0f, 2.0f]));
+ }
+
+ [Fact]
+ public void Vector128SingleCopyToOffsetTest()
+ {
+ float[] array = new float[5];
+ Vector128.Create(2.0f).CopyTo(array, 1);
+ Assert.True(array.AsSpan().SequenceEqual([0.0f, 2.0f, 2.0f, 2.0f, 2.0f]));
+ }
+
[Fact]
public void IsSupportedByte() => TestIsSupported();
diff --git a/src/libraries/System.Runtime.Intrinsics/tests/Vectors/Vector256Tests.cs b/src/libraries/System.Runtime.Intrinsics/tests/Vectors/Vector256Tests.cs
index 6bdc86bc46f43..95c05c50310fe 100644
--- a/src/libraries/System.Runtime.Intrinsics/tests/Vectors/Vector256Tests.cs
+++ b/src/libraries/System.Runtime.Intrinsics/tests/Vectors/Vector256Tests.cs
@@ -5539,6 +5539,22 @@ public void Vector256SingleEqualsNonCanonicalNaNTest()
}
}
+ [Fact]
+ public void Vector256SingleCopyToTest()
+ {
+ float[] array = new float[8];
+ Vector256.Create(2.0f).CopyTo(array);
+ Assert.True(array.AsSpan().SequenceEqual([2.0f, 2.0f, 2.0f, 2.0f, 2.0f, 2.0f, 2.0f, 2.0f]));
+ }
+
+ [Fact]
+ public void Vector256SingleCopyToOffsetTest()
+ {
+ float[] array = new float[9];
+ Vector256.Create(2.0f).CopyTo(array, 1);
+ Assert.True(array.AsSpan().SequenceEqual([0.0f, 2.0f, 2.0f, 2.0f, 2.0f, 2.0f, 2.0f, 2.0f, 2.0f]));
+ }
+
[Fact]
public void IsSupportedByte() => TestIsSupported();
diff --git a/src/libraries/System.Runtime.Intrinsics/tests/Vectors/Vector512Tests.cs b/src/libraries/System.Runtime.Intrinsics/tests/Vectors/Vector512Tests.cs
index 4922cf8cab858..2b97f49d91498 100644
--- a/src/libraries/System.Runtime.Intrinsics/tests/Vectors/Vector512Tests.cs
+++ b/src/libraries/System.Runtime.Intrinsics/tests/Vectors/Vector512Tests.cs
@@ -5016,6 +5016,22 @@ public void Vector512SingleEqualsNonCanonicalNaNTest()
}
}
+ [Fact]
+ public void Vector512SingleCopyToTest()
+ {
+ float[] array = new float[16];
+ Vector512.Create(2.0f).CopyTo(array);
+ Assert.True(array.AsSpan().SequenceEqual([2.0f, 2.0f, 2.0f, 2.0f, 2.0f, 2.0f, 2.0f, 2.0f, 2.0f, 2.0f, 2.0f, 2.0f, 2.0f, 2.0f, 2.0f, 2.0f]));
+ }
+
+ [Fact]
+ public void Vector512SingleCopyToOffsetTest()
+ {
+ float[] array = new float[17];
+ Vector512.Create(2.0f).CopyTo(array, 1);
+ Assert.True(array.AsSpan().SequenceEqual([0.0f, 2.0f, 2.0f, 2.0f, 2.0f, 2.0f, 2.0f, 2.0f, 2.0f, 2.0f, 2.0f, 2.0f, 2.0f, 2.0f, 2.0f, 2.0f, 2.0f]));
+ }
+
[Fact]
public void IsSupportedByte() => TestIsSupported();
diff --git a/src/libraries/System.Runtime.Intrinsics/tests/Vectors/Vector64Tests.cs b/src/libraries/System.Runtime.Intrinsics/tests/Vectors/Vector64Tests.cs
index 8596f3b0ff786..46aed6abbb06a 100644
--- a/src/libraries/System.Runtime.Intrinsics/tests/Vectors/Vector64Tests.cs
+++ b/src/libraries/System.Runtime.Intrinsics/tests/Vectors/Vector64Tests.cs
@@ -3937,6 +3937,22 @@ public void Vector64SingleEqualsNonCanonicalNaNTest()
}
}
+ [Fact]
+ public void Vector64SingleCopyToTest()
+ {
+ float[] array = new float[2];
+ Vector64.Create(2.0f).CopyTo(array);
+ Assert.True(array.AsSpan().SequenceEqual([2.0f, 2.0f]));
+ }
+
+ [Fact]
+ public void Vector64SingleCopyToOffsetTest()
+ {
+ float[] array = new float[3];
+ Vector64.Create(2.0f).CopyTo(array, 1);
+ Assert.True(array.AsSpan().SequenceEqual([0.0f, 2.0f, 2.0f]));
+ }
+
[Fact]
public void IsSupportedByte() => TestIsSupported();
diff --git a/src/libraries/System.Text.Json/src/System.Text.Json.csproj b/src/libraries/System.Text.Json/src/System.Text.Json.csproj
index 5765b6cdbee86..3e867457e45ba 100644
--- a/src/libraries/System.Text.Json/src/System.Text.Json.csproj
+++ b/src/libraries/System.Text.Json/src/System.Text.Json.csproj
@@ -8,6 +8,8 @@
CS8969
true
true
+ true
+ 1
Provides high-performance and low-allocating types that serialize objects to JavaScript Object Notation (JSON) text and deserialize JSON text to objects, with UTF-8 support built-in. Also provides types to read and write JSON text encoded as UTF-8, and to create an in-memory document object model (DOM), that is read-only, for random access of the JSON elements within a structured view of the data.
The System.Text.Json library is built-in as part of the shared framework in .NET Runtime. The package can be installed when you need to use it in other target frameworks.
diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Nodes/JsonArray.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Nodes/JsonArray.cs
index 8dc0924ad36e6..32096972d6df7 100644
--- a/src/libraries/System.Text.Json/src/System/Text/Json/Nodes/JsonArray.cs
+++ b/src/libraries/System.Text.Json/src/System/Text/Json/Nodes/JsonArray.cs
@@ -174,13 +174,7 @@ internal JsonArray(JsonElement element, JsonNodeOptions? options = null) : base(
[RequiresDynamicCode(JsonValue.CreateDynamicCodeMessage)]
public void Add(T? value)
{
- JsonNode? nodeToAdd = value switch
- {
- null => null,
- JsonNode node => node,
- _ => JsonValue.Create(value, Options)
- };
-
+ JsonNode? nodeToAdd = ConvertFromValue(value, Options);
Add(nodeToAdd);
}
diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Nodes/JsonNode.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Nodes/JsonNode.cs
index a06fc47cfc1c4..43b6048828a84 100644
--- a/src/libraries/System.Text.Json/src/System/Text/Json/Nodes/JsonNode.cs
+++ b/src/libraries/System.Text.Json/src/System/Text/Json/Nodes/JsonNode.cs
@@ -3,6 +3,8 @@
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
+using System.Text.Json.Serialization.Converters;
+using System.Text.Json.Serialization.Metadata;
namespace System.Text.Json.Nodes
{
@@ -316,17 +318,16 @@ public static bool DeepEquals(JsonNode? node1, JsonNode? node2)
[RequiresDynamicCode(JsonValue.CreateDynamicCodeMessage)]
public void ReplaceWith(T value)
{
+ JsonNode? node;
switch (_parent)
{
- case null:
- return;
case JsonObject jsonObject:
- JsonValue? jsonValue = JsonValue.Create(value);
- jsonObject.SetItem(GetPropertyName(), jsonValue);
+ node = ConvertFromValue(value);
+ jsonObject.SetItem(GetPropertyName(), node);
return;
case JsonArray jsonArray:
- JsonValue? jValue = JsonValue.Create(value);
- jsonArray.SetItem(GetElementIndex(), jValue);
+ node = ConvertFromValue(value);
+ jsonArray.SetItem(GetElementIndex(), node);
return;
}
}
@@ -351,5 +352,33 @@ internal void AssignParent(JsonNode parent)
Parent = parent;
}
+
+ ///
+ /// Adaptation of the equivalent JsonValue.Create factory method extended
+ /// to support arbitrary and values.
+ /// TODO consider making public cf. https://github.com/dotnet/runtime/issues/70427
+ ///
+ [RequiresUnreferencedCode(JsonSerializer.SerializationUnreferencedCodeMessage)]
+ [RequiresDynamicCode(JsonSerializer.SerializationRequiresDynamicCodeMessage)]
+ internal static JsonNode? ConvertFromValue(T? value, JsonNodeOptions? options = null)
+ {
+ if (value is null)
+ {
+ return null;
+ }
+
+ if (value is JsonNode node)
+ {
+ return node;
+ }
+
+ if (value is JsonElement element)
+ {
+ return JsonNodeConverter.Create(element, options);
+ }
+
+ var jsonTypeInfo = (JsonTypeInfo)JsonSerializerOptions.Default.GetTypeInfo(typeof(T));
+ return new JsonValueCustomized(value, jsonTypeInfo, options);
+ }
}
}
diff --git a/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/JsonNode/JsonArrayTests.cs b/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/JsonNode/JsonArrayTests.cs
index 8f115d44b211f..ef51703958151 100644
--- a/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/JsonNode/JsonArrayTests.cs
+++ b/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/JsonNode/JsonArrayTests.cs
@@ -680,5 +680,74 @@ public static void ReplaceWith()
Assert.Null(jValue.Parent);
Assert.Equal("[5]", jArray.ToJsonString());
}
+
+ [Theory]
+ [InlineData("null")]
+ [InlineData("1")]
+ [InlineData("false")]
+ [InlineData("\"str\"")]
+ [InlineData("""{"test":"hello world"}""")]
+ [InlineData("[1,2,3]")]
+ public static void AddJsonElement(string json)
+ {
+ // Regression test for https://github.com/dotnet/runtime/issues/94842
+ using var jdoc = JsonDocument.Parse(json);
+ var array = new JsonArray();
+
+ array.Add(jdoc.RootElement);
+
+ JsonNode arrayElement = Assert.Single(array);
+ switch (jdoc.RootElement.ValueKind)
+ {
+ case JsonValueKind.Object:
+ Assert.IsAssignableFrom(arrayElement);
+ break;
+ case JsonValueKind.Array:
+ Assert.IsAssignableFrom(arrayElement);
+ break;
+ case JsonValueKind.Null:
+ Assert.Null(arrayElement);
+ break;
+ default:
+ Assert.IsAssignableFrom(arrayElement);
+ break;
+ }
+ Assert.Equal($"[{json}]", array.ToJsonString());
+ }
+
+ [Theory]
+ [InlineData("null")]
+ [InlineData("1")]
+ [InlineData("false")]
+ [InlineData("\"str\"")]
+ [InlineData("""{"test":"hello world"}""")]
+ [InlineData("[1,2,3]")]
+ public static void ReplaceWithJsonElement(string json)
+ {
+ // Regression test for https://github.com/dotnet/runtime/issues/94842
+ using var jdoc = JsonDocument.Parse(json);
+ var array = new JsonArray { 1 };
+
+ array[0].ReplaceWith(jdoc.RootElement);
+
+ JsonNode arrayElement = Assert.Single(array);
+ switch (jdoc.RootElement.ValueKind)
+ {
+ case JsonValueKind.Object:
+ Assert.IsAssignableFrom(arrayElement);
+ break;
+ case JsonValueKind.Array:
+ Assert.IsAssignableFrom(arrayElement);
+ break;
+ case JsonValueKind.Null:
+ Assert.Null(arrayElement);
+ break;
+ default:
+ Assert.IsAssignableFrom(arrayElement);
+ break;
+ }
+
+ Assert.Equal($"[{json}]", array.ToJsonString());
+ }
}
}
diff --git a/src/mono/mono/mini/interp/transform.c b/src/mono/mono/mini/interp/transform.c
index d4b7a94d8a4d9..67034e9c59c81 100644
--- a/src/mono/mono/mini/interp/transform.c
+++ b/src/mono/mono/mini/interp/transform.c
@@ -788,28 +788,32 @@ fixup_newbb_stack_locals (TransformData *td, InterpBasicBlock *newbb)
}
}
+static void
+merge_stack_type_information (StackInfo *state1, StackInfo *state2, int len)
+{
+ // Discard type information if we have type conflicts for stack contents
+ for (int i = 0; i < len; i++) {
+ if (state1 [i].klass != state2 [i].klass) {
+ state1 [i].klass = NULL;
+ state2 [i].klass = NULL;
+ }
+ }
+}
+
// Initializes stack state at entry to bb, based on the current stack state
static void
init_bb_stack_state (TransformData *td, InterpBasicBlock *bb)
{
- // FIXME If already initialized, then we need to generate mov to the registers in the state.
// Check if already initialized
if (bb->stack_height >= 0) {
- // Discard type information if we have type conflicts for stack contents
- for (int i = 0; i < bb->stack_height; i++) {
- if (bb->stack_state [i].klass != td->stack [i].klass) {
- bb->stack_state [i].klass = NULL;
- td->stack [i].klass = NULL;
- }
+ merge_stack_type_information (td->stack, bb->stack_state, bb->stack_height);
+ } else {
+ bb->stack_height = GPTRDIFF_TO_INT (td->sp - td->stack);
+ if (bb->stack_height > 0) {
+ int size = bb->stack_height * sizeof (td->stack [0]);
+ bb->stack_state = (StackInfo*)mono_mempool_alloc (td->mempool, size);
+ memcpy (bb->stack_state, td->stack, size);
}
- return;
- }
-
- bb->stack_height = GPTRDIFF_TO_INT (td->sp - td->stack);
- if (bb->stack_height > 0) {
- int size = bb->stack_height * sizeof (td->stack [0]);
- bb->stack_state = (StackInfo*)mono_mempool_alloc (td->mempool, size);
- memcpy (bb->stack_state, td->stack, size);
}
}
@@ -5010,8 +5014,12 @@ generate_code (TransformData *td, MonoMethod *method, MonoMethodHeader *header,
td->cbb = new_bb;
if (new_bb->stack_height >= 0) {
- if (new_bb->stack_height > 0)
+ if (new_bb->stack_height > 0) {
+ if (link_bblocks)
+ merge_stack_type_information (td->stack, new_bb->stack_state, new_bb->stack_height);
+ // This is relevant only for copying the vars associated with the values on the stack
memcpy (td->stack, new_bb->stack_state, new_bb->stack_height * sizeof(td->stack [0]));
+ }
td->sp = td->stack + new_bb->stack_height;
} else if (link_bblocks) {
/* This bblock is not branched to. Initialize its stack state */
@@ -7550,6 +7558,7 @@ generate_code (TransformData *td, MonoMethod *method, MonoMethodHeader *header,
interp_ins_set_sreg (td->last_ins, td->sp [0].local);
td->sp = td->stack;
++td->ip;
+ link_bblocks = FALSE;
break;
case CEE_MONO_LD_DELEGATE_METHOD_PTR:
diff --git a/src/native/libs/System.Security.Cryptography.Native/opensslshim.h b/src/native/libs/System.Security.Cryptography.Native/opensslshim.h
index 9abc53338796e..cf10d2f794987 100644
--- a/src/native/libs/System.Security.Cryptography.Native/opensslshim.h
+++ b/src/native/libs/System.Security.Cryptography.Native/opensslshim.h
@@ -347,6 +347,8 @@ int EVP_DigestFinalXOF(EVP_MD_CTX *ctx, unsigned char *md, size_t len);
REQUIRED_FUNCTION(EVP_MD_CTX_copy_ex) \
RENAMED_FUNCTION(EVP_MD_CTX_free, EVP_MD_CTX_destroy) \
RENAMED_FUNCTION(EVP_MD_CTX_new, EVP_MD_CTX_create) \
+ REQUIRED_FUNCTION(EVP_MD_CTX_set_flags) \
+ LIGHTUP_FUNCTION(EVP_MD_fetch) \
RENAMED_FUNCTION(EVP_MD_get_size, EVP_MD_size) \
REQUIRED_FUNCTION(EVP_PKCS82PKEY) \
REQUIRED_FUNCTION(EVP_PKEY2PKCS8) \
@@ -842,6 +844,8 @@ FOR_ALL_OPENSSL_FUNCTIONS
#define EVP_MD_CTX_copy_ex EVP_MD_CTX_copy_ex_ptr
#define EVP_MD_CTX_free EVP_MD_CTX_free_ptr
#define EVP_MD_CTX_new EVP_MD_CTX_new_ptr
+#define EVP_MD_CTX_set_flags EVP_MD_CTX_set_flags_ptr
+#define EVP_MD_fetch EVP_MD_fetch_ptr
#define EVP_MD_get_size EVP_MD_get_size_ptr
#define EVP_PKCS82PKEY EVP_PKCS82PKEY_ptr
#define EVP_PKEY2PKCS8 EVP_PKEY2PKCS8_ptr
diff --git a/src/native/libs/System.Security.Cryptography.Native/osslcompat_30.h b/src/native/libs/System.Security.Cryptography.Native/osslcompat_30.h
index 095dd3176e7aa..5167f2a0fbcd1 100644
--- a/src/native/libs/System.Security.Cryptography.Native/osslcompat_30.h
+++ b/src/native/libs/System.Security.Cryptography.Native/osslcompat_30.h
@@ -19,6 +19,7 @@ void ERR_new(void);
void ERR_set_debug(const char *file, int line, const char *func);
void ERR_set_error(int lib, int reason, const char *fmt, ...);
int EVP_CIPHER_get_nid(const EVP_CIPHER *e);
+EVP_MD* EVP_MD_fetch(OSSL_LIB_CTX *ctx, const char *algorithm, const char *properties);
int EVP_MD_get_size(const EVP_MD* md);
int EVP_PKEY_CTX_set_rsa_keygen_bits(EVP_PKEY_CTX* ctx, int bits);
int EVP_PKEY_CTX_set_rsa_oaep_md(EVP_PKEY_CTX* ctx, const EVP_MD* md);
diff --git a/src/native/libs/System.Security.Cryptography.Native/pal_evp.c b/src/native/libs/System.Security.Cryptography.Native/pal_evp.c
index cadc29a67000d..b623df6b0b178 100644
--- a/src/native/libs/System.Security.Cryptography.Native/pal_evp.c
+++ b/src/native/libs/System.Security.Cryptography.Native/pal_evp.c
@@ -1,13 +1,41 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
+#include "openssl.h"
#include "pal_evp.h"
#include "pal_utilities.h"
#include
+#include
#define SUCCESS 1
+static const EVP_MD* g_evpFetchMd5 = NULL;
+static pthread_once_t g_evpFetch = PTHREAD_ONCE_INIT;
+
+static void EnsureFetchEvpMdAlgorithms(void)
+{
+ // This is called from a pthread_once - this method should not be called directly.
+
+#ifdef NEED_OPENSSL_3_0
+ if (API_EXISTS(EVP_MD_fetch))
+ {
+ ERR_clear_error();
+
+ // Try to fetch an MD5 implementation that will work regardless if
+ // FIPS is enforced or not.
+ g_evpFetchMd5 = EVP_MD_fetch(NULL, "MD5", "-fips");
+ }
+#endif
+
+ // No error queue impact.
+ // If EVP_MD_fetch is unavailable, use the implicit loader. If it failed, use the implicit loader as a last resort.
+ if (g_evpFetchMd5 == NULL)
+ {
+ g_evpFetchMd5 = EVP_md5();
+ }
+}
+
EVP_MD_CTX* CryptoNative_EvpMdCtxCreate(const EVP_MD* type)
{
ERR_clear_error();
@@ -23,6 +51,13 @@ EVP_MD_CTX* CryptoNative_EvpMdCtxCreate(const EVP_MD* type)
return NULL;
}
+ // For OpenSSL 1.x, set the non-FIPS allow flag for MD5. OpenSSL 3 does this differently with EVP_MD_fetch
+ // and no longer has this flag.
+ if (CryptoNative_OpenSslVersionNumber() < OPENSSL_VERSION_3_0_RTM && type == EVP_md5())
+ {
+ EVP_MD_CTX_set_flags(ctx, EVP_MD_CTX_FLAG_NON_FIPS_ALLOW);
+ }
+
int ret = EVP_DigestInit_ex(ctx, type, NULL);
if (!ret)
{
@@ -230,8 +265,8 @@ int32_t CryptoNative_EvpMdSize(const EVP_MD* md)
const EVP_MD* CryptoNative_EvpMd5(void)
{
- // No error queue impact.
- return EVP_md5();
+ pthread_once(&g_evpFetch, EnsureFetchEvpMdAlgorithms);
+ return g_evpFetchMd5;
}
const EVP_MD* CryptoNative_EvpSha1(void)