From 1f53b81051e6560f63147d0a9f9a3b50d97d1976 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Thu, 16 Nov 2023 17:31:07 -0800 Subject: [PATCH 01/14] [release/8.0-staging] Support TimeSpan with RangeAttribute in Options validation source generator (#94857) Co-authored-by: Tarek Mahmoud Sayed --- .../gen/Emitter.cs | 91 +++++++++++-- .../gen/Parser.cs | 41 ++++-- .../gen/SymbolHolder.cs | 1 + .../gen/SymbolLoader.cs | 4 + .../src/Microsoft.Extensions.Options.csproj | 2 +- .../EmitterWithCustomValidator.netcore.g.cs | 8 +- .../EmitterWithCustomValidator.netfx.g.cs | 8 +- ...eneratedAttributesTest.netcore.lang10.g.cs | 81 +++++++++++- ...eneratedAttributesTest.netcore.lang11.g.cs | 81 +++++++++++- .../GeneratedAttributesTest.netfx.lang10.g.cs | 81 +++++++++++- .../GeneratedAttributesTest.netfx.lang11.g.cs | 81 +++++++++++- .../tests/SourceGeneration.Unit.Tests/Main.cs | 7 + .../Baselines/NetCoreApp/Validators.g.cs | 124 ++++++++++++++++-- .../Baselines/NetFX/Validators.g.cs | 120 +++++++++++++++-- .../Generated/OptionsValidationTests.cs | 38 ++++++ .../TestClasses/Models.cs | 20 +++ .../tests/TrimmingTests/ConfigureTests.cs | 7 +- 17 files changed, 713 insertions(+), 82 deletions(-) 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] From 30d6848e1f13017706c6e43ec0268478ceda6b06 Mon Sep 17 00:00:00 2001 From: "dotnet-maestro[bot]" <42748379+dotnet-maestro[bot]@users.noreply.github.com> Date: Fri, 17 Nov 2023 12:38:17 -0600 Subject: [PATCH 02/14] Update dependencies from https://github.com/dotnet/emsdk build 20231116.4 (#94883) Microsoft.SourceBuild.Intermediate.emsdk , Microsoft.NET.Workload.Emscripten.Current.Manifest-8.0.100 From Version 8.0.1-servicing.23563.2 -> To Version 8.0.1-servicing.23566.4 Co-authored-by: dotnet-maestro[bot] --- NuGet.config | 9 +-------- eng/Version.Details.xml | 6 +++--- 2 files changed, 4 insertions(+), 11 deletions(-) diff --git a/NuGet.config b/NuGet.config index b8ad566c065a4..579d3b53ca8f2 100644 --- a/NuGet.config +++ b/NuGet.config @@ -9,14 +9,7 @@ - - - - - - - - + + + + + + diff --git a/eng/Version.Details.xml b/eng/Version.Details.xml index 15cfbc281eb89..e9a081259ef47 100644 --- a/eng/Version.Details.xml +++ b/eng/Version.Details.xml @@ -354,9 +354,9 @@ https://dev.azure.com/dnceng/internal/_git/dotnet-optimization 5914dbda1860830a56179692058d94c9f615deda - + https://github.com/dotnet/hotreload-utils - 8e108a21d0c7d8fa2050a1bdd4d4ba50d2b8df13 + 5524f726f92ef862b415793758cebbd2a1950b70 https://github.com/dotnet/runtime-assets diff --git a/eng/Versions.props b/eng/Versions.props index 9f818a6668303..e89c94c55e627 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 From 7dc2e67ca495b479f1081a5e193d10b7a396bae8 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Tue, 21 Nov 2023 11:05:17 -0800 Subject: [PATCH 04/14] Re-check for existance of syncblock in HasInteropInfo - in case the syncblock is gone. (#94649) Co-authored-by: vsadov <8218165+VSadov@users.noreply.github.com> --- src/coreclr/vm/weakreferencenative.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) 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 From b126b9a50434ffb164b1e2db21ba85915db38c8e Mon Sep 17 00:00:00 2001 From: Elinor Fung Date: Tue, 21 Nov 2023 11:05:52 -0800 Subject: [PATCH 05/14] Store entry assembly path for easy access for diagnostics (#95045) - Add `g_EntryAssemblyPath` global variable holding the full path to the entry assembly - Set right before loading the entry assembly (so also before startup hooks are run) - NULL if there is no entry assembly - Ensure value is included dumps - For triage dumps, the dumped value is updated to only be the assembly file name instead of the full path --- src/coreclr/debug/daccess/enummem.cpp | 28 +++++++++++++++++++++++++++ src/coreclr/inc/daccess.h | 6 ++++-- src/coreclr/inc/dacvars.h | 2 ++ src/coreclr/vm/corhost.cpp | 9 +++++++++ src/coreclr/vm/vars.cpp | 2 ++ src/coreclr/vm/vars.hpp | 3 +++ 6 files changed, 48 insertions(+), 2 deletions(-) diff --git a/src/coreclr/debug/daccess/enummem.cpp b/src/coreclr/debug/daccess/enummem.cpp index 44d98beec2de3..7856a1ff0c096 100644 --- a/src/coreclr/debug/daccess/enummem.cpp +++ b/src/coreclr/debug/daccess/enummem.cpp @@ -224,6 +224,34 @@ HRESULT ClrDataAccess::EnumMemCLRStatic(IN CLRDataEnumMemoryFlags flags) ReportMem(g_gcDacGlobals.GetAddr(), sizeof(GcDacVars)); + PTR_WSTR entryAssemblyPath = (PTR_WSTR)g_EntryAssemblyPath; + entryAssemblyPath.EnumMem(); + + // Triage dumps must not include full paths (PII data). Replace entry assembly path with file name only. + if (flags == CLRDATA_ENUM_MEM_TRIAGE) + { + WCHAR* path = entryAssemblyPath; + if (path != NULL) + { + size_t pathLen = u16_strlen(path) + 1; + + // Get the file name based on the last directory separator + const WCHAR* name = u16_strrchr(path, DIRECTORY_SEPARATOR_CHAR_W); + if (name != NULL) + { + name += 1; + size_t len = u16_strlen(name) + 1; + wcscpy_s(path, len, name); + + // Null out the rest of the buffer + for (size_t i = len; i < pathLen; ++i) + path[i] = W('\0'); + + DacUpdateMemoryRegion(entryAssemblyPath.GetAddr(), pathLen, (BYTE*)path); + } + } + } + // We need all of the dac variables referenced by the GC DAC global struct. // This struct contains pointers to pointers, so we first dereference the pointers // to obtain the location of the variable that's reported. diff --git a/src/coreclr/inc/daccess.h b/src/coreclr/inc/daccess.h index 3ca0ca40ee42e..54ce86551caee 100644 --- a/src/coreclr/inc/daccess.h +++ b/src/coreclr/inc/daccess.h @@ -574,6 +574,8 @@ #include "crosscomp.h" #endif +#include + // 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/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; From f7652ef0e03e2795d112580ddc8f6d8a39db4da2 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Tue, 21 Nov 2023 12:04:03 -0800 Subject: [PATCH 06/14] [release/8.0-staging] Fix Vector512.CopyTo (#95026) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Fix Vector512.CopyTo * Also unify Vector * Handle float vectors too * Fix legacy vectors * Simplify the change * Update Vector_1.cs * Add tests * Fix build --------- Co-authored-by: Michał Petryka <35800402+MichalPetryka@users.noreply.github.com> --- .../src/System/Runtime/Intrinsics/Vector512.cs | 6 ++---- .../tests/Vectors/Vector128Tests.cs | 16 ++++++++++++++++ .../tests/Vectors/Vector256Tests.cs | 16 ++++++++++++++++ .../tests/Vectors/Vector512Tests.cs | 16 ++++++++++++++++ .../tests/Vectors/Vector64Tests.cs | 16 ++++++++++++++++ 5 files changed, 66 insertions(+), 4 deletions(-) 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(); From 07cff1c67827e5a0a37984e6d66ad0c89c9cf59c Mon Sep 17 00:00:00 2001 From: "dotnet-maestro[bot]" <42748379+dotnet-maestro[bot]@users.noreply.github.com> Date: Tue, 21 Nov 2023 12:04:52 -0800 Subject: [PATCH 07/14] Update dependencies from https://github.com/dotnet/source-build-reference-packages build 20231115.1 (#95008) Microsoft.SourceBuild.Intermediate.source-build-reference-packages From Version 8.0.0-alpha.1.23556.3 -> To Version 8.0.0-alpha.1.23565.1 Co-authored-by: dotnet-maestro[bot] --- eng/Version.Details.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/eng/Version.Details.xml b/eng/Version.Details.xml index e9a081259ef47..ab9af74ea01fe 100644 --- a/eng/Version.Details.xml +++ b/eng/Version.Details.xml @@ -99,9 +99,9 @@ d75f77cb8eb87322453288a22ce9b2d880bd1cb1 - + https://github.com/dotnet/source-build-reference-packages - fa4c0e8f53ef2541a23e519af4dfb86cb88e1bae + 95f83e27806330fec09edd96e06bba3acabe3f35 From 9b0f22f6aa2c2ad07312441370ec0ae08fdc63e2 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Tue, 21 Nov 2023 12:07:01 -0800 Subject: [PATCH 08/14] [release/8.0-staging] Permit MD5 regardless of FIPS configuration for Linux (#94979) * Re-enable MD5 for FIPS with OpenSSL 3 * Permit MD5 on OpenSSL 1.1.1 regardless of FIPS * Fix build --------- Co-authored-by: Kevin Jones --- .../opensslshim.h | 4 ++ .../osslcompat_30.h | 1 + .../pal_evp.c | 39 ++++++++++++++++++- 3 files changed, 42 insertions(+), 2 deletions(-) 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) From 3e1f6722fead808a3e9ba41399f334a20eefddfa Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Tue, 21 Nov 2023 12:07:30 -0800 Subject: [PATCH 09/14] [release/8.0-staging] [mono][interp] Fix incorrect stack type information (#94966) * [mono][interp] Don't link bblock after rethrow * [mono][interp] Small code refactoring * [mono][interp] Fix incorrect stack type information Assume we have a basic block that it is a forward branch destination, then its stack type information will be initialized at the moment of branching (let's say there is a value of type Obj1). If later in the code we reach this bblock by falling through (let's say the current stack contains a value of Obj2), the current stack state will be copied from the original state, with the type Obj1. If later on we do a virtual call, we will try to devirtualize it as if this is Obj1 which is incorrect, since the fallthrough path would produce an Obj2. This commit adds missing checks for removing type information if we have different types on the execution types on incoming paths. --------- Co-authored-by: Vlad Brezae --- src/mono/mono/mini/interp/transform.c | 41 ++++++++++++++++----------- 1 file changed, 25 insertions(+), 16 deletions(-) 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: From adbb6ae8005674b3f85ef1868d1f0593ae547971 Mon Sep 17 00:00:00 2001 From: "dotnet-maestro[bot]" <42748379+dotnet-maestro[bot]@users.noreply.github.com> Date: Tue, 21 Nov 2023 12:10:37 -0800 Subject: [PATCH 10/14] [release/8.0-staging] Update dependencies from dotnet/runtime-assets (#94919) * Update dependencies from https://github.com/dotnet/runtime-assets build 20231116.1 Microsoft.DotNet.CilStrip.Sources , System.ComponentModel.TypeConverter.TestData , System.Data.Common.TestData , System.Drawing.Common.TestData , System.Formats.Tar.TestData , System.IO.Compression.TestData , System.IO.Packaging.TestData , System.Net.TestData , System.Private.Runtime.UnicodeData , System.Runtime.Numerics.TestData , System.Runtime.TimeZoneData , System.Security.Cryptography.X509Certificates.TestData , System.Text.RegularExpressions.TestData , System.Windows.Extensions.TestData From Version 8.0.0-beta.23558.5 -> To Version 8.0.0-beta.23566.1 * Update dependencies from https://github.com/dotnet/runtime-assets build 20231116.1 Microsoft.DotNet.CilStrip.Sources , System.ComponentModel.TypeConverter.TestData , System.Data.Common.TestData , System.Drawing.Common.TestData , System.Formats.Tar.TestData , System.IO.Compression.TestData , System.IO.Packaging.TestData , System.Net.TestData , System.Private.Runtime.UnicodeData , System.Runtime.Numerics.TestData , System.Runtime.TimeZoneData , System.Security.Cryptography.X509Certificates.TestData , System.Text.RegularExpressions.TestData , System.Windows.Extensions.TestData From Version 8.0.0-beta.23558.5 -> To Version 8.0.0-beta.23566.1 * Update dependencies from https://github.com/dotnet/runtime-assets build 20231116.1 Microsoft.DotNet.CilStrip.Sources , System.ComponentModel.TypeConverter.TestData , System.Data.Common.TestData , System.Drawing.Common.TestData , System.Formats.Tar.TestData , System.IO.Compression.TestData , System.IO.Packaging.TestData , System.Net.TestData , System.Private.Runtime.UnicodeData , System.Runtime.Numerics.TestData , System.Runtime.TimeZoneData , System.Security.Cryptography.X509Certificates.TestData , System.Text.RegularExpressions.TestData , System.Windows.Extensions.TestData From Version 8.0.0-beta.23558.5 -> To Version 8.0.0-beta.23566.1 * Update dependencies from https://github.com/dotnet/runtime-assets build 20231116.1 Microsoft.DotNet.CilStrip.Sources , System.ComponentModel.TypeConverter.TestData , System.Data.Common.TestData , System.Drawing.Common.TestData , System.Formats.Tar.TestData , System.IO.Compression.TestData , System.IO.Packaging.TestData , System.Net.TestData , System.Private.Runtime.UnicodeData , System.Runtime.Numerics.TestData , System.Runtime.TimeZoneData , System.Security.Cryptography.X509Certificates.TestData , System.Text.RegularExpressions.TestData , System.Windows.Extensions.TestData From Version 8.0.0-beta.23558.5 -> To Version 8.0.0-beta.23566.1 --------- Co-authored-by: dotnet-maestro[bot] Co-authored-by: Larry Ewing From 1aa09ef4ec76baf24c5fc3371e2c83cd1607413b Mon Sep 17 00:00:00 2001 From: Viktor Hofer Date: Tue, 21 Nov 2023 21:15:03 +0100 Subject: [PATCH 11/14] [release/8.0-staging] Use live M.Bcl.AsyncInterfaces dependency (#94459) * [release/8.0-staging] Use live M.Bcl.AsyncInterfaces dependency Manual backport of https://github.com/dotnet/runtime/pull/94451 * Update Microsoft.Bcl.TimeProvider.csproj * Update Microsoft.Bcl.TimeProvider.csproj * Update Microsoft.Bcl.TimeProvider.csproj --- .../ref/Microsoft.Bcl.TimeProvider.csproj | 2 +- .../src/Microsoft.Bcl.TimeProvider.csproj | 4 +++- 2 files changed, 4 insertions(+), 2 deletions(-) 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 - + From ed566543bb1156d96f582fd8e2c3e224151492ae Mon Sep 17 00:00:00 2001 From: "dotnet-maestro[bot]" <42748379+dotnet-maestro[bot]@users.noreply.github.com> Date: Tue, 21 Nov 2023 15:51:13 -0800 Subject: [PATCH 12/14] [release/8.0-staging] Update dependencies from dotnet/source-build-externals (#94918) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Update dependencies from https://github.com/dotnet/source-build-externals build 20231116.2 Microsoft.SourceBuild.Intermediate.source-build-externals From Version 8.0.0-alpha.1.23518.1 -> To Version 8.0.0-alpha.1.23566.2 * Update dependencies from https://github.com/dotnet/source-build-externals build 20231116.2 Microsoft.SourceBuild.Intermediate.source-build-externals From Version 8.0.0-alpha.1.23518.1 -> To Version 8.0.0-alpha.1.23566.2 * Update dependencies from https://github.com/dotnet/source-build-externals build 20231116.2 Microsoft.SourceBuild.Intermediate.source-build-externals From Version 8.0.0-alpha.1.23518.1 -> To Version 8.0.0-alpha.1.23566.2 * Update dependencies from https://github.com/dotnet/source-build-externals build 20231116.2 Microsoft.SourceBuild.Intermediate.source-build-externals From Version 8.0.0-alpha.1.23518.1 -> To Version 8.0.0-alpha.1.23566.2 * Update dependencies from https://github.com/dotnet/source-build-externals build 20231120.1 Microsoft.SourceBuild.Intermediate.source-build-externals From Version 8.0.0-alpha.1.23518.1 -> To Version 8.0.0-alpha.1.23570.1 --------- Co-authored-by: dotnet-maestro[bot] Co-authored-by: Carlos Sánchez López <1175054+carlossanlop@users.noreply.github.com> --- NuGet.config | 1 + eng/Version.Details.xml | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/NuGet.config b/NuGet.config index ceec57bb70dfc..a9123a91e4272 100644 --- a/NuGet.config +++ b/NuGet.config @@ -9,6 +9,7 @@ + diff --git a/eng/Version.Details.xml b/eng/Version.Details.xml index ab9af74ea01fe..ccf2f5e57c8e1 100644 --- a/eng/Version.Details.xml +++ b/eng/Version.Details.xml @@ -104,9 +104,9 @@ 95f83e27806330fec09edd96e06bba3acabe3f35 - + https://github.com/dotnet/source-build-externals - 3dc05150cf234f76f6936dcb2853d31a0da1f60e + e844aa02a05b90d8cbe499676ec6ee0f19ec4980 From 72a375ff416aa089e9bd29955e438794cad6a790 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Tue, 21 Nov 2023 18:41:40 -0800 Subject: [PATCH 13/14] Do not generate broken debug info for non-methods (#94757) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The related methods (`EmitDebugInfo`, `EmitEHClauseInfo`) already do this - if the node is not a method node, don't emit anything. It is not clear what purpose the "if this is not a method, emit broken debug information" serves. I traced it all the way back to https://github.com/dotnet/corert/blob/d78cf62480331f63b26eb08b86b838ffa355ff0d/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/ObjectWriter.cs#L427-L447 - it was surrounded by TODOs so maybe it didn't fully stand out but it doesn't look right there already. Co-authored-by: Michal Strehovský --- .../Compiler/DependencyAnalysis/ObjectWriter.cs | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) 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)] From b56fb2a7201976b1b6e7723960479e28171b97a3 Mon Sep 17 00:00:00 2001 From: "dotnet-maestro[bot]" <42748379+dotnet-maestro[bot]@users.noreply.github.com> Date: Tue, 21 Nov 2023 20:34:03 -0800 Subject: [PATCH 14/14] [release/8.0-staging] Update dependencies from dotnet/emsdk (#94932) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Update dependencies from https://github.com/dotnet/emsdk build 20231117.1 Microsoft.SourceBuild.Intermediate.emsdk , Microsoft.NET.Workload.Emscripten.Current.Manifest-8.0.100 From Version 8.0.1-servicing.23566.4 -> To Version 8.0.1-servicing.23567.1 Dependency coherency updates runtime.linux-arm64.Microsoft.NETCore.Runtime.ObjWriter,runtime.linux-x64.Microsoft.NETCore.Runtime.ObjWriter,runtime.linux-musl-arm64.Microsoft.NETCore.Runtime.ObjWriter,runtime.linux-musl-x64.Microsoft.NETCore.Runtime.ObjWriter,runtime.win-arm64.Microsoft.NETCore.Runtime.ObjWriter,runtime.win-x64.Microsoft.NETCore.Runtime.ObjWriter,runtime.osx-arm64.Microsoft.NETCore.Runtime.ObjWriter,runtime.osx-x64.Microsoft.NETCore.Runtime.ObjWriter,runtime.linux-arm64.Microsoft.NETCore.Runtime.JIT.Tools,runtime.linux-x64.Microsoft.NETCore.Runtime.JIT.Tools,runtime.linux-musl-arm64.Microsoft.NETCore.Runtime.JIT.Tools,runtime.linux-musl-x64.Microsoft.NETCore.Runtime.JIT.Tools,runtime.win-arm64.Microsoft.NETCore.Runtime.JIT.Tools,runtime.win-x64.Microsoft.NETCore.Runtime.JIT.Tools,runtime.osx-arm64.Microsoft.NETCore.Runtime.JIT.Tools,runtime.osx-x64.Microsoft.NETCore.Runtime.JIT.Tools,runtime.linux-arm64.Microsoft.NETCore.Runtime.Mono.LLVM.Sdk,runtime.linux-arm64.Microsoft.NETCore.Runtime.Mono.LLVM.Tools,runtime.linux-musl-arm64.Microsoft.NETCore.Runtime.Mono.LLVM.Sdk,runtime.linux-musl-arm64.Microsoft.NETCore.Runtime.Mono.LLVM.Tools,runtime.linux-x64.Microsoft.NETCore.Runtime.Mono.LLVM.Sdk,runtime.linux-x64.Microsoft.NETCore.Runtime.Mono.LLVM.Tools,runtime.linux-musl-x64.Microsoft.NETCore.Runtime.Mono.LLVM.Sdk,runtime.linux-musl-x64.Microsoft.NETCore.Runtime.Mono.LLVM.Tools,runtime.win-x64.Microsoft.NETCore.Runtime.Mono.LLVM.Sdk,runtime.win-x64.Microsoft.NETCore.Runtime.Mono.LLVM.Tools,runtime.osx-arm64.Microsoft.NETCore.Runtime.Mono.LLVM.Sdk,runtime.osx-arm64.Microsoft.NETCore.Runtime.Mono.LLVM.Tools,runtime.osx-x64.Microsoft.NETCore.Runtime.Mono.LLVM.Sdk,runtime.osx-x64.Microsoft.NETCore.Runtime.Mono.LLVM.Tools From Version 16.0.5-alpha.1.23558.12 -> To Version 16.0.5-alpha.1.23566.1 (parent: Microsoft.NET.Workload.Emscripten.Current.Manifest-8.0.100 * Update dependencies from https://github.com/dotnet/emsdk build 20231121.1 Microsoft.SourceBuild.Intermediate.emsdk , Microsoft.NET.Workload.Emscripten.Current.Manifest-8.0.100 From Version 8.0.1-servicing.23567.1 -> To Version 8.0.1-servicing.23571.1 --------- Co-authored-by: dotnet-maestro[bot] Co-authored-by: Carlos Sánchez López <1175054+carlossanlop@users.noreply.github.com> --- NuGet.config | 1 + eng/Version.Details.xml | 6 +++--- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/NuGet.config b/NuGet.config index a9123a91e4272..f34253e5b0e2f 100644 --- a/NuGet.config +++ b/NuGet.config @@ -9,6 +9,7 @@ + diff --git a/eng/Version.Details.xml b/eng/Version.Details.xml index ccf2f5e57c8e1..8fbbd47bfd2d0 100644 --- a/eng/Version.Details.xml +++ b/eng/Version.Details.xml @@ -92,11 +92,11 @@ https://github.com/dotnet/emsdk - d75f77cb8eb87322453288a22ce9b2d880bd1cb1 + 201f4dae9d1a1e105d8ba86d7ece61eed1f665e0 - + https://github.com/dotnet/emsdk - d75f77cb8eb87322453288a22ce9b2d880bd1cb1 + 201f4dae9d1a1e105d8ba86d7ece61eed1f665e0